diff options
author | Sergey Shepelev <temotor@gmail.com> | 2022-01-14 01:50:53 +0300 |
---|---|---|
committer | Sergey Shepelev <temotor@gmail.com> | 2022-01-14 01:50:53 +0300 |
commit | ab746cbbfd4f1b52ce11c75a9bcc241d77ecf787 (patch) | |
tree | 45a67e5dadff7be31af9df5261175585ba3b3568 | |
parent | 955be1c7227a6df0daa537ebb8aed0cfa174d2e5 (diff) | |
download | eventlet-746-chunked-204.tar.gz |
wsgi: server MUST NOT send Content-Length/Transfer-Encoding header in response with a status code of 1xx, 204 or (2xx to CONNECT request)746-chunked-204
https://httpwg.org/specs/rfc7230.html#rfc.section.3.3.1
https://github.com/eventlet/eventlet/issues/746
-rw-r--r-- | eventlet/wsgi.py | 15 | ||||
-rw-r--r-- | tests/wsgi_test.py | 14 |
2 files changed, 26 insertions, 3 deletions
diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index 7ef0254..8af3027 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -479,6 +479,9 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): use_chunked = [False] length = [0] status_code = [200] + # Status code of 1xx or 204 or 2xx to CONNECT request MUST NOT send body and related headers + # https://httpwg.org/specs/rfc7230.html#rfc.section.3.3.1 + bodyless = [False] def write(data): towrite = [] @@ -510,7 +513,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): self.close_connection = 1 if 'content-length' not in header_list: - if self.request_version == 'HTTP/1.1': + if not bodyless[0] and self.request_version == 'HTTP/1.1': use_chunked[0] = True towrite.append(b'Transfer-Encoding: chunked\r\n') elif 'content-length' not in header_list: @@ -534,7 +537,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): length[0] = length[0] + sum(map(len, towrite)) def start_response(status, response_headers, exc_info=None): - status_code[0] = status.split()[0] + status_code[0] = int(status.split(" ", 1)[0]) if exc_info: try: if headers_sent: @@ -544,6 +547,12 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): # Avoid dangling circular ref exc_info = None + bodyless[0] = ( + status_code[0] == 204 + or (100 <= status_code[0] < 200) + or (self.command == "CONNECT" and 200 <= status_code[0] < 300) + ) + # Response headers capitalization # CONTent-TYpe: TExt/PlaiN -> Content-Type: TExt/PlaiN # Per HTTP RFC standard, header name is case-insensitive. @@ -571,7 +580,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): # Set content-length if possible if headers_set and not headers_sent and hasattr(result, '__len__'): # We've got a complete final response - if 'Content-Length' not in [h for h, _v in headers_set[1]]: + if not bodyless[0] and 'Content-Length' not in [h for h, _v in headers_set[1]]: headers_set[1].append(('Content-Length', str(sum(map(len, result))))) if request_input.should_send_hundred_continue: # We've got a complete final response, and never sent a 100 Continue. diff --git a/tests/wsgi_test.py b/tests/wsgi_test.py index 999d6f0..6f1a613 100644 --- a/tests/wsgi_test.py +++ b/tests/wsgi_test.py @@ -1862,6 +1862,20 @@ class TestHttpd(_TestBase): except Exception: assert False, self.logfile.getvalue() + def test_no_transfer_encoding_in_empty_response(self): + def app(environ, start_response): + write = start_response("204 OK", []) + write(b"") + + self.spawn_server(site=app) + sock = eventlet.connect(self.server_addr) + sock.sendall(b"DELETE /foo HTTP/1.1\r\n\r\n") + + response = read_http(sock) + sock.close() + print(response) + assert "transfer-encoding" not in response.headers_lower + def read_headers(sock): fd = sock.makefile('rb') |