Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/net/http2/h2c: h2c server hangs on empty r.Body read and fails on writes + hangs for POSTs #52882

Closed
ldemailly opened this issue May 13, 2022 · 13 comments
Labels
NeedsFix
Milestone

Comments

@ldemailly
Copy link

@ldemailly ldemailly commented May 13, 2022

What version of Go are you using (go version)?

$ go version
go version go1.18.2 darwin/arm64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GOARCH="arm64"
GOVERSION="go1.18.2"
GOHOSTARCH="arm64"
GOHOSTOS="darwin"

What did you do?

h2c server - with code to read request body:

https://go.dev/play/p/yaKhF5u4ut6

package main

import (
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"

	"golang.org/x/net/http2"
	"golang.org/x/net/http2/h2c"
)

func server() {
	h2s := &http2.Server{}

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Printf("Got %v %v request from %v\n", r.Method, r.Proto, r.RemoteAddr)
		body, err := io.ReadAll(r.Body)
		if err != nil {
			log.Fatalf("server body read err: %v\n", err)
		}
		fmt.Printf("read %d from body\n", len(body))
		w.WriteHeader(http.StatusAccepted)
		fmt.Fprintf(w, "Hello, %v; HTTP Version: %v\n\nbody:\n%s\n", r.URL.Path, r.Proto, string(body))
	})

	server := &http.Server{
		Addr:    ":8001",
		Handler: h2c.NewHandler(handler, h2s),
	}
	fmt.Printf("H2c Server starting on %s\n", server.Addr)
	err := server.ListenAndServe()
	if err != nil {
		panic(err)
	}
}

func main() {
	server()
}

What did you expect to see?

$ curl -d bar --http2 localhost:8001/foo
Hello, /foo; HTTP Version: HTTP/2.0

body:
bar

to succeed - it hangs

note that --http2-prior-knowledge or --http1.1 works fine

What did you see instead?

hang on curl side, server side:

H2c Server starting on :8001
Got POST HTTP/1.1 request from 127.0.0.1:57205
read 3 from body
2022/05/12 17:15:13 http: response.WriteHeader on hijacked connection from main.server.func1 (h2-bug.go:51)
2022/05/12 17:29:16 http: response.Write on hijacked connection from fmt.Fprintf (print.go:205)

If not doing a POST (ie without a body) the ReadAll hangs (that I could workaround by checking the method, even though it probably shouldn't hang)

@ldemailly ldemailly changed the title affected/package: golang.org/x/net/http2/h2c - h2c server hangs on r.Body read and fails on w.WriteHeader golang.org/x/net/http2/h2c: h2c server hangs on r.Body read and fails on w.WriteHeader May 13, 2022
@ldemailly ldemailly changed the title golang.org/x/net/http2/h2c: h2c server hangs on r.Body read and fails on w.WriteHeader golang.org/x/net/http2/h2c: h2c server hangs on empty r.Body read and fails on writes + hangs for POSTs May 13, 2022
@ldemailly
Copy link
Author

@ldemailly ldemailly commented May 13, 2022

Forgot to mention that this is with

curl 7.83.0 (aarch64-apple-darwin21.3.0) libcurl/7.83.0 (SecureTransport) OpenSSL/1.1.1o zlib/1.2.11 brotli/1.0.9 zstd/1.5.2 libidn2/2.3.2 libssh2/1.10.0 nghttp2/1.47.0 librtmp/2.3 OpenLDAP/2.6.2
Release-Date: 2022-04-27
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp 
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz MultiSSL NTLM NTLM_WB SPNEGO SSL TLS-SRP UnixSockets zstd

and just rechecked, same with go1.18.2

just the error is only on 2nd write:

H2c Server starting on :8001
Got POST HTTP/1.1 request from 127.0.0.1:57528
read 3 from body
2022/05/12 17:29:16 http: response.Write on hijacked connection from fmt.Fprintf (print.go:205)

@ldemailly
Copy link
Author

@ldemailly ldemailly commented May 13, 2022

Code is also available on https://github.com/fortio/h2-bug

@heschi
Copy link
Contributor

@heschi heschi commented May 13, 2022

cc @neild

@heschi heschi added the NeedsInvestigation label May 13, 2022
@heschi heschi added this to the Backlog milestone May 13, 2022
@ldemailly
Copy link
Author

@ldemailly ldemailly commented May 14, 2022

Thanks! Note that the same happens when trying to use ReverseProxy as well (which is how I went down this path:)

curl/7.83.0/bin/curl -v --http2 -d foo "localhost:8001/echo?delay=1s"

Server GODEBUG=http2debug=2 go run .

2022/05/14 14:21:57 http2: Framer 0x1400014e1c0: wrote SETTINGS len=18, settings: MAX_CONCURRENT_STREAMS=100, INITIAL_WINDOW_SIZE=33554432, ENABLE_PUSH=0
2022/05/14 14:21:57 http2: Framer 0x1400014e1c0: wrote HEADERS flags=END_HEADERS stream=1 len=74
web req: /echo?delay=1s
2022/05/14 14:21:58 http: response.WriteHeader on hijacked connection from net/http/httputil.(*ReverseProxy).ServeHTTP (reverseproxy.go:337)
2022/05/14 14:21:58 http: response.Write on hijacked connection from net/http/httputil.(*ReverseProxy).copyBuffer (reverseproxy.go:467)
	httpURL, _ := url.Parse("http://localhost:8080/")
	httpProxy := httputil.NewSingleHostReverseProxy(httpURL)
	mux := http.NewServeMux()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Printf("web req: %v\n", r.URL)
		httpProxy.ServeHTTP(w, r)
	})
	h2s := &http2.Server{}
	srv := &http.Server{
		Addr:    ":8001",
		Handler: h2c.NewHandler(mux, h2s),
	}
	//http2.ConfigureServer(srv, nil)
	log.Fatal(srv.ListenAndServe())

