Skip to content

Commit

Permalink
Fix edge case were client responds with invalid header
Browse files Browse the repository at this point in the history
  • Loading branch information
becheran committed Mar 3, 2022
1 parent 76a66e1 commit 6d164c0
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 1 deletion.
2 changes: 1 addition & 1 deletion client.go
Expand Up @@ -1454,7 +1454,7 @@ func (c *HostClient) doNonNilReqResp(req *Request, resp *Response) (bool, error)
br := c.acquireReader(conn)
err = resp.ReadLimitBody(br, c.MaxResponseBodySize)
c.releaseReader(br)
if err != nil && !errors.Is(err, syscall.ECONNRESET) {
if err != nil {
c.closeConn(cc)
// Don't retry in case of ErrBodyTooLarge since we will just get the same again.
retry := err != ErrBodyTooLarge
Expand Down
53 changes: 53 additions & 0 deletions client_test.go
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"bytes"
"crypto/tls"
"errors"
"fmt"
"io"
"io/ioutil"
Expand All @@ -16,6 +17,7 @@ import (
"strings"
"sync"
"sync/atomic"
"syscall"
"testing"
"time"

Expand Down Expand Up @@ -2875,3 +2877,54 @@ func TestRstConnResponseWhileSending(t *testing.T) {
t.Fatalf("Expected %d status code, but got %d", expectedStatus, resp.StatusCode())
}
}

// See issue #1232
func TestRstConnClosedWithoutResponse(t *testing.T) {
const expectedStatus = http.StatusTeapot
const payload = "payload"

srv, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err.Error())
}

go func() {
conn, err := srv.Accept()
if err != nil {
t.Errorf(err.Error())
}

// Read at least one byte of the header
// Otherwise we would have an unsolicited response
ioutil.ReadAll(io.LimitReader(conn, 1))

// Respond with incomplete header
conn.Write([]byte("Http"))

// Forcefully close connection
err = conn.(*net.TCPConn).SetLinger(0)
if err != nil {
t.Errorf(err.Error())
}
conn.Close()
}()

svrUrl := "http://" + srv.Addr().String()

client := HostClient{Addr: srv.Addr().String()}

req := AcquireRequest()
defer ReleaseRequest(req)
resp := AcquireResponse()
defer ReleaseResponse(resp)

req.Header.SetMethod("POST")
req.SetBodyStream(strings.NewReader(payload), len(payload))
req.SetRequestURI(svrUrl)

err = client.Do(req, resp)

if !errors.Is(err, syscall.ECONNRESET) {
t.Fatalf("Expected connection reset error")
}
}
7 changes: 7 additions & 0 deletions http.go
Expand Up @@ -12,6 +12,7 @@ import (
"net"
"os"
"sync"
"syscall"
"time"

"github.com/valyala/bytebufferpool"
Expand Down Expand Up @@ -1272,13 +1273,19 @@ func (resp *Response) ReadLimitBody(r *bufio.Reader, maxBodySize int) error {

if !resp.mustSkipBody() {
err = resp.ReadBody(r, maxBodySize)
if errors.Is(err, syscall.ECONNRESET) {
return nil
}
if err != nil {
return err
}
}

if resp.Header.ContentLength() == -1 {
err = resp.Header.ReadTrailer(r)
if errors.Is(err, syscall.ECONNRESET) {
return nil
}
if err != nil && err != io.EOF {
return err
}
Expand Down

0 comments on commit 6d164c0

Please sign in to comment.