53

HTTP1.X、HTTP/2、websocket、http.Hijacker golang

 5 years ago
source link: https://studygolang.com/articles/15598?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
  • 从Go 1.6开始, net/http 下提供的 Server 在调用 ListenAndServeTLS 函数启动https服务的情况下会自动支持HTTP/2。其会根据与客户端TLS握手阶段的ALPN扩展判断客户端是否支持HTTP/2(h2),若支持,在TLS握手结束后会直接使用HTTP/2进行通讯。

  • 若需要使用HTTPS但不想开启HTTP/2可以有以下两种方法:

    • 初始化 Server 结构体时,将 TLSNextProto 字段置为一个非 nil 的空map

      // 栗子
      server := http.Server{
          Addr:         ":8080",
          TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
      }
      log.Fatal(server.ListenAndServeTLS("./ssl/ca.crt", "./ssl/ca.key"))
    • 使用 GODEBUG 环境变量

      GODEBUG=http2client=0  # disable HTTP/2 client support
      GODEBUG=http2server=0  # disable HTTP/2 server support
      GODEBUG=http2debug=1   # enable verbose HTTP/2 debug logs
      GODEBUG=http2debug=2   # ... even more verbose, with frame dumps

  • 也就是说默认提供的 http.Server 仅在启用https时才会支持HTTP/2,也就是只支持h2模式。若要使用h2c模式,需要使用 golang.org/x/net/http2 中提供的API。(P.S:目前大多数浏览器仅支持h2模式)

  • WebSocket和HTTP/2不兼容,如果想让WebSocket跑在TLS上,需要用上面的方法禁用HTTP/2

    • 原因分析:

      • 在HTTP1.X中,一个请求和回复对应在一个tcp连接上,在websocket握手结束后,该tcp链接升级为websocket协议。而在HTTP/2中,多个请求和回复会复用一个tcp链接,无法实现上述的过程。

      • 对应在Go的代码上,以 github.com/gorilla/websocket 的WebSocket实现为例。其会在握手阶段将 http.ResponseWriter 断言为 http.Hijacker 接口并调用其中的 Hijack() 方法,拿到原始tcp链接对象并进行接管。而在使用HTTP/2时, http.ResponseWriter 无法断言为 http.Hijacker

        github.com/gorilla/websocket

        h, ok := w.(http.Hijacker)
        if !ok {
          return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
        }
        var brw *bufio.ReadWriter
        netConn, brw, err = h.Hijack()
        if err != nil {
          return u.returnError(w, r, http.StatusInternalServerError, err.Error())
        }

        http.Hijacker

        // The Hijacker interface is implemented by ResponseWriters that allow
        // an HTTP handler to take over the connection.
        //
        // The default ResponseWriter for HTTP/1.x connections supports
        // Hijacker, but HTTP/2 connections intentionally do not.
        // ResponseWriter wrappers may also not support Hijacker. Handlers
        // should always test for this ability at runtime.
        type Hijacker interface {
          // Hijack lets the caller take over the connection.
          // After a call to Hijack the HTTP server library
          // will not do anything else with the connection.
          //
          // It becomes the caller's responsibility to manage
          // and close the connection.
          //
          // The returned net.Conn may have read or write deadlines
          // already set, depending on the configuration of the
          // Server. It is the caller's responsibility to set
          // or clear those deadlines as needed.
          //
          // The returned bufio.Reader may contain unprocessed buffered
          // data from the client.
          //
          // After a call to Hijack, the original Request.Body should
          // not be used.
          Hijack() (net.Conn, *bufio.ReadWriter, error)
        }
    • 有一个 WebSocket over HTTP/2 的草案,不过并没有什么用,看样子这问题暂时无解。

  • 关于HTTP/2的Server Push,其主要用途是提前推送web资源以减少延时。无法像WebSocket一样作为实时的消息推送手段

    UZRzArA.png!web

    image.png


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK