Skip to content

Commit

Permalink
Remove Connection and Transfer-Encoding headers from WSGI responses.
Browse files Browse the repository at this point in the history
  • Loading branch information
hannosch committed Jul 22, 2016
1 parent 4f0f50c commit be5b14b
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 109 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ https://zope.readthedocs.io/en/2.13/CHANGES.html
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
Products.PloneHotfix20160419. [maurits]

Expand Down
50 changes: 8 additions & 42 deletions src/ZPublisher/WSGIPublisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,24 @@
from ZPublisher.pubevents import PubStart, PubBeforeCommit, PubAfterTraversal
from ZPublisher.Iterators import IUnboundStreamIterator, IStreamIterator

_NOW = None # overwrite for testing
_NOW = None # overwrite for testing


def _now():
if _NOW is not None:
return _NOW
return time.time()


class WSGIResponse(HTTPResponse):
"""A response object for WSGI
This Response object knows nothing about ZServer, but tries to be
compatible with the ZServerHTTPResponse.
Most significantly, streaming is not (yet) supported.
"""
_streaming = _chunking = 0
_streaming = 0
_http_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.
after_list = ()
Expand All @@ -68,8 +64,8 @@ def finalize(self):
# set 204 (no content) status if 200 and response is empty
# and not streaming
if ('content-type' not in headers and
'content-length' not in headers and
not self._streaming and self.status == 200):
'content-length' not in headers and
not self._streaming and self.status == 200):
self.setStatus('nocontent')

# add content length if not streaming
Expand All @@ -78,25 +74,6 @@ def finalize(self):
if content_length is None and not self._streaming:
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()

def listHeaders(self):
Expand All @@ -114,17 +91,15 @@ def _unauthorized(self):
if realm:
self.setHeader('WWW-Authenticate', 'basic realm="%s"' % realm, 1)

def write(self,data):
def write(self, data):
""" Add data to our output stream.
HTML data may be returned using a stream-oriented interface.
This allows the browser to display partial results while
computation of a response to proceed.
"""
if not self._streaming:

notify(PubBeforeStreaming(self))

self._streaming = 1
self.stdout.flush()

Expand All @@ -148,14 +123,6 @@ def setBody(self, body, title='', is_error=0):
HTTPResponse.setBody(self, body, title, is_error)

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


Expand Down Expand Up @@ -256,7 +223,6 @@ def publish_module(environ, start_response,
stderr = StringIO()
response = _response_factory(stdout=stdout, stderr=stderr)
response._http_version = environ['SERVER_PROTOCOL'].split('/')[1]
response._http_connection = environ.get('CONNECTION_TYPE', 'close')
response._server_version = environ.get('SERVER_SOFTWARE')

request = _request_factory(environ['wsgi.input'], environ, response)
Expand Down
67 changes: 0 additions & 67 deletions src/ZPublisher/tests/test_WSGIPublisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,61 +55,6 @@ def test_finalize_skips_setting_content_length_if_missing_w_streaming(self):
response.finalize()
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):
response = self._makeOne()
response.setBody('TESTING')
Expand Down Expand Up @@ -187,18 +132,6 @@ def __len__(self):
self.assertEqual(response.getHeader('Content-Length'),
'%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):
response = self._makeOne()
response.setBody('TESTING')
Expand Down

0 comments on commit be5b14b

Please sign in to comment.