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

fix enable http/2 by default as intended by flags #2908

Merged
merged 4 commits into from
Jul 31, 2024
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
30 changes: 15 additions & 15 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"errors"
"fmt"
"io"
"net"
"net/http"
"net/http/httputil"
"net/url"
Expand Down Expand Up @@ -48,6 +47,9 @@ type Config struct {

// The http client used, leave nil for the default
HTTPClient *http.Client

// optional TLS configuration primarily used for testing
TLSConfig *tls.Config
}

// A Client manages communication with the Buildkite Agent API.
Expand All @@ -74,28 +76,26 @@ func NewClient(l logger.Logger, conf Config) *Client {

httpClient := conf.HTTPClient
if conf.HTTPClient == nil {
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DisableCompression: false,
DisableKeepAlives: false,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 30 * time.Second,
}

// use the default transport as it is optimized and configured for http2
// and will avoid accidents in the future
tr := http.DefaultTransport.(*http.Transport).Clone()
Copy link
Contributor

@DrJosh9000 DrJosh9000 Jul 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making a note in case this is important later on - no changes needed.

The diff from the old transport to the default is:

- 	TLSHandshakeTimeout:   30 * time.Second,
+ 	ForceAttemptHTTP2:     true,
+	TLSHandshakeTimeout:   10 * time.Second,
+	ExpectContinueTimeout: 1 * time.Second,

and the use of defaultTransportDialContext wrapping net.Dialer, but that is equivalent to .DialContext.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good callout, thanks!


if conf.DisableHTTP2 {
t.TLSNextProto = make(map[string]func(string, *tls.Conn) http.RoundTripper)
tr.ForceAttemptHTTP2 = false
tr.TLSNextProto = make(map[string]func(authority string, c *tls.Conn) http.RoundTripper)
// The default TLSClientConfig has h2 in NextProtos, so the negotiated TLS connection will assume h2 support.
// see https://github.com/golang/go/issues/50571
tr.TLSClientConfig.NextProtos = []string{"http/1.1"}
}

tr.TLSClientConfig = conf.TLSConfig

httpClient = &http.Client{
Timeout: 60 * time.Second,
Transport: &authenticatedTransport{
Token: conf.Token,
Delegate: t,
Delegate: tr,
},
}
}
Expand Down
18 changes: 14 additions & 4 deletions api/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api_test

import (
"context"
"crypto/tls"
"fmt"
"net/http"
"net/http/httptest"
Expand All @@ -13,7 +14,7 @@ import (
)

func TestRegisteringAndConnectingClient(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
server := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
case "/register":
if got, want := authToken(req), "llamas"; got != want {
Expand All @@ -37,16 +38,21 @@ func TestRegisteringAndConnectingClient(t *testing.T) {
}))
defer server.Close()

// enable HTTP/2.0 to ensure the client can handle defaults to using it
server.EnableHTTP2 = true
server.StartTLS()

ctx := context.Background()

// Initial client with a registration token
c := api.NewClient(logger.Discard, api.Config{
Endpoint: server.URL,
Token: "llamas",
Endpoint: server.URL,
Token: "llamas",
TLSConfig: &tls.Config{InsecureSkipVerify: true},
})

// Check a register works
regResp, _, err := c.Register(ctx, &api.AgentRegisterRequest{})
regResp, httpResp, err := c.Register(ctx, &api.AgentRegisterRequest{})
if err != nil {
t.Fatalf("c.Register(&AgentRegisterRequest{}) error = %v", err)
}
Expand All @@ -59,6 +65,10 @@ func TestRegisteringAndConnectingClient(t *testing.T) {
t.Errorf("regResp.AccessToken = %q, want %q", got, want)
}

if got, want := httpResp.Proto, "HTTP/2.0"; got != want {
t.Errorf("httpResp.Proto = %q, want %q", got, want)
}

// New client with the access token
c2 := c.FromAgentRegisterResponse(regResp)

Expand Down