[Zope-Checkins] CVS: Zope3/lib/python/Zope/Server - HTTPServer2.py:1.1.2.2 dual_mode_channel.py:1.1.2.2

Shane Hathaway shane@digicool.com
Mon, 26 Nov 2001 09:57:24 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/Server
In directory cvs.zope.org:/tmp/cvs-serv4654

Modified Files:
      Tag: Zope-3x-branch
	HTTPServer2.py dual_mode_channel.py 
Log Message:
- Better in sync with HTTP spec.

- Pipelineable without making unnecessary buffers

- Added ZPL

- Moved task management out of dual_mode_channel



=== Zope3/lib/python/Zope/Server/HTTPServer2.py 1.1.2.1 => 1.1.2.2 === (436/536 lines abridged)
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 1.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS 
+# FOR A PARTICULAR PURPOSE.
 
 # This server uses asyncore to accept connections and do initial
 # processing but threads to do work.
@@ -17,14 +25,15 @@
     from dual_mode_channel import simultaneous_mode_channel as channel_type
 else:
     from dual_mode_channel import dual_mode_channel as channel_type
-
-from dual_mode_channel import synchronous_instream
+from dual_mode_channel import OverflowableBuffer
 
 try:
     from cStringIO import StringIO
 except ImportError:
     from StringIO import StringIO
 
+from thread import allocate_lock
+
 
 if 1:
     # Patch asyncore for speed.
@@ -32,6 +41,9 @@
         del asyncore.dispatcher.__getattr__
 
 
+default_body = "The HTTP server is running!\r\n" * 10
+
+
 class http_task:
 
     # __implements__ = ITask
@@ -42,14 +54,18 @@
     wrote_header = 0
     accumulated_headers = None
 
-    def __init__(self, channel, request_header_plus, body_start):
+    def __init__(self, channel, request_data):
         self.channel = channel
-        self.request_header_plus = request_header_plus
-        self.body_start = body_start
+        self.request_data = request_data
         self.response_headers = {
             'Server' : 'Zope.Server.HTTPServer',

[-=- -=- -=- 436 lines omitted -=- -=- -=-]

+                return
+            conn, addr = v
         except socket.error:
                 # linux: on rare occasions we get a bogus socket back from
                 # accept.  socketmodule.c:makesockaddr complains that the
@@ -303,33 +493,8 @@
             task.service()
 
 
-first_line_re = re.compile (
-    '([^ ]+) (?:[^ :?#]+://[^ ?#/]*)?([^ ]+)(( HTTP/([0-9.]+))$|$)')
-
-def crack_first_line (r):
-    m = first_line_re.match (r)
-    if m is not None and m.end() == len(r):
-        if m.group(3):
-            version = m.group(5)
-        else:
-            version = None
-        return m.group(1).upper(), m.group(2), version
-    else:
-        return None, None, None
 
 
-def get_header_lines(header):
-    """
-    Splits the header into lines, putting multi-line headers together.
-    """
-    r = []
-    lines = header.split('\r\n')
-    for line in lines:
-        if line and line[0] in ' \t':	
-            r[-1] = r[-1] + line[1:]
-        else:
-            r.append(line)
-    return r
 
 
 if __name__ == '__main__':
@@ -338,7 +503,9 @@
     tasks.setThreadCount(4)
     http_server('', 8080, tasks=tasks)
     try:
-        asyncore.loop()
+        while 1:
+            asyncore.poll(5)
+            #print http_channel.active_channels
     except KeyboardInterrupt:
         print 'shutting down...'
         tasks.shutdown()


=== Zope3/lib/python/Zope/Server/dual_mode_channel.py 1.1.2.1 => 1.1.2.2 === (710/810 lines abridged)
+# Copyright (c) 2001 Zope Corporation and Contributors.  All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 1.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS 
+# FOR A PARTICULAR PURPOSE.
 
 import asyncore
 import socket
 import sys
-import time
+from time import time
 
 try:
     from cStringIO import StringIO
@@ -15,6 +22,9 @@
 pull_trigger = trigger().pull_trigger
 
 
+# copy_bytes controls the size of temp. strings for shuffling data around.
+COPY_BYTES = 1 << 18  # 64K
+
 
 
 class dual_mode_channel (asyncore.dispatcher):
@@ -24,40 +34,28 @@
 
     # recv_bytes is the argument to pass to socket.recv().
     recv_bytes = 8192
+    # send_bytes is the number of bytes to send to socket.send().
+    send_bytes = 8192
 
-    # outbuf_maxsize specifies maximum outbuf is allowed to hold
-    # before the application starts blocking on output.
-    # Raising outbuf_maxsize will improve throughput if you have
-    # files larger than outbuf_maxsize being transferred over
-    # more concurrent, slow connections than your worker thread count.
-    # Expect maximum RAM consumption by outbufs to be
-    # at most (number_of_concurrent_connections * outbuf_maxsize),
-    # but if you're using ZODB and everyone is downloading the
-    # same file then the normal RAM consumption is only a little more
-    # than (number of ZODB threads * outbuf_maxsize) because of
-    # ConservingStringBuffer.  Also, if ZODB is changed to
-    # share strings among threads, normal RAM consumption by outbufs
-    # will decrease significantly.
-    outbuf_maxsize = 4200000  # About 4 MB
-
-    # Create a tempfile if the input data gets larger than inbuf_overflow.

[-=- -=- -=- 710 lines omitted -=- -=- -=-]

+                self.strbuf = ''
+                return
+            buf = self._create_buffer()
+        buf.skip(bytes, allow_prune)
 
-    def del_bytes(self, bytes):
+    def prune(self):
         """
-        Deletes the given number of bytes from the start of the buffer.
+        A potentially expensive operation that removes all data
+        already retrieved from the buffer.
         """
-        gotbytes = 0
-        data = self.data
-        for index in range(len(data)):
-            s = data[index]
-            slen = len(s)
-            gotbytes = gotbytes + slen
-            if gotbytes > bytes:
-                position = slen - (gotbytes - bytes)
-                del data[:index]
-                data[0] = s[position:]
-                self.len = self.len - bytes
-                return
-            elif gotbytes == bytes:
-                del data[:index + 1]
-                self.len = self.len - bytes
-                return
-        # Hmm, too many!
-        raise ValueError, (
-            "Can't delete %d bytes from buffer of %d bytes" %
-            (bytes, gotbytes))
+        buf = self.buf
+        if buf is None:
+            self.strbuf = ''
+            return
+        buf.prune()
+        if self.overflowed:
+            sz = len(buf)
+            if sz < self.overflow:
+                # Revert to a faster buffer.
+                self._set_small_buffer()
+
+    def getfile(self):
+        buf = self.buf
+        if buf is None:
+            buf = self._create_buffer()
+        return buf.getfile()