From c6a57b2cc129eb0f64d898e8901859f7fd8afeaf Mon Sep 17 00:00:00 2001 From: mmmray <142015632+mmmray@users.noreply.github.com> Date: Mon, 1 Jul 2024 05:30:34 +0200 Subject: [PATCH] Fix connection reuse in splithttp HTTP/1.1 (#3485) --- transport/internet/splithttp/dialer.go | 33 ++++++++++++++++++-------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index cecab58f..1a927a49 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -1,6 +1,7 @@ package splithttp import ( + "bytes" "context" gotls "crypto/tls" "io" @@ -263,6 +264,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me return } + req.ContentLength = int64(chunk.Len()) req.Header = transportConfiguration.GetRequestHeader() if httpClient.isH2 { @@ -280,11 +282,19 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me return } } else { - var err error var uploadConn any - for i := 0; i < 5; i++ { + + // stringify the entire HTTP/1.1 request so it can be + // safely retried. if instead req.Write is called multiple + // times, the body is already drained after the first + // request + requestBytes := new(bytes.Buffer) + common.Must(req.Write(requestBytes)) + + for { uploadConn = httpClient.uploadRawPool.Get() - if uploadConn == nil { + newConnection := uploadConn == nil + if newConnection { uploadConn, err = httpClient.dialUploadConn(context.WithoutCancel(ctx)) if err != nil { errors.LogInfoInner(ctx, err, "failed to connect upload") @@ -293,18 +303,21 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me } } - err = req.Write(uploadConn.(net.Conn)) + _, err = uploadConn.(net.Conn).Write(requestBytes.Bytes()) + + // if the write failed, we try another connection from + // the pool, until the write on a new connection fails. + // failed writes to a pooled connection are normal when + // the connection has been closed in the meantime. if err == nil { break + } else if newConnection { + errors.LogInfoInner(ctx, err, "failed to send upload") + uploadPipeReader.Interrupt() + return } } - if err != nil { - errors.LogInfoInner(ctx, err, "failed to send upload") - uploadPipeReader.Interrupt() - return - } - httpClient.uploadRawPool.Put(uploadConn) } }()