英文:
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) // 获取到意外的EOF错误
}
defer resp.Body.Close()
fmt.Println(resp.StatusCode)
输出
panic: 意外的EOF
goroutine 1 [running]:
main.main()
/Users/rishabharya/Desktop/Projects/src/github.com/rishabh-arya95/raw_http/main.go:31 +0x217
exit status 2
英文:
Was trying out the http2 package in go, and while creating an HTTP2 connection I am getting an unexpected EOF error. Not able to figure out the exact issue.
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)
Output
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
答案1
得分: 1
你应该使用传输层安全性(TLS)加密和应用层协议协商(ALPN)。
发生了什么?
- 客户端与服务器建立网络连接。
- 客户端使用HTTP/2协议发送请求。
- 服务器接收数据,并期望它是TLS握手协议的一部分。
- 服务器必须关闭连接,因为数据不符合TLS协议。
- 在响应读取之前,返回io.ErrUnexpectedEOF错误,因为连接已关闭。
需要进行的更改
客户端使用net.Dial()函数建立与标准HTTPS端口(端口号为443)的连接。HTTPS协议使用TLS在互联网上保护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:服务器没有回复任何内容,这在这里被视为错误。
参数 | 描述 |
---|---|
-i | 在输出中包含HTTP响应头。 |
-v | 在操作过程中使curl详细显示。 |
--no-alpn | 禁用ALPN TLS扩展。 |
--http2 | 告诉curl使用HTTP版本2。 |
--http2-prior-knowledge | 告诉curl使用HTTP版本2。 |
英文:
You should use TLS encryption with Application-Layer Protocol Negotiation (ALPN).
What happens?
- Client establish network connection to server.
- Client sends a request using HTTP/2 protocol.
- Server receives the data and expects it to be part of TLS Handshake protocol.
- Server has to close the connection because the data does not satisfy TLS protocol.
- An io.ErrUnexpectedEOF error is returned because the connection was closed before the response was read.
What needs to be change
The client establishes a connection to the standard HTTPS port (port number is 443), but use net.Dial() function. HTTPS protocol use TLS to secure HTTP connection over the Internet. Therefore, the client have to use tls.Dial() function to establish a connection:
tcpConn, err := tls.Dial("tcp", "clients1.google.com:443", new(tls.Config))
However, the client cannot use an empty TLS configuration. In this particular case, the server supports both protocols: HTTP/1.1 and HTTP/2. By default, the server uses HTTP/1.1 protocol. There are two ways to request the use of HTTP/2 protocol:
- Use Application-Layer Protocol Negotiation (ALPN) extension of TLS protocol.
- Use the HTTP Upgrade mechanism.
The second method is not suitable. The first reason is that http2 package is not intended for this. The second reason is that the server ignores HTTP Upgrade mechanism for the given request.
In order to use the first methods, we need to add "h2" identifier to a list of supported application level protocols (a list of all registered identifiers can be found on IANA website).
conf := & tls.Config {
NextProtos: []string{"h2"},
}
tcpConn, err := tls.Dial("tcp", "clients1.google.com:443", conf)
Working Code
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)
}
And a slightly modified version.
cURL is a handy tool for checking.
Make an HTTP/2 request:
$ curl -i --http2 https://clients1.google.com/generate_204
-| HTTP/2 204
Disable Application-Layer Protocol Negotiation (ALPN):
$ curl -i --http2 --no-alpn https://clients1.google.com/generate_204
-| HTTP/1.1 204 No Content
Send an HTTP/2 request without Application-Layer Protocol Negotiation (ALPN):
$ curl -i --http2-prior-knowledge --no-alpn https://clients1.google.com/generate_204
-| curl: (52) Empty reply from server
Error 52: The server did not reply anything, which here is considered an error.
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. |
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论