-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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: bogus greeting when providing TLSClientConfig #21336
Comments
@jbowens, to avoid using http2, the documentation at https://golang.org/pkg/net/http says config.NextProtos = []string{"h1"} and that should use HTTP/1. Does this change your bug report? |
@odeke-em thanks, but I don't think it does. I can avoid this error by explicitly configuring the protocols (either including http2 or not). I was more reporting the fact that this configuration causes the client to negotiate http/2 but then request using http1. |
Aye aye, thanks for letting me know. I'll call in the big guns /cc @bradfitz @tombergan. |
The behavior is definitely inconsistent. https://golang.org/pkg/net/http/#Transport // TLSClientConfig specifies the TLS configuration to use with
// tls.Client.
// If nil, the default configuration is used.
// If non-nil, HTTP/2 support may not be enabled by default.
TLSClientConfig *tls.Config Note "may not be enabled". As you point out, the code does not enable HTTP/2 when TLCClientConfig is specified.
I'm guessing the H2 server was written that way to allow callers to specify the cert, while it's less common for H2 clients to provide a cert. I think the Transport should behave like the server, to allow clients to provide certs where needed. |
To add a little bit of context here: this is caused by the same When the server starts serving, it correctly detects that it can indeed serve HTTP2. Because it has detected that it can serve HTTP2, it appends
Note that it first clones the Therefore, when the server decides that it can serve The two protocol levels in the client disagree here -- TLS has been negotiated with HTTP2 being the next protocol, but the |
Happy New Year @tombergan! How's it going here? |
There's also a data race, since the goroutine running ListenAndServeTLS is also initializing the * tls.Config.NextProtos concurrently with the main goroutine's http Client reading it. You can observe that by adding the lines: time.Sleep(500 * time.Millisecond)
log.Printf("TLS config NextProto = %q", config.NextProtos) ... to the main.go code above, adding it after the goroutine, before the client initialization. Comment-out the 500ms sleep and you'll need NextProtos sometimes be empty. Running with -race shows:
To fix this all, I think we'd need to stop mutating the caller's provided *Server, at least via its public fields. We might need to add an addition private field (likely guarded by the existing private mutex) and have the http2.ConfigureServer set that instead, to a clone of the exported config, if any. But since that's in a different package, we'd need an exported setter method? (Gross.) Or we'd need to do some trickery that only works in the bundled x/net/http2-in-std version, which is probably what the answer will be. Then whenever the net/http Server needs its config, it would get it via a private accessor method that prefers the unexported TLS config field over the exported one. Unrelated, we could also be smarter in the HTTP/1 Transport to check its TLS connection's state and if it had negotiated ALPN "h2", either fail early with something useful ("you configured it wrong.") or go out of its way to actually speak HTTP/2, if that isn't too much work. In any case, there's too much to do here for Go 1.10, which is due out soon. Sorry. At least there are plenty of workarounds. |
/cc @FiloSottile |
Is there a temporary fix for this on the client side? |
What version of Go are you using (
go version
)?go version go1.9beta2 darwin/amd64
What operating system and processor architecture are you using (
go env
)?What did you do?
Download https://gist.github.com/jbowens/3e1f7443d6cb1bf8071f4aea2397c4b1
go1.9beta2 run main.go
What did you expect to see?
The program should exit without panicking.
What did you see instead?
From my understanding, the presence of TLSClientConfig should cause the client to not use http2, but it appears to negotiate to http2 anyways.
The text was updated successfully, but these errors were encountered: