Skip to content

Commit

Permalink
Fix #2311: Accept partial status line
Browse files Browse the repository at this point in the history
  • Loading branch information
asvetlov committed Oct 15, 2017
1 parent fc8fb04 commit 0c2c5c6
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 10 deletions.
44 changes: 34 additions & 10 deletions aiohttp/_http_parser.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ cdef class HttpParser:

bint _started
object _url
bytearray _buf
str _path
str _reason
list _headers
Expand Down Expand Up @@ -92,6 +93,7 @@ cdef class HttpParser:
self._loop = loop
self._timer = timer

self._buf = bytearray()
self._payload = None
self._payload_error = 0
self._payload_exception = payload_exception
Expand Down Expand Up @@ -223,6 +225,9 @@ cdef class HttpParser:
cdef _on_chunk_complete(self):
self._payload.end_http_chunk_receiving()

cdef object _on_status_complete(self):
pass

### Public API ###

def http_version(self):
Expand Down Expand Up @@ -311,6 +316,22 @@ cdef class HttpRequestParserC(HttpParser):
max_line_size, max_headers, max_field_size,
payload_exception, response_with_body)

cdef object _on_status_complete(self):
cdef Py_buffer py_buf
if not self._buf:
return
self._path = self._buf.decode('utf-8', 'surrogateescape')
if self._cparser.method == 5: # CONNECT
self._url = URL(self._path)
else:
PyObject_GetBuffer(self._buf, &py_buf, PyBUF_SIMPLE)
try:
self._url = _parse_url(<char*>py_buf.buf,
py_buf.len)
finally:
PyBuffer_Release(&py_buf)
self._buf.clear()


cdef class HttpResponseParserC(HttpParser):

Expand All @@ -323,32 +344,32 @@ cdef class HttpResponseParserC(HttpParser):
max_line_size, max_headers, max_field_size,
payload_exception, response_with_body, auto_decompress)

cdef object _on_status_complete(self):
if self._buf:
self._reason = self._buf.decode('utf-8', 'surrogateescape')
self._buf.clear()


cdef int cb_on_message_begin(cparser.http_parser* parser) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data

pyparser._started = True
pyparser._headers = []
pyparser._raw_headers = []
pyparser._buf.clear()
pyparser._path = None
pyparser._reason = None
return 0


cdef int cb_on_url(cparser.http_parser* parser,
const char *at, size_t length) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data
cdef str path = None
try:
if length > pyparser._max_line_size:
raise LineTooLong(
'Status line is too long', pyparser._max_line_size)

path = at[:length].decode('utf-8', 'surrogateescape')
pyparser._path = path

if pyparser._cparser.method == 5: # CONNECT
pyparser._url = URL(path)
else:
pyparser._url = _parse_url(at[:length], length)
pyparser._buf.extend(at[:length])
except BaseException as ex:
pyparser._last_error = ex
return -1
Expand All @@ -359,11 +380,12 @@ cdef int cb_on_url(cparser.http_parser* parser,
cdef int cb_on_status(cparser.http_parser* parser,
const char *at, size_t length) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data
cdef str reason
try:
if length > pyparser._max_line_size:
raise LineTooLong(
'Status line is too long', pyparser._max_line_size)
pyparser._reason = at[:length].decode('utf-8', 'surrogateescape')
pyparser._buf.extend(at[:length])
except BaseException as ex:
pyparser._last_error = ex
return -1
Expand All @@ -375,6 +397,7 @@ cdef int cb_on_header_field(cparser.http_parser* parser,
const char *at, size_t length) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data
try:
pyparser._on_status_complete()
if length > pyparser._max_field_size:
raise LineTooLong(
'Header name is too long', pyparser._max_field_size)
Expand Down Expand Up @@ -410,6 +433,7 @@ cdef int cb_on_header_value(cparser.http_parser* parser,
cdef int cb_on_headers_complete(cparser.http_parser* parser) except -1:
cdef HttpParser pyparser = <HttpParser>parser.data
try:
pyparser._on_status_complete()
pyparser._on_headers_complete()
except BaseException as exc:
pyparser._last_error = exc
Expand Down
2 changes: 2 additions & 0 deletions changes/2311.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix C HTTP parser for cases when status line is split into different
TCP packets.
14 changes: 14 additions & 0 deletions tests/test_http_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,20 @@ def test_parse_no_length_payload(parser):
assert payload.is_eof()


def test_partial_url(parser):
messages, upgrade, tail = parser.feed_data(b'GET /te')
assert len(messages) == 0
messages, upgrade, tail = parser.feed_data(b'st HTTP/1.1\r\n\r\n')
assert len(messages) == 1

msg, payload = messages[0]

assert msg.method == 'GET'
assert msg.path == '/test'
assert msg.version == (1, 1)
assert payload.is_eof()


class TestParsePayload(unittest.TestCase):

def setUp(self):
Expand Down

0 comments on commit 0c2c5c6

Please sign in to comment.