golang http2 client 和server 使用非TLS 模式(h2c)

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

研究了一下 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 mainimport (    "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{})}
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.