遇到 HTTP2 传输时出现意外的 EOF(文件结束符)。

huangapple go评论109阅读模式
英文:

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)

发生了什么?

  1. 客户端与服务器建立网络连接。
  2. 客户端使用HTTP/2协议发送请求。
  3. 服务器接收数据,并期望它是TLS握手协议的一部分。
  4. 服务器必须关闭连接,因为数据不符合TLS协议。
  5. 在响应读取之前,返回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.1HTTP/2。默认情况下,服务器使用HTTP/1.1协议。有两种方法可以请求使用HTTP/2协议:

第二种方法不适用。第一个原因是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?

  1. Client establish network connection to server.
  2. Client sends a request using HTTP/2 protocol.
  3. Server receives the data and expects it to be part of TLS Handshake protocol.
  4. Server has to close the connection because the data does not satisfy TLS protocol.
  5. 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:

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.

huangapple
  • 本文由 发表于 2022年5月20日 19:10:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/72317981.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定