-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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: upgrade fails with request body #38064
Comments
Any updates on this issue? |
This looks like a bug in the x/net/http2/h2c package. From RFC 7540 section 3.2:
and
The specified behavior (which is also how the Java client seems to behave, based on the screenshots in the issue description) can be seen by using something like $ curl -d hello=world --http2 http://127.0.0.1:12345 and in another shell $ nc -l 12345
POST / HTTP/1.1
Host: 127.0.0.1:12345
User-Agent: curl/7.66.0
Accept: */*
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
Content-Length: 11
Content-Type: application/x-www-form-urlencoded
hello=world At this point curl will wait for the response. To respond it's enough to send the following to curl (including the multiple line breaks at the end): HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c
Once the response was send, curl will continue speaking HTTP/2 PRI * HTTP/2.0
SM
d? The reason the x/net/http2/h2c handler fails is that the I think the correct way to fix this would be to just handle the initial request before hijacking the connection, by passing it to the wrapped |
cc @fraenkel |
Can someone provide a test case? |
using curl: package main
import (
"fmt"
"net/http"
"os"
"os/exec"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
func main() {
H2CUpgrade()
}
func H2CUpgrade() {
h2s := &http2.Server{}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %v, http: %v", r.URL.Path, r.TLS == nil)
})
server := &http.Server{
Addr: "0.0.0.0:8090",
Handler: h2c.NewHandler(handler, h2s),
}
go func() {
fmt.Printf("Listening [0.0.0.0:8090]...\n")
server.ListenAndServe()
}()
cmd := exec.Command("curl", "-d", "hello=world", "--http2", "http://127.0.0.1:8090")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
fmt.Println(cmd.Run())
} outputs and hangs indefinitely
|
This uncovered a few issues.
The body of the original request is being munged with the client preface, |
Change https://golang.org/cl/288572 mentions this issue: |
A related problem exists with GET requests. If handler attempts to read out the body it will hang: package main
import (
"fmt"
"net/http"
"os"
"io"
"os/exec"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
func main() {
H2CUpgrade()
}
func H2CUpgrade() {
h2s := &http2.Server{}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("proto: %v, content-length: %v\n", r.Proto, r.ContentLength)
io.Copy(io.Discard, r.Body)
})
server := &http.Server{
Addr: "0.0.0.0:8090",
Handler: h2c.NewHandler(handler, h2s),
}
go func() {
fmt.Printf("Listening [0.0.0.0:8090]...\n")
server.ListenAndServe()
}()
cmd := exec.Command("curl", "--http2", "http://127.0.0.1:8090")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
fmt.Println(cmd.Run())
} Running with Also I think the approach https://golang.org/cl/288572 is not correct - it attempts to throw away request body while it should be properly encoded similar to the request headers and passed to the handler. |
Enables HTTP/2 connections over cleartext TCP with Prior Knowledge (RFC 7540 3.4). The implementation is based on the golang.org/x/net/http2/h2c and workarounds several issues: * golang/go#38064 * golang/go#26682 See h2c package docs for details. Signed-off-by: Alexander Yastrebov <[email protected]>
Enables HTTP/2 connections over cleartext TCP with Prior Knowledge (RFC 7540 3.4). The implementation is based on the golang.org/x/net/http2/h2c and workarounds several issues: * golang/go#38064 * golang/go#26682 See h2c package docs for details. Signed-off-by: Alexander Yastrebov <[email protected]>
Enables HTTP/2 connections over cleartext TCP with Prior Knowledge (RFC 7540 3.4). The implementation is based on the golang.org/x/net/http2/h2c and workarounds several issues: * golang/go#38064 * golang/go#26682 See h2c package docs for details. Signed-off-by: Alexander Yastrebov <[email protected]>
Enables HTTP/2 connections over cleartext TCP with Prior Knowledge (RFC 7540 3.4). The implementation is based on the golang.org/x/net/http2/h2c and workarounds several issues: * golang/go#38064 * golang/go#26682 See h2c package docs for details. Signed-off-by: Alexander Yastrebov <[email protected]>
Enables HTTP/2 connections over cleartext TCP with Prior Knowledge (RFC 7540 3.4). The implementation is based on the golang.org/x/net/http2/h2c and workarounds several issues: * golang/go#38064 * golang/go#26682 See h2c package docs for details. Signed-off-by: Alexander Yastrebov <[email protected]>
Enables HTTP/2 connections over cleartext TCP with Prior Knowledge (RFC 7540 3.4). The implementation is based on the golang.org/x/net/http2/h2c and workarounds several issues: * golang/go#38064 * golang/go#26682 See h2c package docs for details. Signed-off-by: Alexander Yastrebov <[email protected]>
Enables HTTP/2 connections over cleartext TCP with Prior Knowledge (RFC 7540 3.4). The implementation is based on the golang.org/x/net/http2/h2c and workarounds several issues: * golang/go#38064 * golang/go#26682 See h2c package docs for details. Signed-off-by: Alexander Yastrebov <[email protected]>
Enables HTTP/2 connections over cleartext TCP with Prior Knowledge (RFC 7540 3.4). The implementation is based on the golang.org/x/net/http2/h2c and workarounds several issues: * golang/go#38064 * golang/go#26682 See h2c package docs for details. Signed-off-by: Alexander Yastrebov <[email protected]>
This fixes golang/go#38064 If a request that triggered an upgrade from HTTP/1.1 -> HTTP/2 contained a body, it would not be replayed by the server as a HTTP/2 data frame. This would result in hangs as the client would get no data back, as the request body was never actually handled. This code corrects this, and sends HTTP/2 DATA frames with the request body. As an example: Client: ``` $ curl -v --http2 -d 'POST BODY' http://localhost:5555 * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 5555 (#0) > POST / HTTP/1.1 > Host: localhost:5555 > User-Agent: curl/7.64.1 > Accept: */* > Connection: Upgrade, HTTP2-Settings > Upgrade: h2c > HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA > Content-Length: 9 > Content-Type: application/x-www-form-urlencoded > * upload completely sent off: 9 out of 9 bytes < HTTP/1.1 101 Switching Protocols < Connection: Upgrade < Upgrade: h2c * Received 101 * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 * Connection state changed (MAX_CONCURRENT_STREAMS == 250)! < HTTP/2 200 < content-length: 0 < date: Sat, 29 Jan 2022 06:51:05 GMT < * Connection #0 to host localhost left intact * Closing connection 0 ``` Echo server: ``` $ ./bin/h2test Listening [0.0.0.0:5555]... Request: {Method:POST URL:/ Proto:HTTP/2.0 ProtoMajor:2 ProtoMinor:0 Header:map[Accept:[*/*] Content-Length:[9] Content-Type:[application/x-www-form-urlencoded] User-Agent:[curl/7.64.1]] Body:0xc000098120 GetBody:<nil> ContentLength:9 TransferEncoding:[] Close:false Host:localhost:5555 Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr:127.0.0.1:54540 RequestURI:/ TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc0000a0000} Received body: POST BODY ```
I think this was closed without a fix |
I think this was fixed with #52882 ? |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
// ex: https://godoc.org/golang.org/x/net/http2/h2c
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a
I created a server(h2c) as per the above,
java11 code
What did you expect to see?
I tried using the latest version of net/http2/h2c and The problem is the same,
H2c server is expected to work properly in the case of the post method.
thank you very much.
What did you see instead?
This client works correctly if it accesses Java's undertow server, not work if it accesses go's h2c server,
Error message source call chain:
(ServeHTTP>h2cUpgrade>drainClientPreface)
golang.org/x/net/http2/h2c/h2c.go
[return fmt.Errorf("Client never sent: %s", http2.ClientPreface)], but The Get method is correct
Analyze TCP messages and discover that the location of http2.ClientPreface(PRI * HTTP/2.0...) may not conform to the go's rules
The text was updated successfully, but these errors were encountered: