summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Shepelev <temotor@gmail.com>2022-01-14 01:50:53 +0300
committerSergey Shepelev <temotor@gmail.com>2022-01-14 01:50:53 +0300
commitab746cbbfd4f1b52ce11c75a9bcc241d77ecf787 (patch)
tree45a67e5dadff7be31af9df5261175585ba3b3568
parent955be1c7227a6df0daa537ebb8aed0cfa174d2e5 (diff)
downloadeventlet-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.py15
-rw-r--r--tests/wsgi_test.py14
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')