在 go 中嘗試使用 http2 包,在創建 HTTP2 連接時遇到意外的 EOF 錯誤。無法弄清楚確切的問題。
tcpConn, err := net.Dial("tcp", "clients1.google.com:443")
if err != nil {
panic(err)
}
defer tcpConn.Close()
t := http2.Transport{}
http2ClientConn, err := t.NewClientConn(tcpConn)
if err != nil {
panic(err)
}
req, err := http.NewRequest("GET", "https://clients1.google.com/generate_204", nil)
if err != nil {
panic(err)
}
resp, err := http2ClientConn.RoundTrip(req)
if err != nil {
panic(err) // getting unexpected EOF
}
defer resp.Body.Close()
fmt.Println(resp.StatusCode)
輸出
panic: unexpected EOF
goroutine 1 [running]:
main.main()
/Users/rishabharya/Desktop/Projects/src/github.com/rishabh-arya95/raw_http/main.go:31 0x217
exit status 2
uj5u.com熱心網友回復:
您應該將TLS加密與應用層協議協商(ALPN) 一起使用。
發生什么了?
- 客戶端與服務器建立網路連接。
- 客戶端使用HTTP/2 協議發送請求。
- 服務器接收資料并期望它是TLS 握手協議的一部分。
- 服務器必須關閉連接,因為資料不滿足TLS協議。
- 一個io.ErrUnexpectedEOF錯誤被回傳,因為在讀取回應之前連接已關閉。
需要改變什么
客戶端與標準HTTPS 埠(埠號為443 )建立連接,但使用net.Dial()函式。HTTPS協議使用 TLS來保護 Internet 上的 HTTP 連接。因此,客戶端必須使用tls.Dial()函式來建立連接:
tcpConn, err := tls.Dial("tcp", "clients1.google.com:443", new(tls.Config))
但是,客戶端不能使用空的TLS 配置。在這種特殊情況下,服務器支持兩種協議:HTTP/1.1和HTTP/2。默認情況下,服務器使用 HTTP/1.1 協議。請求使用 HTTP/2 協議有兩種方式:
- 使用TLS 協議的應用層協議協商(ALPN)擴展。
- 使用HTTP 升級機制。
第二種方法不合適。第一個原因是http2包不是為此而設計的。第二個原因是服務器忽略了給定請求的 HTTP 升級機制。
為了使用第一種方法,我們需要將“h2”識別符號添加到支持的應用級協議串列中(所有注冊識別符號的串列可以在IANA網站上找到)。
conf := & tls.Config {
NextProtos: []string{"h2"},
}
tcpConn, err := tls.Dial("tcp", "clients1.google.com:443", conf)
作業代碼
package main
import (
"crypto/tls"
"fmt"
"net/http"
"golang.org/x/net/http2"
)
func main() {
conf := & tls.Config {
NextProtos: []string{"h2"},
}
tcpConn, err := tls.Dial("tcp", "clients1.google.com:443", conf)
if err != nil {
panic(err)
}
defer tcpConn.Close()
t := http2.Transport{}
http2ClientConn, err := t.NewClientConn(tcpConn)
if err != nil {
panic(err)
}
req, err := http.NewRequest("GET", "https://clients1.google.com/generate_204", nil)
if err != nil {
panic(err)
}
resp, err := http2ClientConn.RoundTrip(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println(resp.StatusCode)
}
和一個稍微修改的版本。
cURL是一個方便的檢查工具。
發出 HTTP/2 請求:
$ curl -i --http2 https://clients1.google.com/generate_204
-| HTTP/2 204
禁用應用層協議協商 (ALPN):
$ curl -i --http2 --no-alpn https://clients1.google.com/generate_204
-| HTTP/1.1 204 No Content
發送沒有應用層協議協商 (ALPN) 的 HTTP/2 請求:
$ curl -i --http2-prior-knowledge --no-alpn https://clients1.google.com/generate_204
-| curl: (52) Empty reply from server
錯誤52:服務器沒有回復任何內容,這里認為是錯誤。
| 范圍 | 描述 |
|---|---|
| -一世 | 在輸出中包含 HTTP 回應標頭。 |
| -v | 在操作期間使 curl 冗長。 |
| --no-alpn | 禁用 ALPN TLS 擴展。 |
| --http2 | 告訴 curl 使用 HTTP 版本 2。 |
| --http2-先驗知識 | 告訴 curl 使用 HTTP 版本 2。 |
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/480237.html
