Skip to content

Commit

Permalink
Fix for the http parse stack trace from #4
Browse files Browse the repository at this point in the history
There was a bug in the chunked encoding handling
when the segment boundary is between \r and \n. This
adds a test to show the issue and the code to fix it.
  • Loading branch information
monicasarbu committed May 11, 2014
1 parent e34f1b2 commit 110bdb0
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 34 deletions.
89 changes: 55 additions & 34 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ func httpParseHeader(m *HttpMessage, data []byte) (bool, bool, int) {

func httpMessageParser(s *HttpStream) (bool, bool) {

var cont, ok, complete bool
m := s.message

//DEBUG("http", "Data: [%s]", s.data)
Expand Down Expand Up @@ -288,51 +289,71 @@ func httpMessageParser(s *HttpStream) (bool, bool) {
}

case BODY_CHUNKED_START:
// read hexa length
i := bytes.Index(s.data[s.parseOffset:], []byte("\r\n"))
if i == -1 {
return true, false
}
line := string(s.data[s.parseOffset : s.parseOffset+i])
_, err := fmt.Sscanf(line, "%x", &m.chunked_length)
if err != nil {
WARN("Failed to understand chunked body start line")
return false, false
cont, ok, complete = state_body_chunked_start(s, m)
if !cont {
return ok, complete
}

s.parseOffset += i + 2 //+ \r\n
if m.chunked_length == 0 {
s.parseOffset += 2 // final \r\n
m.end = s.parseOffset
return true, true
}
s.bodyReceived = 0
s.parseState = BODY_CHUNKED

case BODY_CHUNKED:
// skip chunked data
if len(s.data[s.parseOffset:]) >= m.chunked_length-s.bodyReceived+2 /*\r\n*/ {
// Received more data than expected
m.chunked_body = append(m.chunked_body, s.data[s.parseOffset:s.parseOffset+m.chunked_length-s.bodyReceived]...)
s.parseOffset += (m.chunked_length - s.bodyReceived + 2 /*\r\n*/)
m.ContentLength += m.chunked_length
s.parseState = BODY_CHUNKED_START
continue
} else {
// Received less data than expected
m.chunked_body = append(m.chunked_body, s.data[s.parseOffset:]...)
s.bodyReceived += (len(s.data) - s.parseOffset)
s.parseOffset = len(s.data)
return true, false
cont, ok, complete = state_body_chunked(s, m)
if !cont {
return ok, complete
}

}

}

return true, false
}

func state_body_chunked_start(s *HttpStream, m *HttpMessage) (cont bool, ok bool, complete bool) {
// read hexa length
i := bytes.Index(s.data[s.parseOffset:], []byte("\r\n"))
if i == -1 {
return false, true, false
}
line := string(s.data[s.parseOffset : s.parseOffset+i])
_, err := fmt.Sscanf(line, "%x", &m.chunked_length)
if err != nil {
WARN("Failed to understand chunked body start line")
return false, false, false
}

s.parseOffset += i + 2 //+ \r\n
if m.chunked_length == 0 {
s.parseOffset += 2 // final \r\n
m.end = s.parseOffset
return false, true, true
}
s.bodyReceived = 0
s.parseState = BODY_CHUNKED

return true, false, false
}


func state_body_chunked(s *HttpStream, m *HttpMessage) (cont bool, ok bool, complete bool) {
if len(s.data[s.parseOffset:]) >= m.chunked_length-s.bodyReceived+2 /*\r\n*/ {
// Received more data than expected
m.chunked_body = append(m.chunked_body, s.data[s.parseOffset:s.parseOffset+m.chunked_length-s.bodyReceived]...)
s.parseOffset += (m.chunked_length - s.bodyReceived + 2 /*\r\n*/)
m.ContentLength += m.chunked_length
s.parseState = BODY_CHUNKED_START
return true, false, false
} else {
if len(s.data[s.parseOffset:]) >= m.chunked_length-s.bodyReceived {
// we need need to wait for the +2, else we can crash on next call
return false, true, false
}
// Received less data than expected
m.chunked_body = append(m.chunked_body, s.data[s.parseOffset:]...)
s.bodyReceived += (len(s.data) - s.parseOffset)
s.parseOffset = len(s.data)
return false, true, false
}
return true, false, false
}

func (stream *HttpStream) PrepareForNewMessage() {
stream.data = stream.data[stream.message.end:]
stream.parseState = START
Expand Down
100 changes: 100 additions & 0 deletions http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,3 +540,103 @@ func TestHttpParser_301_response(t *testing.T) {
t.Error("Expecting content length 290")
}
}

func TestEatBodyChunked(t *testing.T) {
msgs := [][]byte{
[]byte("03\r"),
[]byte("\n123\r\n03\r\n123\r"),
[]byte("\n0\r\n"),
}
stream := &HttpStream{
data: msgs[0],
parseOffset: 0,
bodyReceived: 0,
parseState: BODY_CHUNKED_START,
}
message := &HttpMessage{
chunked_length: 5,
ContentLength: 0,
}

cont, ok, complete := state_body_chunked_start(stream, message)
if cont != false || ok != true || complete != false {
t.Error("Wrong return values")
}
if stream.parseOffset != 0 {
t.Error("Wrong parseOffset")
}

stream.data = append(stream.data, msgs[1]...)

cont, ok, complete = state_body_chunked_start(stream, message)
if cont != true {
t.Error("Wrong return values")
}
if message.chunked_length != 3 {
t.Error("Wrong chunked_length")
}
if stream.parseOffset != 4 {
t.Error("Wrong parseOffset")
}
if stream.parseState != BODY_CHUNKED {
t.Error("Wrong state")
}

cont, ok, complete = state_body_chunked(stream, message)
if cont != true {
t.Error("Wrong return values")
}
if stream.parseState != BODY_CHUNKED_START {
t.Error("Wrong state")
}
if stream.parseOffset != 9 {
t.Error("Wrong parseOffset")
}


cont, ok, complete = state_body_chunked_start(stream, message)
if cont != true {
t.Error("Wrong return values")
}
if message.chunked_length != 3 {
t.Error("Wrong chunked_length")
}
if stream.parseOffset != 13 {
t.Error("Wrong parseOffset")
}
if stream.parseState != BODY_CHUNKED {
t.Error("Wrong state")
}


cont, ok, complete = state_body_chunked(stream, message)
if cont != false || ok != true || complete != false {
t.Error("Wrong return values")
}
if stream.parseState != BODY_CHUNKED {
t.Error("Wrong state")
}
if stream.parseOffset != 13 {
t.Error("Wrong parseOffset")
}
if stream.bodyReceived != 0 {
t.Error("Wrong bodyReceived")
}

stream.data = append(stream.data, msgs[2]...)
cont, ok, complete = state_body_chunked(stream, message)
if cont != true {
t.Error("Wrong return values")
}
if stream.parseState != BODY_CHUNKED_START {
t.Error("Wrong state")
}
if stream.parseOffset != 18 {
t.Error("Wrong parseOffset")
}

cont, ok, complete = state_body_chunked_start(stream, message)
if cont != false || ok != true || complete != true {
t.Error("Wrong return values")
}
}

0 comments on commit 110bdb0

Please sign in to comment.