Getting unexpected EOF with HTTP2 Transport

Issue

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

Solution

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.

Answered By – Andrei Vasilev

Answer Checked By – Robin (GoLangFix Admin)

Leave a Reply

Your email address will not be published.