使用 HTTP2 传输获得意外的 EOF
Getting unexpected EOF with HTTP2 Transport
正在尝试使用 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
您应该使用 TLS encryption with Application-Layer Protocol Negotiation (ALPN)。
会发生什么?
- 客户端 establish 到服务器的网络连接。
- 客户sends a request using HTTP/2 protocol.
- 服务器接收数据并期望它是 TLS Handshake 协议的一部分。
- 服务器不得不关闭连接,因为数据不满足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 configuration. In this particular case, the server supports both protocols: HTTP/1.1 and HTTP/2。默认情况下,服务器使用 HTTP/1.1 协议。请求使用HTTP/2协议有两种方式:
- 使用 Application-Layer Protocol Negotiation (ALPN) extension 的 TLS 协议。
- 使用HTTP Upgrade机制。
第二种方法不适用。第一个原因是 http2 包不是为此而设计的。第二个原因是服务器忽略了给定请求的 HTTP 升级机制。
为了使用第一种方法,我们需要添加“h2”identifier to a list of supported application level protocols (a list of all registered identifiers can be found on 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)
}
还有一点modified version.
cURL 是一个方便的检查工具。
发出 HTTP/2 请求:
$ curl -i --http2 https://clients1.google.com/generate_204
-| HTTP/2 204
禁用Application-Layer协议协商(ALPN):
$ curl -i --http2 --no-alpn https://clients1.google.com/generate_204
-| HTTP/1.1 204 No Content
不使用 Application-Layer 协议协商 (ALPN) 发送 HTTP/2 请求:
$ curl -i --http2-prior-knowledge --no-alpn https://clients1.google.com/generate_204
-| curl: (52) Empty reply from server
错误52:服务器未回复任何内容,此处视为错误。
Parameter
Description
-i
Include the HTTP response headers in the output.
-v
Makes curl verbose during the operation.
--no-alpn
Disable the ALPN TLS extension.
--http2
Tells curl to use HTTP version 2.
--http2-prior-knowledge
Tells curl to use HTTP version 2.
正在尝试使用 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
您应该使用 TLS encryption with Application-Layer Protocol Negotiation (ALPN)。
会发生什么?
- 客户端 establish 到服务器的网络连接。
- 客户sends a request using HTTP/2 protocol.
- 服务器接收数据并期望它是 TLS Handshake 协议的一部分。
- 服务器不得不关闭连接,因为数据不满足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 configuration. In this particular case, the server supports both protocols: HTTP/1.1 and HTTP/2。默认情况下,服务器使用 HTTP/1.1 协议。请求使用HTTP/2协议有两种方式:
- 使用 Application-Layer Protocol Negotiation (ALPN) extension 的 TLS 协议。
- 使用HTTP Upgrade机制。
第二种方法不适用。第一个原因是 http2 包不是为此而设计的。第二个原因是服务器忽略了给定请求的 HTTP 升级机制。
为了使用第一种方法,我们需要添加“h2”identifier to a list of supported application level protocols (a list of all registered identifiers can be found on 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)
}
还有一点modified version.
cURL 是一个方便的检查工具。
发出 HTTP/2 请求:
$ curl -i --http2 https://clients1.google.com/generate_204
-| HTTP/2 204
禁用Application-Layer协议协商(ALPN):
$ curl -i --http2 --no-alpn https://clients1.google.com/generate_204
-| HTTP/1.1 204 No Content
不使用 Application-Layer 协议协商 (ALPN) 发送 HTTP/2 请求:
$ curl -i --http2-prior-knowledge --no-alpn https://clients1.google.com/generate_204
-| curl: (52) Empty reply from server
错误52:服务器未回复任何内容,此处视为错误。
Parameter | Description |
---|---|
-i | Include the HTTP response headers in the output. |
-v | Makes curl verbose during the operation. |
--no-alpn | Disable the ALPN TLS extension. |
--http2 | Tells curl to use HTTP version 2. |
--http2-prior-knowledge | Tells curl to use HTTP version 2. |