[Zope-Checkins] CVS: Zope3/lib/python/Zope/Publisher/HTTP - HTTPRequest.py:1.1.2.10 HTTPResponse.py:1.1.2.7

Shane Hathaway shane@digicool.com
Wed, 21 Nov 2001 19:23:10 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/Publisher/HTTP
In directory cvs.zope.org:/tmp/cvs-serv31156/Publisher/HTTP

Modified Files:
      Tag: Zope-3x-branch
	HTTPRequest.py HTTPResponse.py 
Log Message:
- Created new HTTP server based on Medusa and ZServer.

- Made some corresponding changes to Zope.Publication.

- Got minitest working again.


=== Zope3/lib/python/Zope/Publisher/HTTP/HTTPRequest.py 1.1.2.9 => 1.1.2.10 ===
         self.environ=environ
         get_env=environ.get
-        other=self.other={'RESPONSE': response}
+        other = self.other
         self.form={}
 
         ################################################################


=== Zope3/lib/python/Zope/Publisher/HTTP/HTTPResponse.py 1.1.2.6 => 1.1.2.7 ===
     The Response type encapsulates all possible responses to HTTP
     requests.  Responses are normally created by the object publisher.
-    A published object may recieve the response abject as an argument
+    A published object may recieve the response object as an argument
     named 'RESPONSE'.  A published object may also create it's own
     response object.  Normally, published objects use response objects
     to:
@@ -111,24 +111,27 @@
     passed into the object must be used.
     """
 
-    accumulated_headers = ''
-    body = ''
+    accumulated_headers = None
     base = None
     realm = 'Zope'
     _error_format = 'text/html'
     payload = None
+    _wrote_headers = 0
+    _streaming = 0
 
-    def __init__(self, payload, outstream, body='', headers=None,
-                 status=None, cookies=None):
+
+    def __init__(self, payload, outstream, header_output=None):
         self.payload = payload
         self._orig_payload = payload
-        BaseResponse.__init__(self, outstream, body, headers, status, cookies)
+        self.header_output = header_output
+        BaseResponse.__init__(self, outstream)
 
     def retry(self):
         """
         Returns a response object to be used in a retry attempt
         """
-        return self.__class__(self._orig_payload, self.outstream)
+        return self.__class__(self._orig_payload, self.outstream,
+                              self.header_output)
 
     def setStatus(self, status, reason=None):
         '''
@@ -182,8 +185,10 @@
         '''
         Sets a new HTTP return header with the given value, while retaining
         any previously set headers with the same name.'''
-        self.accumulated_headers=(
-            "%s%s: %s\n" % (self.accumulated_headers, name, value))
+        accum = self.accumulated_headers
+        if not accum:
+            self.accumulated_headers = accum = []
+        accum.append('%s: %s' % (name, value))
 
     def setBody(self, body):
         return self.payload.setBody(self, body)
@@ -262,7 +267,7 @@
         headers=self.headers
         if headers.has_key(name):
             h=self.header[name]
-            h="%s%s\n\t%s" % (h,delimiter,value)
+            h="%s%s\r\n\t%s" % (h,delimiter,value)
         else: h=value
         self.setHeader(name,h)
 
@@ -277,7 +282,6 @@
         """
         self.payload.handleException(self, exc_info)
 
-    _wrote=None
 
     def _cookie_list(self):
         cookie_list=[]
@@ -302,27 +306,21 @@
         
         return cookie_list
 
-    def __str__(self):
+
+    def getStatusAndHeaders(self):
         """
-        Outputs the headers of the response.
+        Returns a tuple of the status and a mapping of correctly-cased
+        header names to values.
         """
-        if self._wrote: return ''       # Streaming output was used.
+        res = {}
+        headers = self.headers
 
-        headers=self.headers
-        body=self.body
-
-        if (not headers.has_key('content-length') and
-            not headers.has_key('transfer-encoding')):
+        if (not self._streaming and not headers.has_key('content-length')
+            and not headers.has_key('transfer-encoding')):
             self.updateContentLength()
 
-        headersl=[]
-        append=headersl.append
+        res["X-Powered-By"] = "Zope (www.zope.org), Python (www.python.org)"
 
-        # status header must come first.
-        append("Status: %s" % headers.get('status', '200 OK'))
-        append("X-Powered-By: Zope (www.zope.org), Python (www.python.org)")
-        if headers.has_key('status'):
-            del headers['status']
         for key, val in headers.items():
             if key.lower() == key:
                 # only change non-literal header names
@@ -333,13 +331,59 @@
                     key="%s-%s%s" % (key[:l],key[l+1:l+2].upper(),key[l+2:])
                     start=l+1
                     l=key.find('-',start)
-            append("%s: %s" % (key, val))
-        if self.cookies:
-            headersl=headersl+self._cookie_list()
-        headersl[len(headersl):]=[self.accumulated_headers, body]
-        return '\n'.join(headersl)
+            res[key] = val
 
-    def write(self,data):
+        if res.has_key('Status'):
+            status = res['Status']
+            del res['Status']
+        else:
+            if not self._streaming and not self.body:
+                status = '204 No Content'
+            else:
+                status = '200 OK'
+
+        return status, res
+
+
+    def getHeaderText(self, status, m):
+        lst = ['Status: %s' % status]
+        lst.extend(map(lambda x: '%s: %s' % x, m.items()))
+        lst.extend(self._cookie_list())
+        accum = self.accumulated_headers
+        if accum:
+            lst.extend(accum)
+        return ('%s\r\n\r\n' % '\r\n'.join(lst))
+
+
+    def outputHeaders(self):
+        status, m = self.getStatusAndHeaders()
+        header_output = self.header_output
+        if header_output is not None:
+            # Use the IHeaderOutput interface.
+            header_output.setResponseStatus(status)
+            header_output.setResponseHeaders(m)
+            cookies = self._cookie_list()
+            if cookies:
+                header_output.appendResponseHeaders(cookies)
+            accum = self.accumulated_headers
+            if accum:
+                header_output.appendResponseHeaders(accum)
+        else:
+            # Write directly to outstream.
+            s = self.getHeaderText(status, m)
+            self.outstream.write(s)
+
+
+    def __str__(self):
+        """
+        Debugging output.  Does not include headers added for connection
+        control.
+        """
+        status, m = self.getStatusAndHeaders()
+        return self.getHeaderText(status, m) + self.body
+
+
+    def write(self, data):
         """
         Return data as a stream
 
@@ -354,11 +398,19 @@
         after beginning stream-oriented output. 
 
         """
-        if not self._wrote:
-            self.outputBody()
-            self._wrote=1
-            self.outstream.flush()
-
+        if streaming:
+            self._streaming = 1
+        self.output(data)
+
+    def output(self, data):
+        if not self._wrote_headers:
+            self.outputHeaders()
+            self._wrote_headers = 1
         self.outstream.write(data)
 
+    def outputBody(self):
+        """
+        Outputs the response body.
+        """
+        self.output(self.body)