diff --git a/http.go b/http.go index fc324cf6ce74..5b1a576b7a96 100644 --- a/http.go +++ b/http.go @@ -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) @@ -288,44 +289,16 @@ 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 } - } } @@ -333,6 +306,54 @@ func httpMessageParser(s *HttpStream) (bool, bool) { 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 diff --git a/http_test.go b/http_test.go index 80185832e018..33340856b663 100644 --- a/http_test.go +++ b/http_test.go @@ -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") + } +}