如何完全禁用 HTTP/1.x 支持
How to completely disable HTTP/1.x support
我只想为一个新项目支持HTTP/2,客户端不是浏览器所以如果我们根本不支持HTTP/1.x也不是问题。
根据我在 golang.org/x/net/http2 中看到的情况。我可以使用 tls.Listen
并将 net.Conn
传递给 http2.Server.ServeConn
。
但是我对如何在这里使用 http2.Transport
有点困惑,谁能给我一个例子?
谢谢
更新:
这是服务器部分,很简单,它是一个回显服务器
package main
import (
"fmt"
"io"
"net"
"net/http"
"golang.org/x/net/http2"
)
func main() {
l, err := net.Listen("tcp4", ":1234")
panicIfNotNil(err)
s := &http2.Server{}
sopt := &http2.ServeConnOpts{
BaseConfig: &http.Server{},
Handler: http.HandlerFunc(handler),
}
for {
c, err := l.Accept()
panicIfNotNil(err)
go serve(s, sopt, c)
}
}
func serve(s *http2.Server, sopt *http2.ServeConnOpts, c net.Conn) {
defer c.Close()
s.ServeConn(c, sopt)
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.ProtoMajor != 2 {
w.WriteHeader(500)
fmt.Fprintln(w, "Not HTTP/2")
return
}
f, ok := w.(http.Flusher)
if !ok {
w.WriteHeader(500)
fmt.Fprintln(w, "Not Flusher")
return
}
w.Header().Set("Content-Type", "application/octet-stream")
fmt.Fprintln(w, "Hello World, Echo Server")
buf := [1024]byte{}
for {
n, err := r.Body.Read(buf[:])
if err == io.EOF {
break
}
panicIfNotNil(err)
_, err = w.Write(buf[:n])
f.Flush()
panicIfNotNil(err)
}
}
func panicIfNotNil(err error) {
if err != nil {
panic(err)
}
}
已通过 curl --http2-prior-knowledge http://127.0.0.1:1234 -d a=b -d c=d -d e=f
测试
客户端方面,我还在努力中,等有收获再更新这个post
更新:
为了简单起见,我这里不使用TLS
更新:
这是客户端部分
package main
import (
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"net/url"
"time"
"golang.org/x/net/http2"
)
func main() {
t := &http2.Transport{
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return net.Dial(network, addr)
},
AllowHTTP: true,
}
c := &http.Client{
Transport: t,
}
pr, pw := io.Pipe()
req := &http.Request{
Method: "POST",
URL: mustUrl("http://127.0.0.1:1234/"),
Body: pr,
}
resp, err := c.Do(req)
panicIfNotNil(err)
defer resp.Body.Close()
if resp.StatusCode != 200 {
panic(fmt.Errorf("Server return non 200, %d", resp.StatusCode))
}
wchan := make(chan struct{})
go func() {
buf := [1024]byte{}
for {
n, err := resp.Body.Read(buf[:])
if err == io.EOF {
break
}
panicIfNotNil(err)
fmt.Printf("GOT DATA %s\n", string(buf[:n]))
}
close(wchan)
}()
time.Sleep(1 * time.Second)
pw.Write([]byte("hai AAA"))
time.Sleep(1 * time.Second)
pw.Write([]byte("hai BBB"))
time.Sleep(1 * time.Second)
pw.Write([]byte("hai CCC"))
time.Sleep(1 * time.Second)
pw.Write([]byte("hai CCC"))
time.Sleep(1 * time.Second)
pw.Close()
<-wchan
}
func mustUrl(s string) *url.URL {
r, err := url.Parse(s)
panicIfNotNil(err)
return r
}
func panicIfNotNil(err error) {
if err != nil {
panic(err)
}
}
但不知何故它不起作用
您可以在 https://imgur.com/EJV0uGI
中查看网络流量
仔细查看 Wireshark 后我发现了问题,这是因为服务器没有发送任何 header 帧,因此客户端无法继续处理更多数据。只是打印到 http.ResponseWriter
并不能确保它被写入网络,而是被缓冲,所以我们需要明确地刷新它。
这解决了问题:
--- main.go 2018-07-25 22:31:44.092823590 +0700
+++ main2.go 2018-07-25 22:32:50.586179879 +0700
@@ -43,6 +43,9 @@
return
}
w.Header().Set("Content-Type", "application/octet-stream")
+ w.WriteHeader(200)
+ f.Flush()
+
fmt.Fprintln(w, "Hello World, Echo Server")
buf := [1024]byte{}
我只想为一个新项目支持HTTP/2,客户端不是浏览器所以如果我们根本不支持HTTP/1.x也不是问题。
根据我在 golang.org/x/net/http2 中看到的情况。我可以使用 tls.Listen
并将 net.Conn
传递给 http2.Server.ServeConn
。
但是我对如何在这里使用 http2.Transport
有点困惑,谁能给我一个例子?
谢谢
更新:
这是服务器部分,很简单,它是一个回显服务器
package main
import (
"fmt"
"io"
"net"
"net/http"
"golang.org/x/net/http2"
)
func main() {
l, err := net.Listen("tcp4", ":1234")
panicIfNotNil(err)
s := &http2.Server{}
sopt := &http2.ServeConnOpts{
BaseConfig: &http.Server{},
Handler: http.HandlerFunc(handler),
}
for {
c, err := l.Accept()
panicIfNotNil(err)
go serve(s, sopt, c)
}
}
func serve(s *http2.Server, sopt *http2.ServeConnOpts, c net.Conn) {
defer c.Close()
s.ServeConn(c, sopt)
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.ProtoMajor != 2 {
w.WriteHeader(500)
fmt.Fprintln(w, "Not HTTP/2")
return
}
f, ok := w.(http.Flusher)
if !ok {
w.WriteHeader(500)
fmt.Fprintln(w, "Not Flusher")
return
}
w.Header().Set("Content-Type", "application/octet-stream")
fmt.Fprintln(w, "Hello World, Echo Server")
buf := [1024]byte{}
for {
n, err := r.Body.Read(buf[:])
if err == io.EOF {
break
}
panicIfNotNil(err)
_, err = w.Write(buf[:n])
f.Flush()
panicIfNotNil(err)
}
}
func panicIfNotNil(err error) {
if err != nil {
panic(err)
}
}
已通过 curl --http2-prior-knowledge http://127.0.0.1:1234 -d a=b -d c=d -d e=f
客户端方面,我还在努力中,等有收获再更新这个post
更新:
为了简单起见,我这里不使用TLS
更新:
这是客户端部分
package main
import (
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"net/url"
"time"
"golang.org/x/net/http2"
)
func main() {
t := &http2.Transport{
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return net.Dial(network, addr)
},
AllowHTTP: true,
}
c := &http.Client{
Transport: t,
}
pr, pw := io.Pipe()
req := &http.Request{
Method: "POST",
URL: mustUrl("http://127.0.0.1:1234/"),
Body: pr,
}
resp, err := c.Do(req)
panicIfNotNil(err)
defer resp.Body.Close()
if resp.StatusCode != 200 {
panic(fmt.Errorf("Server return non 200, %d", resp.StatusCode))
}
wchan := make(chan struct{})
go func() {
buf := [1024]byte{}
for {
n, err := resp.Body.Read(buf[:])
if err == io.EOF {
break
}
panicIfNotNil(err)
fmt.Printf("GOT DATA %s\n", string(buf[:n]))
}
close(wchan)
}()
time.Sleep(1 * time.Second)
pw.Write([]byte("hai AAA"))
time.Sleep(1 * time.Second)
pw.Write([]byte("hai BBB"))
time.Sleep(1 * time.Second)
pw.Write([]byte("hai CCC"))
time.Sleep(1 * time.Second)
pw.Write([]byte("hai CCC"))
time.Sleep(1 * time.Second)
pw.Close()
<-wchan
}
func mustUrl(s string) *url.URL {
r, err := url.Parse(s)
panicIfNotNil(err)
return r
}
func panicIfNotNil(err error) {
if err != nil {
panic(err)
}
}
但不知何故它不起作用 您可以在 https://imgur.com/EJV0uGI
中查看网络流量仔细查看 Wireshark 后我发现了问题,这是因为服务器没有发送任何 header 帧,因此客户端无法继续处理更多数据。只是打印到 http.ResponseWriter
并不能确保它被写入网络,而是被缓冲,所以我们需要明确地刷新它。
这解决了问题:
--- main.go 2018-07-25 22:31:44.092823590 +0700
+++ main2.go 2018-07-25 22:32:50.586179879 +0700
@@ -43,6 +43,9 @@
return
}
w.Header().Set("Content-Type", "application/octet-stream")
+ w.WriteHeader(200)
+ f.Flush()
+
fmt.Fprintln(w, "Hello World, Echo Server")
buf := [1024]byte{}