From f74324bd750265a3c1f47b4c837f6fd7ce74db54 Mon Sep 17 00:00:00 2001 From: Emile Fugulin Date: Mon, 18 Nov 2019 22:29:02 -0500 Subject: [PATCH] Handle multiple transfer-encoding --- gunicorn/http/errors.py | 8 ++++++++ gunicorn/http/message.py | 10 ++++++++-- gunicorn/workers/base.py | 7 ++++++- tests/requests/invalid/022.http | 5 +++++ tests/requests/invalid/022.py | 5 +++++ tests/requests/valid/029.http | 7 +++++++ tests/requests/valid/029.py | 14 ++++++++++++++ tests/requests/valid/030.http | 7 +++++++ tests/requests/valid/030.py | 14 ++++++++++++++ 9 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 tests/requests/invalid/022.http create mode 100644 tests/requests/invalid/022.py create mode 100644 tests/requests/valid/029.http create mode 100644 tests/requests/valid/029.py create mode 100644 tests/requests/valid/030.http create mode 100644 tests/requests/valid/030.py diff --git a/gunicorn/http/errors.py b/gunicorn/http/errors.py index 7839ef05..ea5b4826 100644 --- a/gunicorn/http/errors.py +++ b/gunicorn/http/errors.py @@ -118,3 +118,11 @@ class ForbiddenProxyRequest(ParseException): class InvalidSchemeHeaders(ParseException): def __str__(self): return "Contradictory scheme headers" + + +class UnsupportedTransferEncoding(ParseException): + def __init__(self, te): + self.te = te + + def __str__(self): + return "Unsupported Transfer-Encoding: %s" % self.te diff --git a/gunicorn/http/message.py b/gunicorn/http/message.py index cbfbd11c..59f50d7e 100644 --- a/gunicorn/http/message.py +++ b/gunicorn/http/message.py @@ -12,7 +12,7 @@ from gunicorn.http.unreader import SocketUnreader from gunicorn.http.body import ChunkedReader, LengthReader, EOFReader, Body from gunicorn.http.errors import (InvalidHeader, InvalidHeaderName, NoMoreData, InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, - LimitRequestLine, LimitRequestHeaders) + LimitRequestLine, LimitRequestHeaders, UnsupportedTransferEncoding) from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest from gunicorn.http.errors import InvalidSchemeHeaders from gunicorn.util import bytes_to_str, split_request_uri @@ -135,7 +135,13 @@ class Message(object): raise InvalidHeader("CONTENT-LENGTH", req=self) content_length = value elif name == "TRANSFER-ENCODING": - chunked = value.lower() == "chunked" + normalized_value = value.lower() + if normalized_value == "identity": + pass + elif normalized_value == "chunked": + chunked = True + else: + raise UnsupportedTransferEncoding(normalized_value) elif name == "SEC-WEBSOCKET-KEY1": content_length = 8 diff --git a/gunicorn/workers/base.py b/gunicorn/workers/base.py index f95994bc..7689c61f 100644 --- a/gunicorn/workers/base.py +++ b/gunicorn/workers/base.py @@ -20,6 +20,7 @@ from gunicorn.http.errors import ( InvalidProxyLine, InvalidRequestLine, InvalidRequestMethod, InvalidSchemeHeaders, LimitRequestHeaders, LimitRequestLine, + UnsupportedTransferEncoding ) from gunicorn.http.wsgi import Response, default_environ from gunicorn.reloader import reloader_engines @@ -206,7 +207,7 @@ class Worker(object): LimitRequestLine, LimitRequestHeaders, InvalidProxyLine, ForbiddenProxyRequest, InvalidSchemeHeaders, - SSLError)): + SSLError, UnsupportedTransferEncoding)): status_int = 400 reason = "Bad Request" @@ -237,6 +238,10 @@ class Worker(object): reason = "Forbidden" mesg = "'%s'" % str(exc) status_int = 403 + elif isinstance(exc, UnsupportedTransferEncoding): + reason = "Not implemented" + mesg = "'%s'" % str(exc) + status_int = 501 msg = "Invalid request from ip={ip}: {error}" self.log.debug(msg.format(ip=addr[0], error=str(exc))) diff --git a/tests/requests/invalid/022.http b/tests/requests/invalid/022.http new file mode 100644 index 00000000..784504be --- /dev/null +++ b/tests/requests/invalid/022.http @@ -0,0 +1,5 @@ +GET /stuff/here?foo=bar HTTP/1.1\r\n +Transfer-Encoding: chunked\r\n +Transfer-Encoding: compress\r\n +\r\n +xyz diff --git a/tests/requests/invalid/022.py b/tests/requests/invalid/022.py new file mode 100644 index 00000000..db5c9f38 --- /dev/null +++ b/tests/requests/invalid/022.py @@ -0,0 +1,5 @@ +from gunicorn.config import Config +from gunicorn.http.errors import UnsupportedTransferEncoding + +cfg = Config() +request = UnsupportedTransferEncoding diff --git a/tests/requests/valid/029.http b/tests/requests/valid/029.http new file mode 100644 index 00000000..c8611dbd --- /dev/null +++ b/tests/requests/valid/029.http @@ -0,0 +1,7 @@ +GET /stuff/here?foo=bar HTTP/1.1\r\n +Transfer-Encoding: chunked\r\n +Transfer-Encoding: identity\r\n +\r\n +5\r\n +hello\r\n +000\r\n diff --git a/tests/requests/valid/029.py b/tests/requests/valid/029.py new file mode 100644 index 00000000..f25449d1 --- /dev/null +++ b/tests/requests/valid/029.py @@ -0,0 +1,14 @@ +from gunicorn.config import Config + +cfg = Config() + +request = { + "method": "GET", + "uri": uri("/stuff/here?foo=bar"), + "version": (1, 1), + "headers": [ + ('TRANSFER-ENCODING', 'chunked'), + ('TRANSFER-ENCODING', 'identity') + ], + "body": b"hello" +} diff --git a/tests/requests/valid/030.http b/tests/requests/valid/030.http new file mode 100644 index 00000000..5d029dd9 --- /dev/null +++ b/tests/requests/valid/030.http @@ -0,0 +1,7 @@ +GET /stuff/here?foo=bar HTTP/1.1\r\n +Transfer-Encoding: identity\r\n +Transfer-Encoding: chunked\r\n +\r\n +5\r\n +hello\r\n +000\r\n diff --git a/tests/requests/valid/030.py b/tests/requests/valid/030.py new file mode 100644 index 00000000..3e98467b --- /dev/null +++ b/tests/requests/valid/030.py @@ -0,0 +1,14 @@ +from gunicorn.config import Config + +cfg = Config() + +request = { + "method": "GET", + "uri": uri("/stuff/here?foo=bar"), + "version": (1, 1), + "headers": [ + ('TRANSFER-ENCODING', 'identity'), + ('TRANSFER-ENCODING', 'chunked') + ], + "body": b"hello" +}