Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions http2/http2.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,19 @@ import (
)

var (
VerboseLogs bool
logFrameWrites bool
logFrameReads bool
inTests bool
disableExtendedConnectProtocol bool
VerboseLogs bool
logFrameWrites bool
logFrameReads bool
inTests bool

// Enabling extended CONNECT by causes browsers to attempt to use
// WebSockets-over-HTTP/2. This results in problems when the server's websocket
// package doesn't support extended CONNECT.
//
// Disable extended CONNECT by default for now.
//
// Issue #71128.
disableExtendedConnectProtocol = true
)

func init() {
Expand All @@ -50,8 +58,8 @@ func init() {
logFrameWrites = true
logFrameReads = true
}
if strings.Contains(e, "http2xconnect=0") {
disableExtendedConnectProtocol = true
if strings.Contains(e, "http2xconnect=1") {
disableExtendedConnectProtocol = false
}
}

Expand Down
5 changes: 5 additions & 0 deletions http2/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1993,6 +1993,11 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
protocol: f.PseudoValue("protocol"),
}

// extended connect is disabled, so we should not see :protocol
if disableExtendedConnectProtocol && rp.protocol != "" {
return nil, nil, streamError(f.StreamID, ErrCodeProtocol)
}

isConnect := rp.method == "CONNECT"
if isConnect {
if rp.protocol == "" && (rp.path != "" || rp.scheme != "" || rp.authority == "") {
Expand Down
64 changes: 64 additions & 0 deletions http2/server_extended_connect_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package http2

import (
"os"
"os/exec"
"testing"
"time"

http "github.com/bogdanfinn/fhttp"
)

// Extended CONNECT is off unless GODEBUG=http2xconnect=1 at startup
// (golang/go#71128). GODEBUG is read at init, so each setting runs in a new
// process.
func TestServerExtendedConnect(t *testing.T) {
if want := os.Getenv("FHTTP_TEST_XCONNECT"); want != "" {
testServerExtendedConnect(t, want == "on")
return
}
for _, tt := range []struct{ godebug, want string }{
{"", "off"},
{"http2xconnect=0", "off"},
{"http2xconnect=1", "on"},
} {
cmd := exec.Command(os.Args[0], "-test.run=^TestServerExtendedConnect$")
cmd.Env = append(os.Environ(), "FHTTP_TEST_XCONNECT="+tt.want, "GODEBUG="+tt.godebug)
if out, err := cmd.CombinedOutput(); err != nil {
t.Errorf("GODEBUG=%q: %v\n%s", tt.godebug, err, out)
}
}
}

// testServerExtendedConnect checks that the server advertises extended CONNECT
// and accepts requests with :protocol exactly when it is enabled.
func testServerExtendedConnect(t *testing.T, enabled bool) {
gotProtocol := make(chan string, 1)
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
gotProtocol <- r.Header.Get(":protocol")
})
defer st.Close()

advertised := false
st.greetAndCheckSettings(func(s Setting) error {
advertised = advertised || s.ID == SettingEnableConnectProtocol && s.Val == 1
return nil
})
if advertised != enabled {
t.Fatalf("SETTINGS_ENABLE_CONNECT_PROTOCOL advertised = %v, want %v", advertised, enabled)
}

st.bodylessReq1(":method", "CONNECT", ":protocol", "websocket")
if !enabled {
st.wantRSTStream(1, ErrCodeProtocol)
return
}
select {
case p := <-gotProtocol:
if p != "websocket" {
t.Fatalf(":protocol = %q, want %q", p, "websocket")
}
case <-time.After(5 * time.Second):
t.Fatal("handler was not called for an extended CONNECT request")
}
}
Loading