curl side logs (failing upgrade:):

/opt/homebrew/Cellar/curl/7.83.0/bin/curl --trace - --trace-time -m 2 --http2 -d foo "localhost:8001/echo?delay=1s"   
14:34:15.830214 == Info:   Trying 127.0.0.1:8001...
14:34:15.830736 == Info: Connected to localhost (127.0.0.1) port 8001 (#0)
14:34:15.830800 => Send header, 253 bytes (0xfd)
0000: 50 4f 53 54 20 2f 65 63 68 6f 3f 64 65 6c 61 79 POST /echo?delay
0010: 3d 31 73 20 48 54 54 50 2f 31 2e 31 0d 0a 48 6f =1s HTTP/1.1..Ho
0020: 73 74 3a 20 6c 6f 63 61 6c 68 6f 73 74 3a 38 30 st: localhost:80
0030: 30 31 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 01..User-Agent: 
0040: 63 75 72 6c 2f 37 2e 38 33 2e 30 0d 0a 41 63 63 curl/7.83.0..Acc
0050: 65 70 74 3a 20 2a 2f 2a 0d 0a 43 6f 6e 6e 65 63 ept: */*..Connec
0060: 74 69 6f 6e 3a 20 55 70 67 72 61 64 65 2c 20 48 tion: Upgrade, H
0070: 54 54 50 32 2d 53 65 74 74 69 6e 67 73 0d 0a 55 TTP2-Settings..U
0080: 70 67 72 61 64 65 3a 20 68 32 63 0d 0a 48 54 54 pgrade: h2c..HTT
0090: 50 32 2d 53 65 74 74 69 6e 67 73 3a 20 41 41 4d P2-Settings: AAM
00a0: 41 41 41 42 6b 41 41 51 43 41 41 41 41 41 41 49 AAABkAAQCAAAAAAI
00b0: 41 41 41 41 41 0d 0a 43 6f 6e 74 65 6e 74 2d 4c AAAAA..Content-L
00c0: 65 6e 67 74 68 3a 20 33 0d 0a 43 6f 6e 74 65 6e ength: 3..Conten
00d0: 74 2d 54 79 70 65 3a 20 61 70 70 6c 69 63 61 74 t-Type: applicat
00e0: 69 6f 6e 2f 78 2d 77 77 77 2d 66 6f 72 6d 2d 75 ion/x-www-form-u
00f0: 72 6c 65 6e 63 6f 64 65 64 0d 0a 0d 0a          rlencoded....
14:34:15.830909 => Send data, 3 bytes (0x3)
0000: 66 6f 6f                                        foo
14:34:15.831048 == Info: Mark bundle as not supporting multiuse
14:34:15.831057 <= Recv header, 34 bytes (0x22)
0000: 48 54 54 50 2f 31 2e 31 20 31 30 31 20 53 77 69 HTTP/1.1 101 Swi
0010: 74 63 68 69 6e 67 20 50 72 6f 74 6f 63 6f 6c 73 tching Protocols
0020: 0d 0a                                           ..
14:34:15.831078 <= Recv header, 21 bytes (0x15)
0000: 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 55 70 67 72 Connection: Upgr
0010: 61 64 65 0d 0a                                  ade..
14:34:15.831093 <= Recv header, 14 bytes (0xe)
0000: 55 70 67 72 61 64 65 3a 20 68 32 63 0d 0a       Upgrade: h2c..
14:34:15.831103 == Info: Received 101
14:34:15.831118 == Info: Using HTTP2, server supports multiplexing
14:34:15.831131 == Info: Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
14:34:17.831253 == Info: Operation timed out after 2003 milliseconds with 0 bytes received
14:34:17.831512 == Info: Connection #0 to host localhost left intact
curl: (28) Operation timed out after 2003 milliseconds with 0 bytes received

prior knowledge (working):

dl@Laurents-Air fortio % /opt/homebrew/Cellar/curl/7.83.0/bin/curl --trace - --trace-time -m 2 --http2-prior-knowledge -d foo "localhost:8001/echo?delay=1s"
14:35:43.087749 == Info:   Trying 127.0.0.1:8001...
14:35:43.088298 == Info: Connected to localhost (127.0.0.1) port 8001 (#0)
14:35:43.088327 == Info: Using HTTP2, server supports multiplexing
14:35:43.088338 == Info: Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
14:35:43.088396 == Info: h2h3 [:method: POST]
14:35:43.088402 == Info: h2h3 [:path: /echo?delay=1s]
14:35:43.088407 == Info: h2h3 [:scheme: http]
14:35:43.088412 == Info: h2h3 [:authority: localhost:8001]
14:35:43.088417 == Info: h2h3 [user-agent: curl/7.83.0]
14:35:43.088421 == Info: h2h3 [accept: */*]
14:35:43.088426 == Info: h2h3 [content-length: 3]
14:35:43.088430 == Info: h2h3 [content-type: application/x-www-form-urlencoded]
14:35:43.088442 == Info: Using Stream ID: 1 (easy handle 0x14c013a00)
14:35:43.088469 => Send header, 158 bytes (0x9e)
0000: 50 4f 53 54 20 2f 65 63 68 6f 3f 64 65 6c 61 79 POST /echo?delay
0010: 3d 31 73 20 48 54 54 50 2f 32 0d 0a 48 6f 73 74 =1s HTTP/2..Host
0020: 3a 20 6c 6f 63 61 6c 68 6f 73 74 3a 38 30 30 31 : localhost:8001
0030: 0d 0a 75 73 65 72 2d 61 67 65 6e 74 3a 20 63 75 ..user-agent: cu
0040: 72 6c 2f 37 2e 38 33 2e 30 0d 0a 61 63 63 65 70 rl/7.83.0..accep
0050: 74 3a 20 2a 2f 2a 0d 0a 63 6f 6e 74 65 6e 74 2d t: */*..content-
0060: 6c 65 6e 67 74 68 3a 20 33 0d 0a 63 6f 6e 74 65 length: 3..conte
0070: 6e 74 2d 74 79 70 65 3a 20 61 70 70 6c 69 63 61 nt-type: applica
0080: 74 69 6f 6e 2f 78 2d 77 77 77 2d 66 6f 72 6d 2d tion/x-www-form-
0090: 75 72 6c 65 6e 63 6f 64 65 64 0d 0a 0d 0a       urlencoded....
14:35:43.088555 => Send data, 3 bytes (0x3)
0000: 66 6f 6f                                        foo
14:35:43.088564 == Info: We are completely uploaded and fine
14:35:43.088786 == Info: Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
14:35:44.092396 <= Recv header, 13 bytes (0xd)
0000: 48 54 54 50 2f 32 20 32 30 30 20 0d 0a          HTTP/2 200 ..
14:35:44.092445 <= Recv header, 49 bytes (0x31)
0000: 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 20 61 70 content-type: ap
0010: 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 77 77 77 2d plication/x-www-
0020: 66 6f 72 6d 2d 75 72 6c 65 6e 63 6f 64 65 64 0d form-urlencoded.
0030: 0a                                              .
14:35:44.092484 <= Recv header, 37 bytes (0x25)
0000: 64 61 74 65 3a 20 53 61 74 2c 20 31 34 20 4d 61 date: Sat, 14 Ma
0010: 79 20 32 30 32 32 20 32 31 3a 33 35 3a 34 34 20 y 2022 21:35:44 
0020: 47 4d 54 0d 0a                                  GMT..
14:35:44.092515 <= Recv header, 19 bytes (0x13)
0000: 63 6f 6e 74 65 6e 74 2d 6c 65 6e 67 74 68 3a 20 content-length: 
0010: 33 0d 0a                                        3..
14:35:44.092537 <= Recv header, 2 bytes (0x2)
0000: 0d 0a                                           ..
14:35:44.092557 <= Recv data, 3 bytes (0x3)
0000: 66 6f 6f                                        foo
foo14:35:44.092602 == Info: Connection #0 to host localhost left intact

working server side:

2022/05/14 14:35:43 h2c: attempting h2c with prior knowledge.
2022/05/14 14:35:43 http2: server connection from 127.0.0.1:50901 on 0x14000282460
2022/05/14 14:35:43 http2: Framer 0x14000282620: wrote SETTINGS len=24, settings: MAX_FRAME_SIZE=1048576, MAX_CONCURRENT_STREAMS=250, MAX_HEADER_LIST_SIZE=1048896, INITIAL_WINDOW_SIZE=1048576
2022/05/14 14:35:43 http2: server: client 127.0.0.1:50901 said hello
2022/05/14 14:35:43 http2: Framer 0x14000282620: wrote WINDOW_UPDATE len=4 (conn) incr=983041
2022/05/14 14:35:43 http2: Framer 0x14000282620: read SETTINGS len=18, settings: MAX_CONCURRENT_STREAMS=100, INITIAL_WINDOW_SIZE=33554432, ENABLE_PUSH=0
2022/05/14 14:35:43 http2: server read frame SETTINGS len=18, settings: MAX_CONCURRENT_STREAMS=100, INITIAL_WINDOW_SIZE=33554432, ENABLE_PUSH=0
2022/05/14 14:35:43 http2: server processing setting [MAX_CONCURRENT_STREAMS = 100]
2022/05/14 14:35:43 http2: server processing setting [INITIAL_WINDOW_SIZE = 33554432]
2022/05/14 14:35:43 http2: server processing setting [ENABLE_PUSH = 0]
2022/05/14 14:35:43 http2: Framer 0x14000282620: wrote SETTINGS flags=ACK len=0
2022/05/14 14:35:43 http2: Framer 0x14000282620: read WINDOW_UPDATE len=4 (conn) incr=33488897
2022/05/14 14:35:43 http2: server read frame WINDOW_UPDATE len=4 (conn) incr=33488897
2022/05/14 14:35:43 http2: Framer 0x14000282620: read HEADERS flags=END_HEADERS stream=1 len=72
2022/05/14 14:35:43 http2: decoded hpack field header field ":method" = "POST"
2022/05/14 14:35:43 http2: decoded hpack field header field ":path" = "/echo?delay=1s"
2022/05/14 14:35:43 http2: decoded hpack field header field ":scheme" = "http"
2022/05/14 14:35:43 http2: decoded hpack field header field ":authority" = "localhost:8001"
2022/05/14 14:35:43 http2: decoded hpack field header field "user-agent" = "curl/7.83.0"
2022/05/14 14:35:43 http2: decoded hpack field header field "accept" = "*/*"
2022/05/14 14:35:43 http2: decoded hpack field header field "content-length" = "3"
2022/05/14 14:35:43 http2: decoded hpack field header field "content-type" = "application/x-www-form-urlencoded"
2022/05/14 14:35:43 http2: server read frame HEADERS flags=END_HEADERS stream=1 len=72
2022/05/14 14:35:43 http2: Framer 0x14000282620: read DATA flags=END_STREAM stream=1 len=3 data="foo"
2022/05/14 14:35:43 http2: server read frame DATA flags=END_STREAM stream=1 len=3 data="foo"
2022/05/14 14:35:43 http2: Framer 0x14000282620: read SETTINGS flags=ACK len=0
2022/05/14 14:35:43 http2: server read frame SETTINGS flags=ACK len=0
web2 req: /echo?delay=1s
2022/05/14 14:35:43 http2: Framer 0x14000282620: wrote WINDOW_UPDATE len=4 (conn) incr=3
2022/05/14 14:35:44 http2: server encoding header ":status" = "200"
2022/05/14 14:35:44 http2: server encoding header "content-type" = "application/x-www-form-urlencoded"
2022/05/14 14:35:44 http2: server encoding header "date" = "Sat, 14 May 2022 21:35:44 GMT"
2022/05/14 14:35:44 http2: server encoding header "content-length" = "3"
2022/05/14 14:35:44 http2: Framer 0x14000282620: wrote HEADERS flags=END_HEADERS stream=1 len=54
2022/05/14 14:35:44 http2: Framer 0x14000282620: wrote DATA flags=END_STREAM stream=1 len=3 data="foo"

