Skip to content

Commit

Permalink
Fix StatusLine issue
Browse files Browse the repository at this point in the history
Parsing the status line only parsed the text while the code expects the
full line (as the name of the functions implies).

Fixes #1132
  • Loading branch information
erikdubbelboer committed Oct 24, 2021
1 parent 528dd62 commit 064c277
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 3 deletions.
23 changes: 22 additions & 1 deletion header.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ func (h *ResponseHeader) SetStatusCode(statusCode int) {
}

// StatusLine returns response status line.
//
// Return value looks something like "HTTP/1.1 200 OK\r\n"
//
// The returned value is valid until the response is released,
// either though ReleaseResponse or your request handler returning.
// Do not store references to returned value. Make copies instead.
func (h *ResponseHeader) StatusLine() []byte {
if len(h.statusLine) > 0 {
return h.statusLine
Expand All @@ -146,8 +152,17 @@ func (h *ResponseHeader) StatusLine() []byte {
}

// SetStatusLine sets response status line bytes.
//
// statusLine should be something like "HTTP/1.1 200 OK\r\n"
// The \r\n is optional and will automatically be added.
//
// statusLine is safe to reuse after this method has returned.
func (h *ResponseHeader) SetStatusLine(statusLine []byte) {
h.statusLine = append(h.statusLine[:0], statusLine...)

if !bytes.HasSuffix(h.statusLine, strCRLF) {
h.statusLine = append(h.statusLine, strCRLF...)
}
}

// SetLastModified sets 'Last-Modified' header to the given value.
Expand Down Expand Up @@ -1855,6 +1870,9 @@ func (h *ResponseHeader) parseFirstLine(buf []byte) (int, error) {
}
}

// Store the full statis line for use below.
sLine := b

// parse protocol
n := bytes.IndexByte(b, ' ')
if n < 0 {
Expand All @@ -1880,8 +1898,11 @@ func (h *ResponseHeader) parseFirstLine(buf []byte) (int, error) {
}
return 0, fmt.Errorf("unexpected char at the end of status code. Response %q", buf)
}

// Only call SetStatusLine after we did all error checks to prevent
// a broken ResponseHeader.
if len(b) > n+1 && !bytes.Equal(b[n+1:], statusLine(h.statusCode)) {
h.SetStatusLine(b[n+1:])
h.SetStatusLine(sLine)
}

return len(buf) - len(bNext), nil
Expand Down
4 changes: 2 additions & 2 deletions header_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func TestResponseHeaderMultiLineValue(t *testing.T) {
t.Fatalf("parse response using net/http failed, %s", err)
}

if !bytes.Equal(header.StatusLine(), []byte("SuperOK")) {
if !bytes.Equal(header.StatusLine(), []byte("HTTP/1.1 200 SuperOK\r\n")) {
t.Errorf("parse status line with non-default value failed, got: %s want: SuperOK", header.StatusLine())
}

Expand Down Expand Up @@ -83,7 +83,7 @@ func TestResponseHeaderMultiLineName(t *testing.T) {
t.Errorf("expected error, got %q (%v)", m, err)
}

if !bytes.Equal(header.StatusLine(), []byte("OK")) {
if !bytes.Equal(header.StatusLine(), []byte("HTTP/1.1 200 OK\r\n")) {
t.Errorf("expected default status line, got: %s", header.StatusLine())
}
}
Expand Down
35 changes: 35 additions & 0 deletions http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,41 @@ import (
"github.com/valyala/bytebufferpool"
)

func TestIssue1132(t *testing.T) {
t.Parallel()

for _, c := range []struct {
body string
expected string
}{
{
body: "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo",
expected: "HTTP/1.1 200 OK\r\n",
},
{
body: "HTTP/1.1 204 NC\r\n\r\n",
expected: "HTTP/1.1 204 NC\r\n",
},
} {
t.Run(c.expected, func(t *testing.T) {
var r Response

br := bufio.NewReader(bytes.NewBufferString(c.body))
err := r.Read(br)
if err != nil {
t.Fatal(err)
}

got := r.Header.StatusLine()
expected := []byte(c.expected)

if !bytes.Equal(got, expected) {
t.Fatalf("got %q expected %q", got, expected)
}
})
}
}

func TestResponseEmptyTransferEncoding(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 064c277

Please sign in to comment.