[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