@seankhliao seankhliao changed the title golang.org/x/net/http2/h2c: h2c server hangs on empty r.Body read and fails on writes + hangs for POSTs x/net/http2/h2c: h2c server hangs on empty r.Body read and fails on writes + hangs for POSTs May 14, 2022
@ZekeLu
Copy link

@ZekeLu ZekeLu commented May 19, 2022

I think the server is not correctly configured. Here is the revised version that works: https://go.dev/play/p/hO_OnkMS-wz

I will highlight the changes at the end of this comment.

According to the doc of https://pkg.go.dev/golang.org/x/net/http2#ConfigureServer,

ConfigureServer must be called before s begins serving.

And here is the now deleted http2 server demo that could be helpful: https://github.com/golang/net/blob/69e39bad7dc2bbb411fa35755c46020969029fa7/http2/h2demo/h2demo.go

 package main
 
 import (
-   "crypto/tls"
    "fmt"
    "io"
    "log"
-   "net"
    "net/http"
-   "net/url"
 
    "golang.org/x/net/http2"
-   "golang.org/x/net/http2/h2c"
 )
 
 func server() {
    h2s := &http2.Server{}
 
    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Printf("Got %v %v request from %v\n", r.Method, r.Proto, r.RemoteAddr)
        body, err := io.ReadAll(r.Body)
        if err != nil {
            log.Fatalf("server body read err: %v\n", err)
        }
        fmt.Printf("read %d from body\n", len(body))
        w.WriteHeader(http.StatusAccepted)
        fmt.Fprintf(w, "Hello, %v; HTTP Version: %v\n\nbody:\n%s\n", r.URL.Path, r.Proto, string(body))
    })
 
