From 65ae820d952004f218069358259e2693df321aaa Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 4 Jan 2018 14:01:49 -0800 Subject: [PATCH] Disable H2 for retryable requests. We keep having issues with closed streams in POST requests. Signed-off-by: David Calavera --- go/porcelain/http/http.go | 25 ++++++++++++++++++- go/porcelain/http/http_test.go | 44 ++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/go/porcelain/http/http.go b/go/porcelain/http/http.go index 0e0c2ef9..6c5393d8 100644 --- a/go/porcelain/http/http.go +++ b/go/porcelain/http/http.go @@ -1,6 +1,8 @@ package http import ( + "crypto/tls" + "net" "net/http" "strconv" "time" @@ -9,6 +11,8 @@ import ( "github.com/go-openapi/runtime" ) +var DefaultTransport = httpTransport() + type RetryableTransport struct { tr runtime.ClientTransport attempts int @@ -35,7 +39,7 @@ func (t *RetryableTransport) Submit(op *runtime.ClientOperation) (interface{}, e transport := client.Transport if transport == nil { - transport = http.DefaultTransport + transport = DefaultTransport } client.Transport = &retryableRoundTripper{ tr: transport, @@ -95,3 +99,22 @@ func delayWithRateLimit(resp *http.Response, cancel <-chan struct{}) bool { return false } } + +func httpTransport() *http.Transport { + protoUpgrade := map[string]func(string, *tls.Conn) http.RoundTripper{ + "ignore-h2": func(string, *tls.Conn) http.RoundTripper { return nil }, + } + + return &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + TLSNextProto: protoUpgrade, + } +} diff --git a/go/porcelain/http/http_test.go b/go/porcelain/http/http_test.go index 70a859d6..4cf7a180 100644 --- a/go/porcelain/http/http_test.go +++ b/go/porcelain/http/http_test.go @@ -25,10 +25,10 @@ func TestRetryableTransport(t *testing.T) { reset := fmt.Sprintf("%d", time.Now().Add(1*time.Second).Unix()) rw.Header().Set("X-RateLimit-Reset", reset) rw.WriteHeader(http.StatusTooManyRequests) - _, _ = rw.Write([]byte("rate limited")) + rw.Write([]byte("rate limited")) } else { rw.WriteHeader(http.StatusOK) - _, _ = rw.Write([]byte("ok")) + rw.Write([]byte("ok")) } })) defer server.Close() @@ -72,7 +72,7 @@ func TestRetryableTransportExceedsMaxAttempts(t *testing.T) { reset := fmt.Sprintf("%d", time.Now().Add(1*time.Second).Unix()) rw.Header().Set("X-RateLimit-Reset", reset) rw.WriteHeader(http.StatusTooManyRequests) - _, _ = rw.Write([]byte("rate limited")) + rw.Write([]byte("rate limited")) })) defer server.Close() @@ -111,7 +111,7 @@ func TestRetryableWithDifferentError(t *testing.T) { attempts++ rw.WriteHeader(http.StatusNotFound) - _, _ = rw.Write([]byte("not found")) + rw.Write([]byte("not found")) })) defer server.Close() @@ -142,3 +142,39 @@ func TestRetryableWithDifferentError(t *testing.T) { require.Error(t, err) require.Equal(t, 1, attempts) } + +func TestRetryableTransport_POST(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusCreated) + rw.Write([]byte("test result")) + })) + defer server.Close() + + rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { + return req.SetBodyParam("test result") + }) + + hu, _ := url.Parse(server.URL) + rt := NewRetryableTransport(httptransport.New(hu.Host, "/", []string{"http"}), 2) + + result, err := rt.Submit(&runtime.ClientOperation{ + ID: "createSite", + Method: "POST", + PathPattern: "/", + Params: rwrtr, + Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + if response.Code() == 201 { + var result string + if err := consumer.Consume(response.Body(), &result); err != nil { + return nil, err + } + return result, nil + } + return nil, errors.New("Generic error") + }), + }) + + require.NoError(t, err) + actual := result.(string) + require.EqualValues(t, "test result", actual) +}