diff -r 4c65f8641d89 -r 125e080bbe15 Lib/test/test_wsgiref.py --- a/Lib/test/test_wsgiref.py Tue Apr 15 13:52:21 2014 +0100 +++ b/Lib/test/test_wsgiref.py Tue Apr 15 14:33:29 2014 -0400 @@ -17,6 +17,16 @@ from test import support + +# The server signature changes from version to version and we need it multiple +# times later on. Precompute it to make the tests more readable. +ver = sys.version.split()[0] +py = python_implementation() +pyver = py + "/" + ver +EXPECTED_SERVER_SIGNATURE = ( + "Server: WSGIServer/0.2 {}\r\n".format(pyver)).encode('iso-8859-1') + + class MockServer(WSGIServer): """Non-socket HTTP server""" @@ -102,17 +112,16 @@ class IntegrationTests(TestCase): def check_hello(self, out, has_length=True): - pyver = (python_implementation() + "/" + - sys.version.split()[0]) - self.assertEqual(out, - ("HTTP/1.0 200 OK\r\n" - "Server: WSGIServer/0.2 " + pyver +"\r\n" - "Content-Type: text/plain\r\n" - "Date: Mon, 05 Jun 2006 18:49:54 GMT\r\n" + - (has_length and "Content-Length: 13\r\n" or "") + - "\r\n" - "Hello, world!").encode("iso-8859-1") - ) + expected = ( + b"HTTP/1.0 200 OK\r\n" + + EXPECTED_SERVER_SIGNATURE + + b"Content-Type: text/plain\r\n" + + b"Date: Mon, 05 Jun 2006 18:49:54 GMT\r\n" + + (b"Content-Length: 13\r\n" if has_length else b"") + + b"\r\n" + b"Hello, world!") + + self.assertEqual(out, expected) def test_plain_hello(self): out, err = run_amock() @@ -159,17 +168,31 @@ return [b"data"] out, err = run_amock(validator(app)) self.assertTrue(err.endswith('"GET / HTTP/1.0" 200 4\n')) - ver = sys.version.split()[0].encode('ascii') - py = python_implementation().encode('ascii') - pyver = py + b"/" + ver self.assertEqual( - b"HTTP/1.0 200 OK\r\n" - b"Server: WSGIServer/0.2 "+ pyver + b"\r\n" - b"Content-Type: text/plain; charset=utf-8\r\n" - b"Date: Wed, 24 Dec 2008 13:29:32 GMT\r\n" - b"\r\n" - b"data", - out) + b"HTTP/1.0 200 OK\r\n" + + EXPECTED_SERVER_SIGNATURE + + b"Content-Type: text/plain; charset=utf-8\r\n" + b"Date: Wed, 24 Dec 2008 13:29:32 GMT\r\n" + b"\r\n" + b"data", + out) + + def test_304_correctly_does_not_set_content_length(self): + # bug 18099 + + def non_modified_app(environ, start_response): + start_response("304 Not Modified", [ + ("Date", "Wed, 24 Dec 2008 13:29:32 GMT")]) + return [] + + out, err = run_amock(validator(non_modified_app)) + + self.assertEqual( + b"HTTP/1.0 304 Not Modified\r\n" + + EXPECTED_SERVER_SIGNATURE + + b"Date: Wed, 24 Dec 2008 13:29:32 GMT\r\n" + + b"\r\n", + out) class UtilityTests(TestCase): diff -r 4c65f8641d89 -r 125e080bbe15 Lib/wsgiref/handlers.py --- a/Lib/wsgiref/handlers.py Tue Apr 15 13:52:21 2014 +0100 +++ b/Lib/wsgiref/handlers.py Tue Apr 15 14:33:29 2014 -0400 @@ -187,26 +187,30 @@ """Return the URL scheme being used""" return guess_scheme(self.environ) - def set_content_length(self): """Compute Content-Length or switch to chunked encoding if possible""" try: blocks = len(self.result) - except (TypeError,AttributeError,NotImplementedError): + except (TypeError, AttributeError, NotImplementedError): pass else: - if blocks==1: + if blocks in [0, 1]: self.headers['Content-Length'] = str(self.bytes_sent) return # XXX Try for chunked encoding if origin server and client is 1.1 - def cleanup_headers(self): """Make any necessary header changes or defaults Subclasses can extend this to add other defaults. """ - if 'Content-Length' not in self.headers: + # RFC 2616, 4.3 + allows_body = (not self.status.startswith('1') and + not self.status.startswith('204') and + not self.status.startswith('304')) + content_length_set = 'Content-Length' in self.headers + + if allows_body and not content_length_set: self.set_content_length() def start_response(self, status, headers,exc_info=None): @@ -300,16 +304,12 @@ """ return False # No platform-specific transmission by default - def finish_content(self): """Ensure headers and content have both been sent""" if not self.headers_sent: - # Only zero Content-Length if not set by the application (so - # that HEAD requests can be satisfied properly, see #3839) - self.headers.setdefault('Content-Length', "0") self.send_headers() else: - pass # XXX check if content-length was too short? + pass # XXX check if content-length was too short? def close(self): """Close the iterable (if needed) and reset all instance vars