+   http.HandleFunc("/", handler)
+
    server := &http.Server{
        Addr:    ":8001",
-       Handler: h2c.NewHandler(handler, h2s),
    }
+   http2.ConfigureServer(server, h2s)
    fmt.Printf("H2c Server starting on %s\n", server.Addr)
    err := server.ListenAndServe()
    if err != nil {
        panic(err)
    }
 }
 
 func main() {
    server()
 }

@seankhliao
Copy link
Member

@seankhliao seankhliao commented May 19, 2022

@ZekeLu this issue is about the h2c (http2 using cleartext) upgrade process. your example drops the important part out

@ZekeLu
Copy link

@ZekeLu ZekeLu commented May 19, 2022

Ah, I totally missed the h2c part. Thank you for pointing that out and sorry for making the noise.

@ldemailly
Copy link
Author

@ldemailly ldemailly commented May 19, 2022

yeah as I mentioned in the description (sorry if it's noisy/unclear) --http2-prior-knowledge does work fine

@neild
Copy link
Contributor

@neild neild commented May 19, 2022

The h2c package handles protocol upgrades by reading the initial HTTP/1 request and rewriting it into an HTTP/2 stream of bytes:
https://go.googlesource.com/net/+/refs/heads/master/http2/h2c/h2c.go#208

It then takes those converted HTTP/2 bytes, prepends them to the subsequent HTTP/2 stream sent from the client, and presents this to the HTTP/2 server impementation.

The conversion from HTTP/1 to HTTP/2 does not appear to support request bodies at all, so any initial HTTP/1 request with a body is mishandled. Also, I think reading from the request body at all (even when it's not present) may be broken due to the synthetic HTTP/2 request not including an END_STREAM flag.

To make things even more fun, a fundamental problem with this approach is that it doesn't deal with HTTP/2 flow control at all.

I honestly have no idea how to fix this without completely rearchitecting h2c support. If we want to support h2c, we should stop rewriting HTTP/1 requests into HTTP/2 and have a real mechanism for handing off an upgraded request to the HTTP/2 server.

@gopherbot
Copy link

@gopherbot gopherbot commented May 19, 2022

Change https://go.dev/cl/407454 mentions this issue: http2/h2c: connection handoff without translation

@ldemailly
Copy link
Author

@ldemailly ldemailly commented May 20, 2022

I used the your cl branch and I can confirm this fixes the issue I reported! thanks so much! do you know when it'll be available on the main branch?

while I have you I would love an h2c upgrade client and a way for the ReverseProxy to do h2c prior knowledge (h2c:// urls?)
(the reason I need all this is to write a simple proxy that can do h1.1, h2c and grpc proxy https://github.com/fortio/proxy still doesn't work for grpc after this fix - possibly because I don't know how to change the ReverseProxy Director http.Request to tell it to try h2)

@ldemailly
Copy link
Author

@ldemailly ldemailly commented May 20, 2022

Answering my own question, I got it working !!

	rp := httputil.ReverseProxy{
		Director: Director,
		Transport: &http2.Transport{
			AllowHTTP: true,
			DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
				return net.Dial(network, addr)
			},
		},
	}

Still wondering how to configure as h2c upgrade though so it would work with h1.1 targets with a single transport

ldemailly added a commit to fortio/proxy that referenced this issue May 20, 2022
ldemailly added a commit to fortio/proxy that referenced this issue May 20, 2022
@ldemailly
Copy link
Author

@ldemailly ldemailly commented May 21, 2022

unfortunately because of #44840 (comment) I can't seem to use my fork to make a release (do I miss something obvious in my go.mod?)

Edit: found a workaround, change the import and go.mod in the fork...

ldemailly pushed a commit to fortio/net that referenced this issue May 21, 2022
The initial request on an h2c-upgraded connection is written as an
HTTP/1 request, with the response sent as an HTTP/2 stream.

The h2c package handled this request by constructing a sequence of
bytes representing an HTTP/2 stream containing the initial request,
prepending those bytes to the remainder of the connection, and
presenting that to the HTTP/2 server as if no upgrade had happened.
This translation did not handle request bodies. Handling request
bodies under this model would be difficult, since it would require
also translating the HTTP/2 flow control.

Rewrite the h2c upgrade to explicitly hand off the request to the
HTTP/2 server instead.

Fixes golang/go#52882.

Change-Id: I26e0f12e2b1c8b48fd36ba47baea076424983553
ldemailly added a commit to fortio/fortio that referenced this issue May 25, 2022
ldemailly added a commit to fortio/fortio that referenced this issue May 25, 2022
* use h2c fixed version of x/net ( golang/go#52882 )

* add a test using curl from the build image

* add max 10s timeout for when the test fails (on master or future regressions)

* prep for 1.32.1
dteh pushed a commit to dteh/fhttp that referenced this issue Jun 22, 2022
The initial request on an h2c-upgraded connection is written as an
HTTP/1 request, with the response sent as an HTTP/2 stream.

The h2c package handled this request by constructing a sequence of
bytes representing an HTTP/2 stream containing the initial request,
prepending those bytes to the remainder of the connection, and
presenting that to the HTTP/2 server as if no upgrade had happened.
This translation did not handle request bodies. Handling request
bodies under this model would be difficult, since it would require
also translating the HTTP/2 flow control.

Rewrite the h2c upgrade to explicitly hand off the request to the
HTTP/2 server instead.

Fixes golang/go#52882.

Change-Id: I26e0f12e2b1c8b48fd36ba47baea076424983553
Reviewed-on: https://go-review.googlesource.com/c/net/+/407454
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Damien Neil <dneil@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
@dmitshur dmitshur added NeedsFix and removed NeedsInvestigation labels Jul 2, 2022
@dmitshur dmitshur removed this from the Backlog milestone Jul 2, 2022
@dmitshur dmitshur added this to the Unreleased milestone Jul 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsFix
Projects
None yet
Development

No branches or pull requests

7 participants