美文网首页
golang http2 client 和server 使用非

golang http2 client 和server 使用非

作者: 1行者 | 来源:发表于2017-06-21 22:22 被阅读0次

    研究了一下 golang 的http2 用法

    这里先普及几个概念

    • h2,基于TLS之上构建的HTTP/2,作为ALPN的标识符,两个字节表示,0x68, 0x32,即https
    • h2c,直接在TCP之上构建的HTTP/2,缺乏安全保证,即http
      在HTTP/2 RFC文档出现之前,以上版本字段需要添加上草案版本号,类似于h2-11,h2c-17

    首先写了一个服务器的代码

    
    import (
        "fmt"
        "html"
        "net/http"
    
        "golang.org/x/net/http2"
    )
    
    func main() {
        var server http.Server
    
        http2.VerboseLogs = true
        server.Addr = ":8080"
    
        http2.ConfigureServer(&server, &http2.Server{})
    
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprintf(w, "URL: %q\n", html.EscapeString(r.URL.Path))
            ShowRequestInfoHandler(w, r)
        })
    
        server.ListenAndServe() //不启用 https 则默认只支持http1.x
        //log.Fatal(server.ListenAndServeTLS("localhost.cert", "localhost.key"))
    }
    func ShowRequestInfoHandler(w http.ResponseWriter, r *http.Request) {
    
        //  fmt.Fprintf(w, "======")
        //  return
    
        w.Header().Set("Content-Type", "text/plain")
    
        fmt.Fprintf(w, "Method: %s\n", r.Method)
        fmt.Fprintf(w, "Protocol: %s\n", r.Proto)
        fmt.Fprintf(w, "Host: %s\n", r.Host)
        fmt.Fprintf(w, "RemoteAddr: %s\n", r.RemoteAddr)
        fmt.Fprintf(w, "RequestURI: %q\n", r.RequestURI)
        fmt.Fprintf(w, "URL: %#v\n", r.URL)
        fmt.Fprintf(w, "Body.ContentLength: %d (-1 means unknown)\n", r.ContentLength)
        fmt.Fprintf(w, "Close: %v (relevant for HTTP/1 only)\n", r.Close)
        fmt.Fprintf(w, "TLS: %#v\n", r.TLS)
        fmt.Fprintf(w, "\nHeaders:\n")
    
        r.Header.Write(w)
    }
    

    由于不想 使用https 就使用了 server.ListenAndServe(),没想到这里有个坑,等下介绍

    既然服务端使用了http 非 tls 那么 客户端就使用 非 tls 了,看代码

    package main
    
    import (
      "crypto/tls"
      "fmt"
      "io/ioutil"
      "log"
      "net"
      "net/http"
    
      "golang.org/x/net/http2"
    )
    
    func main() {
      url := "http://localhost:8080/"
      client(url)
    }
    
    func client(url string) {
      log.SetFlags(log.Llongfile)
      tr := &http2.Transport{ //可惜服务端 退化成了 http1.x
          AllowHTTP: true, //充许非加密的链接
          // TLSClientConfig: &tls.Config{
          //  InsecureSkipVerify: true,
          // },
          DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
              return net.Dial(netw, addr)
          },
      }
    
      httpClient := http.Client{Transport: tr}
    
      resp, err := httpClient.Get(url)
      if err != nil {
          log.Fatal(err)
      }
      defer resp.Body.Close()
    
      if resp.StatusCode != http.StatusOK {
          fmt.Println("resp StatusCode:", resp.StatusCode)
          return
      }
    
      body, err := ioutil.ReadAll(resp.Body)
      if err != nil {
          log.Fatal(err)
      }
    
      fmt.Println("resp.Body:\n", string(body))
    }
    
    

    由于http2 client 没有暴露 h2c 模式的,所以就 搞了个

    AllowHTTP: true, //充许非加密的链接
    
    DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
                return net.Dial(netw, addr)
            },
    

    本意是想这样实现客户端h2c

    搞完了就运行服务器,执行客户端,结果打印

    Get http://localhost:8080/: unexpected EOF

    看的云里雾里,再抓包一看

    粘贴图片.png

    服务器向客户端发了一个http1.1 的包,并且还close 了client 链接,为什么会这样呢

    server.ListenAndServe() //不启用 https 则默认只支持http1.x

    1.png

    既然服务器只支持http1 了那么客户端 发http2的请求,服务器当然要close 链接了。

    那么有没有办法解决呢,即服务器和客户端都使用 h2c ,客户端的比较好办 AllowHTTP: true 充许非加密的链接 并且

    DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
                return net.Dial(netw, addr)
            },
    

    服务端考虑使用更低一层的库http2库实现,主要是使用ServCon直接替换掉net/http/中的serv函数,例如

    import (
        "fmt"
        "golang.org/x/net/http2"
        "net/http"
    
        "net"
        "time"
    )
    
    //net/http包默认可以采用http2进行服务,在没有进行https的服务上开启H2,
    //需要修改ListenAndServer的默认h2服务
    
    type serverHandler struct {
    }
    
    func (sh *serverHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
        fmt.Println(req)
        w.Header().Set("server", "h2test")
        w.Write([]byte("this is a http2 test sever"))
    }
    
    func main() {
        server := &http.Server{
            Addr:         ":8080",
            Handler:      &serverHandler{},
            ReadTimeout:  5 * time.Second,
            WriteTimeout: 5 * time.Second,
        }
        //http2.Server.ServeConn()
        s2 := &http2.Server{
            IdleTimeout: 1 * time.Minute,
        }
        http2.ConfigureServer(server, s2)
        l, _ := net.Listen("tcp", ":8080")
        defer l.Close()
        fmt.Println("Start server...")
        for {
            rwc, err := l.Accept()
            if err != nil {
                fmt.Println("accept err:", err)
                continue
            }
            go s2.ServeConn(rwc, &http2.ServeConnOpts{BaseConfig: server})
    
        }
        //http.ListenAndServe(":8888",&serverHandler{})
    }
    

    相关文章

      网友评论

          本文标题:golang http2 client 和server 使用非

          本文链接:https://www.haomeiwen.com/subject/pmptcxtx.html