Commit be5b14bd authored by Hanno Schlichting's avatar Hanno Schlichting

Remove `Connection` and `Transfer-Encoding` headers from WSGI responses.

parent 4f0f50c6
...@@ -11,6 +11,9 @@ https://zope.readthedocs.io/en/2.13/CHANGES.html ...@@ -11,6 +11,9 @@ https://zope.readthedocs.io/en/2.13/CHANGES.html
Bugs Fixed Bugs Fixed
++++++++++ ++++++++++
- Remove `Connection` and `Transfer-Encoding` headers from WSGI responses.
According to PEP 333 WSGI applications must not emit hop-by-hop headers.
- Removed docstrings from some methods to avoid publishing them. From - Removed docstrings from some methods to avoid publishing them. From
Products.PloneHotfix20160419. [maurits] Products.PloneHotfix20160419. [maurits]
......
...@@ -34,28 +34,24 @@ from ZPublisher.Publish import missing_name ...@@ -34,28 +34,24 @@ from ZPublisher.Publish import missing_name
from ZPublisher.pubevents import PubStart, PubBeforeCommit, PubAfterTraversal from ZPublisher.pubevents import PubStart, PubBeforeCommit, PubAfterTraversal
from ZPublisher.Iterators import IUnboundStreamIterator, IStreamIterator from ZPublisher.Iterators import IUnboundStreamIterator, IStreamIterator
_NOW = None # overwrite for testing _NOW = None # overwrite for testing
def _now(): def _now():
if _NOW is not None: if _NOW is not None:
return _NOW return _NOW
return time.time() return time.time()
class WSGIResponse(HTTPResponse): class WSGIResponse(HTTPResponse):
"""A response object for WSGI """A response object for WSGI
This Response object knows nothing about ZServer, but tries to be This Response object knows nothing about ZServer, but tries to be
compatible with the ZServerHTTPResponse. compatible with the ZServerHTTPResponse.
Most significantly, streaming is not (yet) supported.
""" """
_streaming = _chunking = 0 _streaming = 0
_http_version = None _http_version = None
_server_version = None _server_version = None
_http_connection = None
# Set this value to 1 if streaming output in
# HTTP/1.1 should use chunked encoding
http_chunk = 0
# Append any "cleanup" functions to this list. # Append any "cleanup" functions to this list.
after_list = () after_list = ()
...@@ -68,8 +64,8 @@ class WSGIResponse(HTTPResponse): ...@@ -68,8 +64,8 @@ class WSGIResponse(HTTPResponse):
# set 204 (no content) status if 200 and response is empty # set 204 (no content) status if 200 and response is empty
# and not streaming # and not streaming
if ('content-type' not in headers and if ('content-type' not in headers and
'content-length' not in headers and 'content-length' not in headers and
not self._streaming and self.status == 200): not self._streaming and self.status == 200):
self.setStatus('nocontent') self.setStatus('nocontent')
# add content length if not streaming # add content length if not streaming
...@@ -78,25 +74,6 @@ class WSGIResponse(HTTPResponse): ...@@ -78,25 +74,6 @@ class WSGIResponse(HTTPResponse):
if content_length is None and not self._streaming: if content_length is None and not self._streaming:
self.setHeader('content-length', len(body)) self.setHeader('content-length', len(body))
if self._http_version == '1.0':
if (self._http_connection == 'keep-alive' and
'content-length' in self.headers):
self.setHeader('Connection', 'Keep-Alive')
else:
self.setHeader('Connection', 'close')
# Close the connection if we have been asked to.
# Use chunking if streaming output.
if self._http_version == '1.1':
if self._http_connection == 'close':
self.setHeader('Connection', 'close')
elif not self.headers.has_key('content-length'):
if self.http_chunk and self._streaming:
self.setHeader('Transfer-Encoding', 'chunked')
self._chunking = 1
else:
self.setHeader('Connection','close')
return '%s %s' % (self.status, self.errmsg), self.listHeaders() return '%s %s' % (self.status, self.errmsg), self.listHeaders()
def listHeaders(self): def listHeaders(self):
...@@ -114,7 +91,7 @@ class WSGIResponse(HTTPResponse): ...@@ -114,7 +91,7 @@ class WSGIResponse(HTTPResponse):
if realm: if realm:
self.setHeader('WWW-Authenticate', 'basic realm="%s"' % realm, 1) self.setHeader('WWW-Authenticate', 'basic realm="%s"' % realm, 1)
def write(self,data): def write(self, data):
""" Add data to our output stream. """ Add data to our output stream.
HTML data may be returned using a stream-oriented interface. HTML data may be returned using a stream-oriented interface.
...@@ -122,9 +99,7 @@ class WSGIResponse(HTTPResponse): ...@@ -122,9 +99,7 @@ class WSGIResponse(HTTPResponse):
computation of a response to proceed. computation of a response to proceed.
""" """
if not self._streaming: if not self._streaming:
notify(PubBeforeStreaming(self)) notify(PubBeforeStreaming(self))
self._streaming = 1 self._streaming = 1
self.stdout.flush() self.stdout.flush()
...@@ -148,14 +123,6 @@ class WSGIResponse(HTTPResponse): ...@@ -148,14 +123,6 @@ class WSGIResponse(HTTPResponse):
HTTPResponse.setBody(self, body, title, is_error) HTTPResponse.setBody(self, body, title, is_error)
def __str__(self): def __str__(self):
# XXX Consider how we are to handle the cases this logic was trying
# to cover
#if self._wrote:
# if self._chunking:
# return '0\r\n\r\n'
# else:
# return ''
raise NotImplementedError raise NotImplementedError
...@@ -256,7 +223,6 @@ def publish_module(environ, start_response, ...@@ -256,7 +223,6 @@ def publish_module(environ, start_response,
stderr = StringIO() stderr = StringIO()
response = _response_factory(stdout=stdout, stderr=stderr) response = _response_factory(stdout=stdout, stderr=stderr)
response._http_version = environ['SERVER_PROTOCOL'].split('/')[1] response._http_version = environ['SERVER_PROTOCOL'].split('/')[1]
response._http_connection = environ.get('CONNECTION_TYPE', 'close')
response._server_version = environ.get('SERVER_SOFTWARE') response._server_version = environ.get('SERVER_SOFTWARE')
request = _request_factory(environ['wsgi.input'], environ, response) request = _request_factory(environ['wsgi.input'], environ, response)
......
...@@ -55,61 +55,6 @@ class WSGIResponseTests(unittest.TestCase): ...@@ -55,61 +55,6 @@ class WSGIResponseTests(unittest.TestCase):
response.finalize() response.finalize()
self.assertFalse(response.getHeader('Content-Length')) self.assertFalse(response.getHeader('Content-Length'))
def test_finalize_HTTP_1_0_keep_alive_w_content_length(self):
response = self._makeOne()
response._http_version = '1.0'
response._http_connection = 'keep-alive'
response.setBody('TESTING')
response.finalize()
self.assertEqual(response.getHeader('Connection'), 'Keep-Alive')
def test_finalize_HTTP_1_0_keep_alive_wo_content_length_streaming(self):
response = self._makeOne()
response._http_version = '1.0'
response._http_connection = 'keep-alive'
response._streaming = True
response.finalize()
self.assertEqual(response.getHeader('Connection'), 'close')
def test_finalize_HTTP_1_0_not_keep_alive_w_content_length(self):
response = self._makeOne()
response._http_version = '1.0'
response.setBody('TESTING')
response.finalize()
self.assertEqual(response.getHeader('Connection'), 'close')
def test_finalize_HTTP_1_1_connection_close(self):
response = self._makeOne()
response._http_version = '1.1'
response._http_connection = 'close'
response.finalize()
self.assertEqual(response.getHeader('Connection'), 'close')
def test_finalize_HTTP_1_1_wo_content_length_streaming_wo_http_chunk(self):
response = self._makeOne()
response._http_version = '1.1'
response._streaming = True
response.http_chunk = 0
response.finalize()
self.assertEqual(response.getHeader('Connection'), 'close')
self.assertEqual(response.getHeader('Transfer-Encoding'), None)
self.assertFalse(response._chunking)
def test_finalize_HTTP_1_1_wo_content_length_streaming_w_http_chunk(self):
response = self._makeOne()
response._http_version = '1.1'
response._streaming = True
response.http_chunk = 1
response.finalize()
self.assertEqual(response.getHeader('Connection'), None)
def test_finalize_HTTP_1_1_w_content_length_wo_chunk_wo_streaming(self):
response = self._makeOne()
response._http_version = '1.1'
response.setBody('TESTING')
response.finalize()
self.assertEqual(response.getHeader('Connection'), None)
def test_listHeaders_skips_Server_header_wo_server_version_set(self): def test_listHeaders_skips_Server_header_wo_server_version_set(self):
response = self._makeOne() response = self._makeOne()
response.setBody('TESTING') response.setBody('TESTING')
...@@ -187,18 +132,6 @@ class WSGIResponseTests(unittest.TestCase): ...@@ -187,18 +132,6 @@ class WSGIResponseTests(unittest.TestCase):
self.assertEqual(response.getHeader('Content-Length'), self.assertEqual(response.getHeader('Content-Length'),
'%d' % len(test_streamiterator.data)) '%d' % len(test_streamiterator.data))
#def test___str__already_wrote_not_chunking(self):
# response = self._makeOne()
# response._wrote = True
# response._chunking = False
# self.assertEqual(str(response), '')
#def test___str__already_wrote_w_chunking(self):
# response = self._makeOne()
# response._wrote = True
# response._chunking = True
# self.assertEqual(str(response), '0\r\n\r\n')
def test___str___raises(self): def test___str___raises(self):
response = self._makeOne() response = self._makeOne()
response.setBody('TESTING') response.setBody('TESTING')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment