[Checkins] SVN: zope.server/trunk/ - Implemented correct server proxy behavior. The HTTP server would always add
Stephan Richter
srichter at gmail.com
Thu Aug 5 17:27:42 EDT 2010
Log message for revision 115511:
- Implemented correct server proxy behavior. The HTTP server would always add
a "Server" and "Date" response header to the list of response headers
regardless whether one had been set already. The HTTP 1.1 spec specifies
that a proxy server must not modify the "Server" and "Date" header but add a
"Via" header instead.
- Get ready for release.
Changed:
U zope.server/trunk/CHANGES.txt
U zope.server/trunk/setup.py
U zope.server/trunk/src/zope/server/http/httptask.py
U zope.server/trunk/src/zope/server/http/tests/test_httpserver.py
U zope.server/trunk/src/zope/server/http/tests/test_wsgiserver.py
-=-
Modified: zope.server/trunk/CHANGES.txt
===================================================================
--- zope.server/trunk/CHANGES.txt 2010-08-05 20:10:41 UTC (rev 115510)
+++ zope.server/trunk/CHANGES.txt 2010-08-05 21:27:42 UTC (rev 115511)
@@ -3,10 +3,14 @@
=======
-3.7.1 (undefined)
+3.8.0 (2010-08-05)
------------------
-- Nothing changed yet.
+- Implemented correct server proxy behavior. The HTTP server would always add
+ a "Server" and "Date" response header to the list of response headers
+ regardless whether one had been set already. The HTTP 1.1 spec specifies
+ that a proxy server must not modify the "Server" and "Date" header but add a
+ "Via" header instead.
3.7.0 (2010-08-01)
------------------
Modified: zope.server/trunk/setup.py
===================================================================
--- zope.server/trunk/setup.py 2010-08-05 20:10:41 UTC (rev 115510)
+++ zope.server/trunk/setup.py 2010-08-05 21:27:42 UTC (rev 115511)
@@ -26,7 +26,7 @@
setup(
name='zope.server',
- version = '3.7.1dev',
+ version = '3.8.0',
author='Zope Foundation and Contributors',
author_email='zope-dev at zope.org',
description='Zope Server (Web and FTP)',
Modified: zope.server/trunk/src/zope/server/http/httptask.py
===================================================================
--- zope.server/trunk/src/zope/server/http/httptask.py 2010-08-05 20:10:41 UTC (rev 115510)
+++ zope.server/trunk/src/zope/server/http/httptask.py 2010-08-05 21:27:42 UTC (rev 115511)
@@ -52,9 +52,7 @@
def __init__(self, channel, request_data):
self.channel = channel
self.request_data = request_data
- self.response_headers = {
- 'Server': channel.server.SERVER_IDENT,
- }
+ self.response_headers = {}
version = request_data.version
if version not in ('1.0', '1.1'):
# fall back to a version we support.
@@ -127,7 +125,7 @@
else:
close_it = 1
elif version == '1.1':
- if 'connection: close' in (header.lower() for header in
+ if 'connection: close' in (header.lower() for header in
accumulated_headers):
close_it = 1
if connection == 'close':
@@ -143,7 +141,7 @@
# the value of content-length manually
if 'content-length' not in (header[:14].lower() for header in
accumulated_headers):
- close_it = 1
+ close_it = 1
# under HTTP 1.1 keep-alive is default, no need to set the header
else:
# Close if unrecognized HTTP version.
@@ -153,6 +151,18 @@
if close_it:
self.response_headers['Connection'] = 'close'
+ # Set the Server and Date field, if not yet specified. This is needed
+ # if the server is used as a proxy.
+ if 'server' not in (header[:6].lower() for header in
+ accumulated_headers):
+ self.response_headers['Server'] = self.channel.server.SERVER_IDENT
+ else:
+ self.response_headers['Via'] = self.channel.server.SERVER_IDENT
+ if 'date' not in (header[:4].lower() for header in
+ accumulated_headers):
+ self.response_headers['Date'] = build_http_date(self.start_time)
+
+
def buildResponseHeader(self):
self.prepareResponseHeaders()
first_line = 'HTTP/%s %s %s' % (self.version, self.status, self.reason)
@@ -221,7 +231,6 @@
def start(self):
now = time.time()
self.start_time = now
- self.response_headers['Date'] = build_http_date (now)
def finish(self):
if not self.wrote_header:
Modified: zope.server/trunk/src/zope/server/http/tests/test_httpserver.py
===================================================================
--- zope.server/trunk/src/zope/server/http/tests/test_httpserver.py 2010-08-05 20:10:41 UTC (rev 115510)
+++ zope.server/trunk/src/zope/server/http/tests/test_httpserver.py 2010-08-05 21:27:42 UTC (rev 115511)
@@ -139,6 +139,9 @@
response_body = response.read()
self.failUnlessEqual(length, len(response_body))
self.failUnlessEqual(response_body, body)
+ # HTTP 1.1 requires the server and date header.
+ self.assertEqual(response.getheader('server'), 'zope.server.http')
+ self.assert_(response.getheader('date') is not None)
def testMultipleRequestsWithoutBody(self):
# Tests the use of multiple requests in a single connection.
@@ -307,10 +310,10 @@
self.failUnlessEqual(int(response.status), 200)
connection = response.getheader('Connection', '')
# We sent no Connection: Keep-Alive header
- # Connection: close (or no header) is default.
+ # Connection: close (or no header) is default.
self.failUnless(connection != 'Keep-Alive')
-
- # If header Connection: Keep-Alive is explicitly sent,
+
+ # If header Connection: Keep-Alive is explicitly sent,
# we want to keept the connection open, we also need to return
# the corresponding header
data = "Keep me alive"
@@ -367,8 +370,8 @@
# response = h.getresponse()
# self.failUnlessEqual(int(response.status), 200)
# self.failUnless(response.getheader('connection') != 'close')
-
- # specifying Connection: close explicitly
+
+ # specifying Connection: close explicitly
data = "Don't keep me alive"
s = ("GET / HTTP/1.1\n"
"Connection: close\n"
Modified: zope.server/trunk/src/zope/server/http/tests/test_wsgiserver.py
===================================================================
--- zope.server/trunk/src/zope/server/http/tests/test_wsgiserver.py 2010-08-05 20:10:41 UTC (rev 115510)
+++ zope.server/trunk/src/zope/server/http/tests/test_wsgiserver.py 2010-08-05 21:27:42 UTC (rev 115511)
@@ -84,6 +84,11 @@
self.tries += 1
raise Conflict
+ def proxy(self, REQUEST):
+ """Behaves like a real proxy response."""
+ REQUEST.response.addHeader('Server', 'Fake/1.0')
+ REQUEST.response.addHeader('Date', 'Thu, 01 Apr 2010 12:00:00 GMT')
+ return 'Proxied Content'
class WSGIInfo(object):
"""Docstring required by publisher"""
@@ -171,7 +176,8 @@
while self.run_loop:
poll(0.1, socket_map)
- def invokeRequest(self, path='/', add_headers=None, request_body=''):
+ def invokeRequest(self, path='/', add_headers=None, request_body='',
+ return_response=False):
h = HTTPConnection(LOCALHOST, self.port)
h.putrequest('GET', path)
h.putheader('Accept', 'text/plain')
@@ -184,6 +190,8 @@
if request_body:
h.send(request_body)
response = h.getresponse()
+ if return_response:
+ return response
length = int(response.getheader('Content-Length', '0'))
if length:
response_body = response.read(length)
@@ -231,6 +239,20 @@
# conflicts.
self.assertEqual(status, 409)
+ def testServerAsProxy(self):
+ response = self.invokeRequest(
+ '/proxy', return_response=True)
+ # The headers set by the proxy are honored,
+ self.assertEqual(
+ response.getheader('Server'), 'Fake/1.0')
+ self.assertEqual(
+ response.getheader('Date'), 'Thu, 01 Apr 2010 12:00:00 GMT')
+ # The server adds a Via header.
+ self.assertEqual(
+ response.getheader('Via'), 'zope.server.http (Browser)')
+ # And the content got here too.
+ self.assertEqual(response.read(), 'Proxied Content')
+
def testWSGIVariables(self):
# Assert that the environment contains all required WSGI variables
status, response_body = self.invokeRequest('/wsgi')
More information about the checkins
mailing list