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 RawHeaders for POST #29

Merged
merged 3 commits into from
Mar 3, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 9 additions & 2 deletions header.go
Original file line number Diff line number Diff line change
Expand Up @@ -1620,6 +1620,7 @@ func (h *RequestHeader) parse(buf []byte) (int, error) {
if err != nil {
return 0, err
}
h.rawHeaders = buf[m : m+n]
Copy link
Owner

Choose a reason for hiding this comment

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

I don't think this is going to work. parse is called from tryRead where it's buffer argument comes from mustPeekBuffered which as you can see gets its return value from bufio.Reader.Peek which will change its return value after a next read (for example for the body) making rawHeaders contain invalid data.

I haven't tested this myself but I see your tests don't cover this case. Can you add a test here a whole POST request including a body of at least 4kb is parsed instead of just the RequestHeader.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks! I have just followed the same trail literally minutes ago, will add a test shortly.

h.rawHeadersParsed = true
} else {
var rawHeaders []byte
Expand Down Expand Up @@ -1870,8 +1871,7 @@ func (h *RequestHeader) parseHeaders(buf []byte) (int, error) {
v := peekArgBytes(h.h, strConnection)
h.connectionClose = !hasHeaderValue(v, strKeepAlive) && !hasHeaderValue(v, strKeepAliveCamelCase)
}

return len(buf) - len(s.b), nil
return s.hLen, nil
}

func (h *RequestHeader) parseRawHeaders() {
Expand Down Expand Up @@ -1922,17 +1922,22 @@ type headerScanner struct {
value []byte
err error

// hLen stores header subslice len
hLen int

disableNormalizing bool
}

func (s *headerScanner) next() bool {
bLen := len(s.b)
if bLen >= 2 && s.b[0] == '\r' && s.b[1] == '\n' {
s.b = s.b[2:]
s.hLen += 2
return false
}
if bLen >= 1 && s.b[0] == '\n' {
s.b = s.b[1:]
s.hLen++
return false
}
n := bytes.IndexByte(s.b, ':')
Expand All @@ -1946,13 +1951,15 @@ func (s *headerScanner) next() bool {
for len(s.b) > n && s.b[n] == ' ' {
n++
}
s.hLen += n
s.b = s.b[n:]
n = bytes.IndexByte(s.b, '\n')
if n < 0 {
s.err = errNeedMore
return false
}
s.value = s.b[:n]
s.hLen += n + 1
s.b = s.b[n+1:]

if n > 0 && s.value[n-1] == '\r' {
Expand Down
60 changes: 46 additions & 14 deletions header_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,12 @@ func TestRequestHeaderEmptyValueFromString(t *testing.T) {
}

func TestRequestRawHeaders(t *testing.T) {
kvs := "EmptyValue:\r\n" +
"host: foobar\r\n" +
kvs := "host: foobar\r\n" +
"value: b\r\n" +
"\r\n"
t.Run("normalized", func(t *testing.T) {
s := "GET / HTTP/1.1\r\n" + kvs
exp := "Emptyvalue:\r\n" +
"Host: foobar\r\n" +
exp := "Host: foobar\r\n" +
"Value: b\r\n" +
"\r\n"
var h RequestHeader
Expand All @@ -129,16 +127,54 @@ func TestRequestRawHeaders(t *testing.T) {
if string(h.Host()) != "foobar" {
t.Fatalf("unexpected host: %q. Expecting %q", h.Host(), "foobar")
}
v1 := h.Peek("EmptyValue")
if len(v1) > 0 {
t.Fatalf("expecting empty value. Got %q", v1)
Copy link

Choose a reason for hiding this comment

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

Why the need to remove EmptyValue testing?

Copy link
Author

Choose a reason for hiding this comment

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

EmptyValue check is not the purpose if this Test. It has been copied alongside other checks (by me, in #27) from a general headers tests that remains unchanged.

In other words EmptyValue is tested elsewhere, the removal is my attempt to DRY my previous PR.

v2 := h.Peek("Value")
if !bytes.Equal(v2, []byte{'b'}) {
t.Fatalf("expecting non empty value. Got %q", v2)
}
if raw := h.RawHeaders(); string(raw) != exp {
t.Fatalf("expected header %q, got %q", exp, raw)
}
})
t.Run("post", func(t *testing.T) {
s := "POST / HTTP/1.1\r\n" + kvs + "body"
exp := "Host: foobar\r\n" +
"Value: b\r\n" +
"\r\n"
var h RequestHeader
br := bufio.NewReader(bytes.NewBufferString(s))
if err := h.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if string(h.Host()) != "foobar" {
t.Fatalf("unexpected host: %q. Expecting %q", h.Host(), "foobar")
}
v2 := h.Peek("Value")
if !bytes.Equal(v2, []byte{'b'}) {
t.Fatalf("expecting non empty value. Got %q", v2)
}
if raw := h.RawHeaders(); string(raw) != exp {
t.Fatalf("expected header %q, got %q", exp, raw)
}
})
t.Run("http10", func(t *testing.T) {
s := "GET / HTTP/1.0\r\n" + kvs
exp := "Host: foobar\r\n" +
"Value: b\r\n" +
"\r\n"
var h RequestHeader
br := bufio.NewReader(bytes.NewBufferString(s))
if err := h.Read(br); err != nil {
t.Fatalf("unexpected error: %s", err)
}
if string(h.Host()) != "foobar" {
t.Fatalf("unexpected host: %q. Expecting %q", h.Host(), "foobar")
}
v2 := h.Peek("Value")
if !bytes.Equal(v2, []byte{'b'}) {
t.Fatalf("expecting non empty value. Got %q", v2)
}
if raw := h.RawHeaders(); string(raw) != exp {
t.Fatalf("unexpected raw header %q, expected %q instead", kvs, raw)
t.Fatalf("expected header %q, got %q", exp, raw)
}
})
t.Run("non-normalized", func(t *testing.T) {
Expand All @@ -153,16 +189,12 @@ func TestRequestRawHeaders(t *testing.T) {
if string(h.Host()) != "" {
t.Fatalf("unexpected host: %q. Expecting %q", h.Host(), "")
}
v1 := h.Peek("EmptyValue")
if len(v1) > 0 {
t.Fatalf("expecting empty value. Got %q", v1)
}
v2 := h.Peek("value")
if !bytes.Equal(v2, []byte{'b'}) {
t.Fatalf("expecting non empty value. Got %q", v2)
}
if raw := h.RawHeaders(); string(raw) != exp {
t.Fatalf("unexpected raw header %q, expected %q instead", kvs, raw)
t.Fatalf("expected header %q, got %q", exp, raw)
}
})
t.Run("no-kvs", func(t *testing.T) {
Expand All @@ -182,7 +214,7 @@ func TestRequestRawHeaders(t *testing.T) {
t.Fatalf("expecting empty value. Got %q", v1)
}
if raw := h.RawHeaders(); string(raw) != exp {
t.Fatalf("unexpected raw header %q, expected %q instead", kvs, raw)
t.Fatalf("expected header %q, got %q", exp, raw)
}
})
}
Expand Down