diff --git a/gunicorn/http/errors.py b/gunicorn/http/errors.py index 6f9ab3a6..879d430b 100644 --- a/gunicorn/http/errors.py +++ b/gunicorn/http/errors.py @@ -35,18 +35,19 @@ class InvalidHTTPVersion(ParseException): return "Invalid HTTP Version: %s" % self.version class InvalidHeader(ParseException): - def __init__(self, hdr): + def __init__(self, hdr, req=None): self.hdr = hdr + self.req = req def __str__(self): - return "Invalid HTTP Header: %r" % self.hdr + return "Invalid HTTP Header: %s" % self.hdr class InvalidHeaderName(ParseException): def __init__(self, hdr): self.hdr = hdr def __str__(self): - return "Invalid HTTP header name: %r" % self.hdr + return "Invalid HTTP header name: %s" % self.hdr class InvalidChunkSize(ParseException): def __init__(self, data): diff --git a/gunicorn/http/message.py b/gunicorn/http/message.py index 8bc4b4cd..4bd554c7 100644 --- a/gunicorn/http/message.py +++ b/gunicorn/http/message.py @@ -94,25 +94,27 @@ class Message(object): def set_body_reader(self): chunked = False - response_length = None + content_length = None for (name, value) in self.headers: if name == "CONTENT-LENGTH": - try: - response_length = int(value) - except ValueError: - response_length = None + content_length = value elif name == "TRANSFER-ENCODING": chunked = value.lower() == "chunked" elif name == "SEC-WEBSOCKET-KEY1": - response_length = 8 - - if response_length is not None or chunked: - break + content_length = 8 if chunked: self.body = Body(ChunkedReader(self, self.unreader)) - elif response_length is not None: - self.body = Body(LengthReader(self.unreader, response_length)) + elif content_length is not None: + try: + content_length = int(content_length) + except ValueError: + raise InvalidHeader("CONTENT-LENGTH", req=self) + + if content_length < 0: + raise InvalidHeader("CONTENT-LENGTH", req=self) + + self.body = Body(LengthReader(self.unreader, content_length)) else: self.body = Body(EOFReader(self.unreader)) diff --git a/gunicorn/workers/base.py b/gunicorn/workers/base.py index 7e8cee07..2392e8dd 100644 --- a/gunicorn/workers/base.py +++ b/gunicorn/workers/base.py @@ -142,7 +142,9 @@ class Worker(object): elif isinstance(exc, InvalidHTTPVersion): mesg = "
Invalid HTTP Version '%s'
" % str(exc) elif isinstance(exc, (InvalidHeaderName, InvalidHeader,)): - mesg = "Invalid Header '%s'
" % str(exc) + mesg = "%s
" % str(exc) + if not req and hasattr(exc, "req"): + req = exc.req # for access log elif isinstance(exc, LimitRequestLine): mesg = "%s
" % str(exc) elif isinstance(exc, LimitRequestHeaders): diff --git a/tests/requests/invalid/014.http b/tests/requests/invalid/014.http new file mode 100644 index 00000000..a5356704 --- /dev/null +++ b/tests/requests/invalid/014.http @@ -0,0 +1,4 @@ +PUT /stuff/here?foo=bar HTTP/1.0\r\n +CONTENT-LENGTH: -1\r\n +\r\n +{"test": "-1} \ No newline at end of file diff --git a/tests/requests/invalid/014.py b/tests/requests/invalid/014.py new file mode 100644 index 00000000..4dcf8254 --- /dev/null +++ b/tests/requests/invalid/014.py @@ -0,0 +1,3 @@ +from gunicorn.http.errors import InvalidHeader + +request = InvalidHeader diff --git a/tests/requests/invalid/015.http b/tests/requests/invalid/015.http new file mode 100644 index 00000000..7d774cf6 --- /dev/null +++ b/tests/requests/invalid/015.http @@ -0,0 +1,4 @@ +POST /stuff/here?foo=bar HTTP/1.0\r\n +CONTENT-LENGTH: bla-bla-bla\r\n +\r\n +{"test": "-1} \ No newline at end of file diff --git a/tests/requests/invalid/015.py b/tests/requests/invalid/015.py new file mode 100644 index 00000000..4dcf8254 --- /dev/null +++ b/tests/requests/invalid/015.py @@ -0,0 +1,3 @@ +from gunicorn.http.errors import InvalidHeader + +request = InvalidHeader diff --git a/tests/requests/valid/025.http b/tests/requests/valid/025.http new file mode 100644 index 00000000..62267add --- /dev/null +++ b/tests/requests/valid/025.http @@ -0,0 +1,19 @@ +POST /chunked_cont_h_at_first HTTP/1.1\r\n +Content-Length: -1\r\n +Transfer-Encoding: chunked\r\n +\r\n +5; some; parameters=stuff\r\n +hello\r\n +6; blahblah; blah\r\n + world\r\n +0\r\n +\r\n +PUT /chunked_cont_h_at_last HTTP/1.1\r\n +Transfer-Encoding: chunked\r\n +Content-Length: -1\r\n +\r\n +5; some; parameters=stuff\r\n +hello\r\n +6; blahblah; blah\r\n + world\r\n +0\r\n \ No newline at end of file diff --git a/tests/requests/valid/025.py b/tests/requests/valid/025.py new file mode 100644 index 00000000..7e8f1826 --- /dev/null +++ b/tests/requests/valid/025.py @@ -0,0 +1,23 @@ +req1 = { + "method": "POST", + "uri": uri("/chunked_cont_h_at_first"), + "version": (1, 1), + "headers": [ + ("CONTENT-LENGTH", "-1"), + ("TRANSFER-ENCODING", "chunked") + ], + "body": "hello world" +} + +req2 = { + "method": "PUT", + "uri": uri("/chunked_cont_h_at_last"), + "version": (1, 1), + "headers": [ + ("TRANSFER-ENCODING", "chunked"), + ("CONTENT-LENGTH", "-1"), + ], + "body": "hello world" +} + +request = [req1, req2]