bug 663096 udpate mochitest/pywebsockets to 489 r=biesi
authorPatrick McManus <mcmanus@ducksong.com>
Fri, 10 Jun 2011 16:52:29 -0400
changeset 71419 30653af91633061c0179398f726606298d623e65
parent 71418 776b0b91568ed6b259adec9a51733e3607a48642
child 71420 2146642133c4cc1ec110788bd352e028ec41be9a
push idunknown
push userunknown
push dateunknown
reviewersbiesi
bugs663096
milestone7.0a1
bug 663096 udpate mochitest/pywebsockets to 489 r=biesi
testing/mochitest/Makefile.in
testing/mochitest/pywebsocket/README
testing/mochitest/pywebsocket/mod_pywebsocket/_stream_base.py
testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hybi06.py
testing/mochitest/pywebsocket/mod_pywebsocket/common.py
testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py
testing/mochitest/pywebsocket/mod_pywebsocket/handshake/__init__.py
testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py
testing/mochitest/pywebsocket/mod_pywebsocket/handshake/draft75.py
testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi00.py
testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi06.py
testing/mochitest/pywebsocket/mod_pywebsocket/headerparserhandler.py
testing/mochitest/pywebsocket/mod_pywebsocket/http_header_util.py
testing/mochitest/pywebsocket/mod_pywebsocket/memorizingfile.py
testing/mochitest/pywebsocket/mod_pywebsocket/standalone.py
testing/mochitest/pywebsocket/mod_pywebsocket/util.py
testing/mochitest/pywebsocket/standalone.py
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -106,23 +106,23 @@ include $(topsrcdir)/build/automation-bu
 		pywebsocket/standalone.py \
 		$(NULL)
 
 _MOD_PYWEBSOCKET_FILES = \
 		pywebsocket/mod_pywebsocket/__init__.py \
 		pywebsocket/mod_pywebsocket/common.py \
 		pywebsocket/mod_pywebsocket/dispatch.py \
 		pywebsocket/mod_pywebsocket/headerparserhandler.py \
+		pywebsocket/mod_pywebsocket/http_header_util.py \
 		pywebsocket/mod_pywebsocket/memorizingfile.py \
 		pywebsocket/mod_pywebsocket/util.py \
 		pywebsocket/mod_pywebsocket/stream.py \
 		pywebsocket/mod_pywebsocket/_stream_hixie75.py \
 		pywebsocket/mod_pywebsocket/msgutil.py \
 		pywebsocket/mod_pywebsocket/_stream_hybi06.py \
-		pywebsocket/mod_pywebsocket/standalone.py \
 		pywebsocket/mod_pywebsocket/_stream_base.py \
 		$(NULL)
 
 _HANDSHAKE_FILES = \
 		pywebsocket/mod_pywebsocket/handshake/__init__.py \
 		pywebsocket/mod_pywebsocket/handshake/hybi00.py \
 		pywebsocket/mod_pywebsocket/handshake/_base.py \
 		pywebsocket/mod_pywebsocket/handshake/draft75.py \
--- a/testing/mochitest/pywebsocket/README
+++ b/testing/mochitest/pywebsocket/README
@@ -1,13 +1,14 @@
 mod_pywebsocket http://pywebsocket.googlecode.com/svn
-version 470
+version 489
 supporting ietf-07
 
-includes the following minor patch::
+includes the following minor patch:: (first bit supports symlinked wsh
+files, the second allows python 2.5 to work)
 
 diff --git a/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py b/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py
 --- a/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py
 +++ b/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py
 @@ -60,17 +60,18 @@ def _normalize_path(path):
          path: the path to normalize.
  
      Path is converted to the absolute path.
@@ -22,8 +23,33 @@ diff --git a/testing/mochitest/pywebsock
      path = path.replace('\\', '/')
      return path
  
  
  def _create_path_to_resource_converter(base_dir):
      base_dir = _normalize_path(base_dir)
  
      base_len = len(base_dir)
+
+diff --git a/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_base.py b/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_base.py
+--- a/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_base.py
++++ b/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_base.py
+@@ -92,19 +92,17 @@ class StreamBase(object):
+         prepends remote address to the exception message and raise again.
+ 
+         Raises:
+             ConnectionTerminatedException: when read returns empty string.
+         """
+ 
+         bytes = self._request.connection.read(length)
+         if not bytes:
+-            raise ConnectionTerminatedException(
+-                'Receiving %d byte failed. Peer (%r) closed connection' %
+-                (length, (self._request.connection.remote_addr,)))
++            raise ConnectionTerminatedException('connection terminated: read failed')
+         return bytes
+ 
+     def _write(self, bytes):
+         """Writes given bytes to connection. In case we catch any exception,
+         prepends remote address to the exception message and raise again.
+         """
+ 
+         try:
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_base.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_base.py
@@ -92,19 +92,17 @@ class StreamBase(object):
         prepends remote address to the exception message and raise again.
 
         Raises:
             ConnectionTerminatedException: when read returns empty string.
         """
 
         bytes = self._request.connection.read(length)
         if not bytes:
-            raise ConnectionTerminatedException(
-                'Receiving %d byte failed. Peer (%r) closed connection' %
-                (length, (self._request.connection.remote_addr,)))
+            raise ConnectionTerminatedException('connection terminated: read failed')
         return bytes
 
     def _write(self, bytes):
         """Writes given bytes to connection. In case we catch any exception,
         prepends remote address to the exception message and raise again.
         """
 
         try:
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hybi06.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hybi06.py
@@ -49,16 +49,18 @@ def is_control_opcode(opcode):
     return (opcode >> 3) == 1
 
 
 _NOOP_MASKER = util.NoopMasker()
 
 
 # Helper functions made public to be used for writing unittests for WebSocket
 # clients.
+
+
 def create_length_header(length, mask):
     """Creates a length header.
 
     Args:
         length: Frame length. Must be less than 2^63.
         mask: Mask bit. Must be boolean.
 
     Raises:
@@ -164,17 +166,21 @@ def create_pong_frame(body, mask=False):
 
 
 def create_close_frame(body, mask=False):
     header = create_header(common.OPCODE_CLOSE, len(body), 1, 0, 0, 0, mask)
     return _build_frame(header, body, mask)
 
 
 class StreamOptions(object):
+    """Holds option values to configure Stream objects."""
+
     def __init__(self):
+        """Constructs StreamOptions."""
+
         self.deflate = False
         self.mask_send = False
         self.unmask_receive = True
 
 
 class Stream(StreamBase):
     """Stream of WebSocket messages."""
 
@@ -230,16 +236,19 @@ class Stream(StreamBase):
         mask = (second_byte >> 7) & 1
         payload_length = second_byte & 0x7f
 
         if (mask == 1) != self._options.unmask_receive:
             raise InvalidFrameException(
                 'Mask bit on the received frame did\'nt match masking '
                 'configuration for received frames')
 
+        # The spec doesn't disallow putting a value in 0x0-0xFFFF into the
+        # 8-octet extended payload length field (or 0x0-0xFD in 2-octet field).
+        # So, we don't check the range of extended_payload_length.
         if payload_length == 127:
             extended_payload_length = self.receive_bytes(8)
             payload_length = struct.unpack(
                 '!Q', extended_payload_length)[0]
             if payload_length > 0x7FFFFFFFFFFFFFFF:
                 raise InvalidFrameException(
                     'Extended payload length >= 2^63')
         elif payload_length == 126:
@@ -289,17 +298,18 @@ class Stream(StreamBase):
                 data.
             UnsupportedFrameException: when the received frame has
                 flags, opcode we cannot handle. You can ignore this exception
                 and continue receiving the next frame.
         """
 
         if self._request.client_terminated:
             raise BadOperationException(
-                'Requested receive_message after receiving a closing handshake')
+                'Requested receive_message after receiving a closing '
+                'handshake')
 
         while True:
             # mp_conn.read will block if no bytes are available.
             # Timeout is controlled by TimeOut directive of Apache.
 
             opcode, bytes, fin, rsv1, rsv2, rsv3 = self._receive_frame()
             if rsv1 or rsv2 or rsv3:
                 raise UnsupportedFrameException(
@@ -410,18 +420,18 @@ class Stream(StreamBase):
 
                 while True:
                     try:
                         expected_body = self._ping_queue.popleft()
                         if expected_body == message:
                             # inflight_pings contains pings ignored by the
                             # other peer. Just forget them.
                             self._logger.debug(
-                                'Ping %r is acked (%d pings were ignored)' %
-                                (expected_body, len(inflight_pings)))
+                                'Ping %r is acked (%d pings were ignored)',
+                                expected_body, len(inflight_pings))
                             break
                         else:
                             inflight_pings.append(expected_body)
                     except IndexError, e:
                         # The received pong was unsolicited pong. Keep the
                         # ping queue as is.
                         self._ping_queue = inflight_pings
                         self._logger.debug('Received a unsolicited pong')
@@ -466,19 +476,19 @@ class Stream(StreamBase):
             return
 
         self._send_closing_handshake(code, reason)
         self._logger.debug('Sent server-initiated closing handshake')
 
         if (code == common.STATUS_GOING_AWAY or
             code == common.STATUS_PROTOCOL_ERROR):
             # It doesn't make sense to wait for a close frame if the reason is
-            # protocol error or that the server is going away. For some of other
-            # reasons, it might not make sense to wait for a close frame, but
-            # it's not clear, yet.
+            # protocol error or that the server is going away. For some of
+            # other reasons, it might not make sense to wait for a close frame,
+            # but it's not clear, yet.
             return
 
         # TODO(ukai): 2. wait until the /client terminated/ flag has been set,
         # or until a server-defined timeout expires.
         #
         # For now, we expect receiving closing handshake right after sending
         # out closing handshake.
         message = self.receive_message()
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/common.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/common.py
@@ -67,8 +67,11 @@ SEC_WEBSOCKET_PROTOCOL_HEADER = 'Sec-Web
 SEC_WEBSOCKET_EXTENSIONS_HEADER = 'Sec-WebSocket-Extensions'
 
 # Status codes
 STATUS_NORMAL = 1000
 STATUS_GOING_AWAY = 1001
 STATUS_PROTOCOL_ERROR = 1002
 STATUS_UNSUPPORTED = 1003
 STATUS_TOO_LARGE = 1004
+
+
+# vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py
@@ -228,28 +228,28 @@ class Dispatcher(object):
         transfer_data_ = self._get_handler_suite(request).transfer_data
         # TODO(tyoshino): Terminate underlying TCP connection if possible.
         try:
             transfer_data_(request)
             if not request.server_terminated:
                 request.ws_stream.close_connection()
         # Catch non-critical exceptions the handler didn't handle.
         except msgutil.BadOperationException, e:
-            self._logger.debug(str(e))
+            self._logger.debug('%s', e)
             request.ws_stream.close_connection(common.STATUS_GOING_AWAY)
         except msgutil.InvalidFrameException, e:
             # InvalidFrameException must be caught before
             # ConnectionTerminatedException that catches InvalidFrameException.
-            self._logger.debug(str(e))
+            self._logger.debug('%s', e)
             request.ws_stream.close_connection(common.STATUS_PROTOCOL_ERROR)
         except msgutil.UnsupportedFrameException, e:
-            self._logger.debug(str(e))
+            self._logger.debug('%s', e)
             request.ws_stream.close_connection(common.STATUS_UNSUPPORTED)
         except msgutil.ConnectionTerminatedException, e:
-            self._logger.debug(str(e))
+            self._logger.debug('%s', e)
         except Exception, e:
             util.prepend_message_to_exception(
                 '%s raised exception for %s: ' % (
                     _TRANSFER_DATA_HANDLER_NAME, request.ws_resource),
                 e)
             raise
 
     def _get_handler_suite(self, request):
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/__init__.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/__init__.py
@@ -35,16 +35,19 @@ successfully established.
 
 
 import logging
 
 from mod_pywebsocket import util
 from mod_pywebsocket.handshake import draft75
 from mod_pywebsocket.handshake import hybi00
 from mod_pywebsocket.handshake import hybi06
+# Export Extension symbol from this module.
+from mod_pywebsocket.handshake._base import Extension
+# Export HandshakeError symbol from this module.
 from mod_pywebsocket.handshake._base import HandshakeError
 
 
 class Handshaker(object):
     """This class performs WebSocket handshake."""
 
     def __init__(self, request, dispatcher, allowDraft75=False, strict=False):
         """Construct an instance.
@@ -71,28 +74,43 @@ class Handshaker(object):
         self._hixie75Handshaker = None
         if allowDraft75:
             self._hixie75Handshaker = draft75.Handshaker(
                 request, dispatcher, strict)
 
     def do_handshake(self):
         """Perform WebSocket Handshake."""
 
+        self._logger.debug('Opening handshake resource: %r', self._request.uri)
+        # To print mimetools.Message as escaped one-line string, we converts
+        # headers_in to dict object. Without conversion, if we use %r, it just
+        # prints the type and address, and if we use %s, it prints the original
+        # header string as multiple lines.
+        #
+        # Both mimetools.Message and MpTable_Type of mod_python can be
+        # converted to dict.
+        #
+        # mimetools.Message.__str__ returns the original header string.
+        # dict(mimetools.Message object) returns the map from header names to
+        # header values. While MpTable_Type doesn't have such __str__ but just
+        # __repr__ which formats itself as well as dictionary object.
         self._logger.debug(
-            'Opening handshake headers: %s' % self._request.headers_in)
+            'Opening handshake request headers: %r',
+            dict(self._request.headers_in))
 
         handshakers = [
             ('HyBi 07', self._hybi07Handshaker),
             ('HyBi 00', self._hybi00Handshaker),
             ('Hixie 75', self._hixie75Handshaker)]
         last_error = HandshakeError('No handshaker available')
         for name, handshaker in handshakers:
             if handshaker:
-                self._logger.info('Trying %s protocol' % name)
+                self._logger.info('Trying %s protocol', name)
                 try:
                     handshaker.do_handshake()
                     return
                 except HandshakeError, e:
-                    self._logger.info('%s handshake failed: %s' % (name, e))
+                    self._logger.info('%s handshake failed: %s', name, e)
                     last_error = e
         raise last_error
 
+
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py
@@ -29,16 +29,56 @@
 
 
 """Common functions and exceptions used by WebSocket opening handshake
 processors.
 """
 
 
 from mod_pywebsocket import common
+from mod_pywebsocket import http_header_util
+
+
+class Extension(object):
+    """Holds information about an extension which is exchanged on extension
+    negotiation in opening handshake.
+    """
+
+    def __init__(self, name):
+        self._name = name
+        # TODO(tyoshino): Change the data structure to more efficient one such
+        # as dict when the spec changes to say like
+        # - Parameter names must be unique
+        # - The order of parameters is not significant
+        self._parameters = []
+
+    def name(self):
+        return self._name
+
+    def add_parameter(self, name, value):
+        self._parameters.append((name, value))
+
+    def get_parameter(self, name):
+        for param_name, param_value in self._parameters:
+            if param_name == name:
+                return param_value
+
+    def get_parameter_names(self):
+        return [name for name, unused_value in self._parameters]
+
+    def get_formatted_string(self):
+        formatted_params = [self._name]
+        for param_name, param_value in self._parameters:
+            if param_value is None:
+                formatted_params.append(param_name)
+            else:
+                quoted_value = http_header_util.quote_if_necessary(param_value)
+                formatted_params.append('%s=%s' % (param_name, quoted_value))
+
+        return '; '.join(formatted_params)
 
 
 class HandshakeError(Exception):
     """This exception will be raised when an error occurred while processing
     WebSocket initial handshake.
     """
     pass
 
@@ -74,16 +114,20 @@ def parse_host_header(request):
     if len(fields) == 1:
         return fields[0], get_default_port(request.is_https())
     try:
         return fields[0], int(fields[1])
     except ValueError, e:
         raise HandshakeError('Invalid port number format: %r' % e)
 
 
+def format_header(name, value):
+    return '%s: %s\r\n' % (name, value)
+
+
 def build_location(request):
     """Build WebSocket location for request."""
     location_parts = []
     if request.is_https():
         location_parts.append(common.WEB_SOCKET_SECURE_SCHEME)
     else:
         location_parts.append(common.WEB_SOCKET_SCHEME)
     location_parts.append('://')
@@ -95,32 +139,129 @@ def build_location(request):
     location_parts.append(host)
     if (port != get_default_port(request.is_https())):
         location_parts.append(':')
         location_parts.append(str(port))
     location_parts.append(request.uri)
     return ''.join(location_parts)
 
 
-def get_mandatory_header(request, key, expected_value=None):
+def get_mandatory_header(request, key):
     value = request.headers_in.get(key)
     if value is None:
         raise HandshakeError('Header %s is not defined' % key)
-    if expected_value is not None and expected_value != value:
+    return value
+
+
+def validate_mandatory_header(request, key, expected_value):
+    value = get_mandatory_header(request, key)
+
+    if value.lower() != expected_value.lower():
         raise HandshakeError(
-            'Illegal value for header %s: %s (expected: %s)' %
-            (key, value, expected_value))
-    return value
+            'Expected %r for header %s but found %r (case-insensitive)' %
+            (expected_value, key, value))
 
 
 def check_header_lines(request, mandatory_headers):
     # 5.1 1. The three character UTF-8 string "GET".
     # 5.1 2. A UTF-8-encoded U+0020 SPACE character (0x20 byte).
     if request.method != 'GET':
         raise HandshakeError('Method is not GET')
     # The expected field names, and the meaning of their corresponding
     # values, are as follows.
     #  |Upgrade| and |Connection|
     for key, expected_value in mandatory_headers:
-        get_mandatory_header(request, key, expected_value)
+        validate_mandatory_header(request, key, expected_value)
+
+
+def _parse_extension_param(state, definition):
+    param_name = http_header_util.consume_token(state)
+
+    if param_name is None:
+        raise HandshakeError('No valid parameter name found')
+
+    http_header_util.consume_lwses(state)
+
+    if not http_header_util.consume_string(state, '='):
+        definition.add_parameter(param_name, None)
+        return
+
+    http_header_util.consume_lwses(state)
+
+    param_value = http_header_util.consume_token_or_quoted_string(state)
+    if param_value is None:
+        raise HandshakeError(
+            'No valid parameter value found on the right-hand side of '
+            'parameter %r' % param_name)
+
+    definition.add_parameter(param_name, param_value)
+
+
+def _parse_extension(state):
+    extension_token = http_header_util.consume_token(state)
+    if extension_token is None:
+        return None
+
+    extension = Extension(extension_token)
+
+    while True:
+        http_header_util.consume_lwses(state)
+
+        if not http_header_util.consume_string(state, ';'):
+            break
+
+        http_header_util.consume_lwses(state)
+
+        try:
+            _parse_extension_param(state, extension)
+        except HandshakeError, e:
+            raise HandshakeError(
+                'Failed to parse Sec-WebSocket-Extensions header: '
+                'Failed to parse parameter for %r (%r)' %
+                (extension_token, e))
+
+    return extension
+
+
+def parse_extensions(data):
+    """Parses Sec-WebSocket-Extensions header value returns a list of
+    common.Extension objects.
+
+    Leading LWSes must be trimmed.
+    """
+
+    state = http_header_util.ParsingState(data)
+
+    extension_list = []
+    while True:
+        extension = _parse_extension(state)
+        if extension is not None:
+            extension_list.append(extension)
+
+        http_header_util.consume_lwses(state)
+
+        if http_header_util.peek(state) is None:
+            break
+
+        if not http_header_util.consume_string(state, ','):
+            raise HandshakeError(
+                'Failed to parse Sec-WebSocket-Extensions header: '
+                'Expected a comma but found %r' %
+                http_header_util.peek(state))
+
+        http_header_util.consume_lwses(state)
+
+    if len(extension_list) == 0:
+        raise HandshakeError(
+            'Sec-WebSocket-Extensions header contains no valid extension')
+
+    return extension_list
+
+
+def format_extensions(extension_list):
+    formatted_extension_list = []
+    for extension in extension_list:
+        formatted_extension_list.append(extension.get_formatted_string())
+
+    return ', '.join(formatted_extension_list)
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/draft75.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/draft75.py
@@ -107,19 +107,23 @@ class Handshaker(object):
         """
 
         self._check_header_lines()
         self._set_resource()
         self._set_origin()
         self._set_location()
         self._set_subprotocol()
         self._set_protocol_version()
+
         self._dispatcher.do_extra_handshake(self._request)
+
         self._send_handshake()
 
+        self._logger.debug('Sent opening handshake response')
+
     def _set_resource(self):
         self._request.ws_resource = self._request.uri
 
     def _set_origin(self):
         self._request.ws_origin = self._request.headers_in['Origin']
 
     def _set_location(self):
         self._request.ws_location = build_location(self._request)
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi00.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi00.py
@@ -43,16 +43,17 @@ import re
 import struct
 
 from mod_pywebsocket import common
 from mod_pywebsocket.stream import StreamHixie75
 from mod_pywebsocket import util
 from mod_pywebsocket.handshake._base import HandshakeError
 from mod_pywebsocket.handshake._base import build_location
 from mod_pywebsocket.handshake._base import check_header_lines
+from mod_pywebsocket.handshake._base import format_header
 from mod_pywebsocket.handshake._base import get_mandatory_header
 from mod_pywebsocket.handshake._base import validate_subprotocol
 
 
 _MANDATORY_HEADERS = [
     # key, expected value or None
     [common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE_HIXIE75],
     [common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE],
@@ -92,19 +93,23 @@ class Handshaker(object):
         # dispatcher sets it in self._request.
         check_header_lines(self._request, _MANDATORY_HEADERS)
         self._set_resource()
         self._set_subprotocol()
         self._set_location()
         self._set_origin()
         self._set_challenge_response()
         self._set_protocol_version()
+
         self._dispatcher.do_extra_handshake(self._request)
+
         self._send_handshake()
 
+        self._logger.debug('Sent opening handshake response')
+
     def _set_resource(self):
         self._request.ws_resource = self._request.uri
 
     def _set_subprotocol(self):
         # |Sec-WebSocket-Protocol|
         subprotocol = self._request.headers_in.get(
             common.SEC_WEBSOCKET_PROTOCOL_HEADER)
         if subprotocol is not None:
@@ -150,83 +155,91 @@ class Handshaker(object):
 
     def _set_challenge_response(self):
         # 5.2 4-8.
         self._request.ws_challenge = self._get_challenge()
         # 5.2 9. let /response/ be the MD5 finterprint of /challenge/
         self._request.ws_challenge_md5 = util.md5_hash(
             self._request.ws_challenge).digest()
         self._logger.debug(
-            'Challenge: %r (%s)' %
-            (self._request.ws_challenge,
-             util.hexify(self._request.ws_challenge)))
+            'Challenge: %r (%s)',
+            self._request.ws_challenge,
+            util.hexify(self._request.ws_challenge))
         self._logger.debug(
-            'Challenge response: %r (%s)' %
-            (self._request.ws_challenge_md5,
-             util.hexify(self._request.ws_challenge_md5)))
+            'Challenge response: %r (%s)',
+            self._request.ws_challenge_md5,
+            util.hexify(self._request.ws_challenge_md5))
 
     def _get_key_value(self, key_field):
         key_value = get_mandatory_header(self._request, key_field)
 
+        self._logger.debug('%s: %r', key_field, key_value)
+
         # 5.2 4. let /key-number_n/ be the digits (characters in the range
         # U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9)) in /key_n/,
         # interpreted as a base ten integer, ignoring all other characters
         # in /key_n/.
         try:
             key_number = int(re.sub("\\D", "", key_value))
         except:
             raise HandshakeError('%s field contains no digit' % key_field)
         # 5.2 5. let /spaces_n/ be the number of U+0020 SPACE characters
         # in /key_n/.
         spaces = re.subn(" ", "", key_value)[1]
         if spaces == 0:
             raise HandshakeError('%s field contains no space' % key_field)
+
+        self._logger.debug(
+            '%s: Key-number is %d and number of spaces is %d',
+            key_field, key_number, spaces)
+
         # 5.2 6. if /key-number_n/ is not an integral multiple of /spaces_n/
         # then abort the WebSocket connection.
         if key_number % spaces != 0:
-            raise HandshakeError('Key-number %d is not an integral '
-                                 'multiple of spaces %d' % (key_number,
-                                                            spaces))
+            raise HandshakeError(
+                '%s: Key-number (%d) is not an integral multiple of spaces '
+                '(%d)' % (key_field, key_number, spaces))
         # 5.2 7. let /part_n/ be /key-number_n/ divided by /spaces_n/.
         part = key_number / spaces
-        self._logger.debug('%s: %s => %d / %d => %d' % (
-            key_field, key_value, key_number, spaces, part))
+        self._logger.debug('%s: Part is %d', key_field, part)
         return part
 
     def _get_challenge(self):
         # 5.2 4-7.
         key1 = self._get_key_value('Sec-WebSocket-Key1')
         key2 = self._get_key_value('Sec-WebSocket-Key2')
         # 5.2 8. let /challenge/ be the concatenation of /part_1/,
         challenge = ''
         challenge += struct.pack('!I', key1)  # network byteorder int
         challenge += struct.pack('!I', key2)  # network byteorder int
         challenge += self._request.connection.read(8)
         return challenge
 
-    def _sendall(self, data):
-        self._request.connection.write(data)
+    def _send_handshake(self):
+        response = []
 
-    def _send_header(self, name, value):
-        self._sendall('%s: %s\r\n' % (name, value))
+        # 5.2 10. send the following line.
+        response.append('HTTP/1.1 101 WebSocket Protocol Handshake\r\n')
 
-    def _send_handshake(self):
-        # 5.2 10. send the following line.
-        self._sendall('HTTP/1.1 101 WebSocket Protocol Handshake\r\n')
         # 5.2 11. send the following fields to the client.
-        self._send_header(
-            common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE_HIXIE75)
-        self._send_header(
-            common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE)
-        self._send_header('Sec-WebSocket-Location', self._request.ws_location)
-        self._send_header(
-            common.SEC_WEBSOCKET_ORIGIN_HEADER, self._request.ws_origin)
+        response.append(format_header(
+            common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE_HIXIE75))
+        response.append(format_header(
+            common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
+        response.append(format_header(
+            'Sec-WebSocket-Location', self._request.ws_location))
+        response.append(format_header(
+            common.SEC_WEBSOCKET_ORIGIN_HEADER, self._request.ws_origin))
         if self._request.ws_protocol:
-            self._send_header(
+            response.append(format_header(
                 common.SEC_WEBSOCKET_PROTOCOL_HEADER,
-                self._request.ws_protocol)
+                self._request.ws_protocol))
         # 5.2 12. send two bytes 0x0D 0x0A.
-        self._sendall('\r\n')
+        response.append('\r\n')
         # 5.2 13. send /response/
-        self._sendall(self._request.ws_challenge_md5)
+        response.append(self._request.ws_challenge_md5)
+
+        raw_response = ''.join(response)
+        self._logger.debug('Opening handshake response: %r', raw_response)
+        self._request.connection.write(raw_response)
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi06.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi06.py
@@ -41,19 +41,24 @@ import base64
 import logging
 import os
 import re
 
 from mod_pywebsocket import common
 from mod_pywebsocket.stream import Stream
 from mod_pywebsocket.stream import StreamOptions
 from mod_pywebsocket import util
+from mod_pywebsocket.handshake._base import check_header_lines
+from mod_pywebsocket.handshake._base import Extension
+from mod_pywebsocket.handshake._base import format_extensions
+from mod_pywebsocket.handshake._base import format_header
+from mod_pywebsocket.handshake._base import get_mandatory_header
 from mod_pywebsocket.handshake._base import HandshakeError
-from mod_pywebsocket.handshake._base import check_header_lines
-from mod_pywebsocket.handshake._base import get_mandatory_header
+from mod_pywebsocket.handshake._base import parse_extensions
+from mod_pywebsocket.handshake._base import validate_mandatory_header
 
 
 _MANDATORY_HEADERS = [
     # key, expected value or None
     [common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE],
     [common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE],
 ]
 
@@ -98,18 +103,18 @@ class Handshaker(object):
 
         self._get_origin()
         self._check_version()
         self._set_protocol()
         self._set_extensions()
 
         key = self._get_key()
         (accept, accept_binary) = compute_accept(key)
-        self._logger.debug('Sec-WebSocket-Accept: %r (%s)' %
-                           (accept, util.hexify(accept_binary)))
+        self._logger.debug('Sec-WebSocket-Accept: %r (%s)',
+                           accept, util.hexify(accept_binary))
 
         self._logger.debug('IETF HyBi 07 protocol')
         self._request.ws_version = common.VERSION_HYBI07
         stream_options = StreamOptions()
         stream_options.deflate = self._request.ws_deflate
         self._request.ws_stream = Stream(self._request, stream_options)
 
         self._request.ws_close_code = None
@@ -131,23 +136,25 @@ class Handshaker(object):
         else:
             if self._request.ws_protocol is not None:
                 raise HandshakeError(
                     'ws_protocol must be None when the client didn\'t request '
                     'any subprotocol')
 
         self._send_handshake(accept)
 
+        self._logger.debug('Sent opening handshake response')
+
     def _get_origin(self):
         origin = self._request.headers_in.get(
             common.SEC_WEBSOCKET_ORIGIN_HEADER)
         self._request.ws_origin = origin
 
     def _check_version(self):
-        unused_value = get_mandatory_header(
+        unused_value = validate_mandatory_header(
             self._request, common.SEC_WEBSOCKET_VERSION_HEADER, '7')
 
     def _set_protocol(self):
         self._request.ws_protocol = None
 
         protocol_header = self._request.headers_in.get(
             common.SEC_WEBSOCKET_PROTOCOL_HEADER)
 
@@ -164,35 +171,41 @@ class Handshaker(object):
         self._logger.debug('Subprotocols requested: %r', requested_protocols)
 
     def _set_extensions(self):
         self._request.ws_deflate = False
 
         extensions_header = self._request.headers_in.get(
             common.SEC_WEBSOCKET_EXTENSIONS_HEADER)
         if not extensions_header:
+            self._request.ws_requested_extensions = None
             self._request.ws_extensions = None
             return
 
         self._request.ws_extensions = []
 
-        requested_extensions = extensions_header.split(',')
-        # TODO(tyoshino): Follow the ABNF in the spec.
-        requested_extensions = [s.strip() for s in requested_extensions]
+        requested_extensions = parse_extensions(extensions_header)
 
         for extension in requested_extensions:
+            extension_name = extension.name()
             # We now support only deflate-stream extension. Any other
             # extension requests are just ignored for now.
-            if extension == 'deflate-stream':
+            if (extension_name == 'deflate-stream' and
+                len(extension.get_parameter_names()) == 0):
                 self._request.ws_extensions.append(extension)
                 self._request.ws_deflate = True
 
-        self._logger.debug('Extensions requested: %r', requested_extensions)
+        self._request.ws_requested_extensions = requested_extensions
+
         self._logger.debug(
-            'Extensions accepted: %r', self._request.ws_extensions)
+            'Extensions requested: %r',
+            map(Extension.name, self._request.ws_requested_extensions))
+        self._logger.debug(
+            'Extensions accepted: %r',
+            map(Extension.name, self._request.ws_extensions))
 
     def _validate_key(self, key):
         # Validate
         key_is_valid = False
         try:
             # Validate key by quick regex match before parsing by base64
             # module. Because base64 module skips invalid characters, we have
             # to do this in advance to make this server strictly reject illegal
@@ -212,39 +225,42 @@ class Handshaker(object):
         return decoded_key
 
     def _get_key(self):
         key = get_mandatory_header(
             self._request, common.SEC_WEBSOCKET_KEY_HEADER)
 
         decoded_key = self._validate_key(key)
 
-        self._logger.debug('Sec-WebSocket-Key: %r (%s)' %
-                           (key, util.hexify(decoded_key)))
+        self._logger.debug('Sec-WebSocket-Key: %r (%s)',
+                           key, util.hexify(decoded_key))
 
         return key
 
-    def _sendall(self, data):
-        self._request.connection.write(data)
+    def _send_handshake(self, accept):
+        response = []
 
-    def _send_header(self, name, value):
-        self._sendall('%s: %s\r\n' % (name, value))
+        response.append('HTTP/1.1 101 Switching Protocols\r\n')
 
-    def _send_handshake(self, accept):
-        self._sendall('HTTP/1.1 101 Switching Protocols\r\n')
-        self._send_header(common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE)
-        self._send_header(
-            common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE)
-        self._send_header(common.SEC_WEBSOCKET_ACCEPT_HEADER, accept)
+        response.append(format_header(
+            common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE))
+        response.append(format_header(
+            common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
+        response.append(format_header(
+            common.SEC_WEBSOCKET_ACCEPT_HEADER, accept))
         # TODO(tyoshino): Encode value of protocol and extensions if any
         # special character that we have to encode by some manner.
         if self._request.ws_protocol is not None:
-            self._send_header(
+            response.append(format_header(
                 common.SEC_WEBSOCKET_PROTOCOL_HEADER,
-                self._request.ws_protocol)
+                self._request.ws_protocol))
         if self._request.ws_extensions is not None:
-            self._send_header(
+            response.append(format_header(
                 common.SEC_WEBSOCKET_EXTENSIONS_HEADER,
-                ', '.join(self._request.ws_extensions))
-        self._sendall('\r\n')
+                format_extensions(self._request.ws_extensions)))
+        response.append('\r\n')
+
+        raw_response = ''.join(response)
+        self._logger.debug('Opening handshake response: %r', raw_response)
+        self._request.connection.write(raw_response)
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/headerparserhandler.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/headerparserhandler.py
@@ -73,20 +73,46 @@ class ApacheLogHandler(logging.Handler):
         self.log_error = apache.log_error
         if request is not None:
             self.log_error = request.log_error
 
     def emit(self, record):
         apache_level = apache.APLOG_DEBUG
         if record.levelno in ApacheLogHandler._LEVELS:
             apache_level = ApacheLogHandler._LEVELS[record.levelno]
-        self.log_error(record.getMessage(), apache_level)
+
+        # "server" parameter must be passed to have "level" parameter work.
+        # If only "level" parameter is passed, nothing shows up on Apache's
+        # log. However, at this point, we cannot get the server object of the
+        # virtual host which will process WebSocket requests. The only server
+        # object we can get here is apache.main_server. But Wherever (server
+        # configuration context or virtual host context) we put
+        # PythonHeaderParserHandler directive, apache.main_server just points
+        # the main server instance (not any of virtual server instance). Then,
+        # Apache follows LogLevel directive in the server configuration context
+        # to filter logs. So, we need to specify LogLevel in the server
+        # configuration context. Even if we specify "LogLevel debug" in the
+        # virtual host context which actually handles WebSocket connections,
+        # DEBUG level logs never show up unless "LogLevel debug" is specified
+        # in the server configuration context.
+        #
+        # TODO(tyoshino): Provide logging methods on request object. When
+        # request is mp_request object (when used together with Apache), the
+        # methods call request.log_error indirectly. When request is
+        # _StandaloneRequest, the methods call Python's logging facility which
+        # we create in standalone.py.
+        self.log_error(record.getMessage(), apache_level, apache.main_server)
 
 
-logging.getLogger('mod_pywebsocket').addHandler(ApacheLogHandler())
+_LOGGER = logging.getLogger('mod_pywebsocket')
+# Logs are filtered by Apache based on LogLevel directive in Apache
+# configuration file. We must just pass logs for all levels to
+# ApacheLogHandler.
+_LOGGER.setLevel(logging.DEBUG)
+_LOGGER.addHandler(ApacheLogHandler())
 
 
 def _create_dispatcher():
     _HANDLER_ROOT = apache.main_server.get_options().get(
             _PYOPT_HANDLER_ROOT, None)
     if not _HANDLER_ROOT:
         raise Exception('PythonOption %s is not defined' % _PYOPT_HANDLER_ROOT,
                         apache.APLOG_ERR)
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/http_header_util.py
@@ -0,0 +1,213 @@
+# Copyright 2011, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"""Utilities for parsing and formatting headers that follow the grammar defined
+in HTTP RFC http://www.ietf.org/rfc/rfc2616.txt.
+"""
+
+
+_SEPARATORS = '()<>@,;:\\"/[]?={} \t'
+
+
+def _is_char(c):
+    """Returns true iff c is in CHAR as specified in HTTP RFC."""
+
+    return ord(c) <= 127
+
+
+def _is_ctl(c):
+    """Returns true iff c is in CTL as specified in HTTP RFC."""
+
+    return ord(c) <= 31 or ord(c) == 127
+
+
+class ParsingState(object):
+    def __init__(self, data):
+        self.data = data
+        self.head = 0
+
+
+def peek(state, pos=0):
+    """Peeks the character at pos from the head of data."""
+
+    if state.head + pos >= len(state.data):
+        return None
+
+    return state.data[state.head + pos]
+
+
+def consume(state, amount=1):
+    """Consumes specified amount of bytes from the head and returns the
+    consumed bytes. If there's not enough bytes to consume, returns None.
+    """
+
+    if state.head + amount > len(state.data):
+        return None
+
+    result = state.data[state.head:state.head + amount]
+    state.head = state.head + amount
+    return result
+
+
+def consume_string(state, expected):
+    """Given a parsing state and a expected string, consumes the string from
+    the head. Returns True if consumed successfully. Otherwise, returns False.
+    """
+
+    pos = 0
+
+    for c in expected:
+        if c != peek(state, pos):
+            return False
+        pos += 1
+
+    consume(state, pos)
+    return True
+
+
+def consume_lws(state):
+    """Consumes a LWS from the head. Returns True if any LWS is consumed.
+    Otherwise, returns False.
+
+    LWS = [CRLF] 1*( SP | HT )
+    """
+
+    original_head = state.head
+
+    consume_string(state, '\r\n')
+
+    pos = 0
+
+    while True:
+        c = peek(state, pos)
+        if c == ' ' or c == '\t':
+            pos += 1
+        else:
+            if pos == 0:
+                state.head = original_head
+                return False
+            else:
+                consume(state, pos)
+                return True
+
+
+def consume_lwses(state):
+    """Consumes *LWS from the head."""
+
+    while consume_lws(state):
+        pass
+
+
+def consume_token(state):
+    """Consumes a token from the head. Returns the token or None if no token
+    was found.
+    """
+
+    pos = 0
+
+    while True:
+        c = peek(state, pos)
+        if c is None or c in _SEPARATORS or _is_ctl(c) or not _is_char(c):
+            if pos == 0:
+                return None
+
+            return consume(state, pos)
+        else:
+            pos += 1
+
+
+def consume_token_or_quoted_string(state):
+    """Consumes a token or a quoted-string, and returns the token or unquoted
+    string. If no token or quoted-string was found, returns None.
+    """
+
+    original_head = state.head
+
+    if not consume_string(state, '"'):
+        return consume_token(state)
+
+    result = []
+
+    expect_quoted_pair = False
+
+    while True:
+        if not expect_quoted_pair and consume_lws(state):
+            result.append(' ')
+            continue
+
+        c = consume(state)
+        if c is None:
+            # quoted-string is not enclosed with double quotation
+            state.head = original_head
+            return None
+        elif expect_quoted_pair:
+            expect_quoted_pair = False
+            if _is_char(c):
+                result.append(c)
+            else:
+                # Non CHAR character found in quoted-pair
+                state.head = original_head
+                return None
+        elif c == '\\':
+            expect_quoted_pair = True
+        elif c == '"':
+            return ''.join(result)
+        elif _is_ctl(c):
+            # Invalid character %r found in qdtext
+            state.head = original_head
+            return None
+        else:
+            result.append(c)
+
+
+def quote_if_necessary(s):
+    """Quotes arbitrary string into quoted-string."""
+
+    quote = False
+    if s == '':
+        return '""'
+
+    result = []
+    for c in s:
+        if c == '"' or c in _SEPARATORS or _is_ctl(c) or not _is_char(c):
+            quote = True
+
+        if c == '"' or _is_ctl(c):
+            result.append('\\' + c)
+        else:
+            result.append(c)
+
+    if quote:
+        return '"' + ''.join(result) + '"';
+    else:
+        return ''.join(result)
+
+
+# vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/memorizingfile.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/memorizingfile.py
@@ -1,11 +1,11 @@
 #!/usr/bin/env python
 #
-# Copyright 2009, Google Inc.
+# Copyright 2011, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
 # met:
 #
 #     * Redistributions of source code must retain the above copyright
 # notice, this list of conditions and the following disclaimer.
@@ -41,25 +41,27 @@ import sys
 
 class MemorizingFile(object):
     """MemorizingFile wraps a file and memorizes lines read by readline.
 
     Note that data read by other methods are not memorized. This behavior
     is good enough for memorizing lines SimpleHTTPServer reads before
     the control reaches WebSocketRequestHandler.
     """
+
     def __init__(self, file_, max_memorized_lines=sys.maxint):
         """Construct an instance.
 
         Args:
             file_: the file object to wrap.
             max_memorized_lines: the maximum number of lines to memorize.
                 Only the first max_memorized_lines are memorized.
                 Default: sys.maxint.
         """
+
         self._file = file_
         self._memorized_lines = []
         self._max_memorized_lines = max_memorized_lines
 
     def __getattribute__(self, name):
         if name in ('_file', '_memorized_lines', '_max_memorized_lines',
                     'readline', 'get_memorized_lines'):
             return object.__getattribute__(self, name)
deleted file mode 100755
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/standalone.py
+++ /dev/null
@@ -1,476 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2011, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-"""Standalone WebSocket server.
-
-Use this server to run mod_pywebsocket without Apache HTTP Server.
-
-Usage:
-    python standalone.py [-p <ws_port>] [-w <websock_handlers>]
-                         [-s <scan_dir>]
-                         [-d <document_root>]
-                         [-m <websock_handlers_map_file>]
-                         ... for other options, see _main below ...
-
-<ws_port> is the port number to use for ws:// connection.
-
-<document_root> is the path to the root directory of HTML files.
-
-<websock_handlers> is the path to the root directory of WebSocket handlers.
-See __init__.py for details of <websock_handlers> and how to write WebSocket
-handlers. If this path is relative, <document_root> is used as the base.
-
-<scan_dir> is a path under the root directory. If specified, only the handlers
-under scan_dir are scanned. This is useful in saving scan time.
-
-Note:
-This server is derived from SocketServer.ThreadingMixIn. Hence a thread is
-used for each request.
-
-SECURITY WARNING: This uses CGIHTTPServer and CGIHTTPServer is not secure.
-It may execute arbitrary Python code or external programs. It should not be
-used outside a firewall.
-"""
-
-import BaseHTTPServer
-import CGIHTTPServer
-import SimpleHTTPServer
-import SocketServer
-import logging
-import logging.handlers
-import optparse
-import os
-import re
-import socket
-import sys
-
-_HAS_OPEN_SSL = False
-try:
-    import OpenSSL.SSL
-    _HAS_OPEN_SSL = True
-except ImportError:
-    pass
-
-from mod_pywebsocket import common
-from mod_pywebsocket import dispatch
-from mod_pywebsocket import handshake
-from mod_pywebsocket import memorizingfile
-from mod_pywebsocket import util
-
-
-_DEFAULT_LOG_MAX_BYTES = 1024 * 256
-_DEFAULT_LOG_BACKUP_COUNT = 5
-
-_DEFAULT_REQUEST_QUEUE_SIZE = 128
-
-# 1024 is practically large enough to contain WebSocket handshake lines.
-_MAX_MEMORIZED_LINES = 1024
-
-
-def _print_warnings_if_any(dispatcher):
-    warnings = dispatcher.source_warnings()
-    if warnings:
-        for warning in warnings:
-            logging.warning('mod_pywebsocket: %s' % warning)
-
-
-class _StandaloneConnection(object):
-    """Mimic mod_python mp_conn."""
-
-    def __init__(self, request_handler):
-        """Construct an instance.
-
-        Args:
-            request_handler: A WebSocketRequestHandler instance.
-        """
-        self._request_handler = request_handler
-
-    def get_local_addr(self):
-        """Getter to mimic mp_conn.local_addr."""
-        return (self._request_handler.server.server_name,
-                self._request_handler.server.server_port)
-    local_addr = property(get_local_addr)
-
-    def get_remote_addr(self):
-        """Getter to mimic mp_conn.remote_addr.
-
-        Setting the property in __init__ won't work because the request
-        handler is not initialized yet there."""
-        return self._request_handler.client_address
-    remote_addr = property(get_remote_addr)
-
-    def write(self, data):
-        """Mimic mp_conn.write()."""
-        return self._request_handler.wfile.write(data)
-
-    def read(self, length):
-        """Mimic mp_conn.read()."""
-        return self._request_handler.rfile.read(length)
-
-    def get_memorized_lines(self):
-        """Get memorized lines."""
-        return self._request_handler.rfile.get_memorized_lines()
-
-
-class _StandaloneRequest(object):
-    """Mimic mod_python request."""
-
-    def __init__(self, request_handler, use_tls):
-        """Construct an instance.
-
-        Args:
-            request_handler: A WebSocketRequestHandler instance.
-        """
-        self._request_handler = request_handler
-        self.connection = _StandaloneConnection(request_handler)
-        self._use_tls = use_tls
-
-    def get_uri(self):
-        """Getter to mimic request.uri."""
-        return self._request_handler.path
-    uri = property(get_uri)
-
-    def get_method(self):
-        """Getter to mimic request.method."""
-        return self._request_handler.command
-    method = property(get_method)
-
-    def get_headers_in(self):
-        """Getter to mimic request.headers_in."""
-        return self._request_handler.headers
-    headers_in = property(get_headers_in)
-
-    def is_https(self):
-        """Mimic request.is_https()."""
-        return self._use_tls
-
-
-class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
-    """HTTPServer specialized for WebSocket."""
-
-    SocketServer.ThreadingMixIn.daemon_threads = True
-    SocketServer.TCPServer.allow_reuse_address = True
-
-    def __init__(self, server_address, RequestHandlerClass):
-        """Override SocketServer.BaseServer.__init__."""
-
-        SocketServer.BaseServer.__init__(
-                self, server_address, RequestHandlerClass)
-        self.socket = self._create_socket()
-        self.server_bind()
-        self.server_activate()
-
-    def _create_socket(self):
-        socket_ = socket.socket(self.address_family, self.socket_type)
-        if WebSocketServer.options.use_tls:
-            ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
-            ctx.use_privatekey_file(WebSocketServer.options.private_key)
-            ctx.use_certificate_file(WebSocketServer.options.certificate)
-            socket_ = OpenSSL.SSL.Connection(ctx, socket_)
-        return socket_
-
-    def handle_error(self, rquest, client_address):
-        """Override SocketServer.handle_error."""
-
-        logging.error(
-            ('Exception in processing request from: %r' % (client_address,)) +
-            '\n' + util.get_stack_trace())
-        # Note: client_address is a tuple. To match it against %r, we need the
-        # trailing comma.
-
-
-class WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
-    """CGIHTTPRequestHandler specialized for WebSocket."""
-
-    def setup(self):
-        """Override SocketServer.StreamRequestHandler.setup to wrap rfile with
-        MemorizingFile.
-        """
-
-        # Call superclass's setup to prepare rfile, wfile, etc. See setup
-        # definition on the root class SocketServer.StreamRequestHandler to
-        # understand what this does.
-        CGIHTTPServer.CGIHTTPRequestHandler.setup(self)
-
-        self.rfile = memorizingfile.MemorizingFile(
-            self.rfile,
-            max_memorized_lines=_MAX_MEMORIZED_LINES)
-
-    def __init__(self, *args, **keywords):
-        self._request = _StandaloneRequest(
-                self, WebSocketRequestHandler.options.use_tls)
-        self._dispatcher = WebSocketRequestHandler.options.dispatcher
-        self._print_warnings_if_any()
-        self._handshaker = handshake.Handshaker(
-                self._request, self._dispatcher,
-                allowDraft75=WebSocketRequestHandler.options.allow_draft75,
-                strict=WebSocketRequestHandler.options.strict)
-        CGIHTTPServer.CGIHTTPRequestHandler.__init__(
-                self, *args, **keywords)
-
-    def _print_warnings_if_any(self):
-        warnings = self._dispatcher.source_warnings()
-        if warnings:
-            for warning in warnings:
-                logging.warning('mod_pywebsocket: %s' % warning)
-
-    def parse_request(self):
-        """Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request.
-
-        Return True to continue processing for HTTP(S), False otherwise.
-        """
-        result = CGIHTTPServer.CGIHTTPRequestHandler.parse_request(self)
-        if result:
-            try:
-                self._handshaker.do_handshake()
-                try:
-                    self._dispatcher.transfer_data(self._request)
-                except Exception, e:
-                    # Catch exception in transfer_data.
-                    # In this case, handshake has been successful, so just log
-                    # the exception and return False.
-                    logging.info('mod_pywebsocket: %s' % e)
-                    logging.info('mod_pywebsocket: %s' % util.get_stack_trace())
-                return False
-            except handshake.HandshakeError, e:
-                # Handshake for ws(s) failed. Assume http(s).
-                logging.info('mod_pywebsocket: %s' % e)
-                return True
-            except dispatch.DispatchError, e:
-                logging.warning('mod_pywebsocket: %s' % e)
-                return False
-            except Exception, e:
-                logging.warning('mod_pywebsocket: %s' % e)
-                logging.warning('mod_pywebsocket: %s' % util.get_stack_trace())
-                return False
-        return result
-
-    def log_request(self, code='-', size='-'):
-        """Override BaseHTTPServer.log_request."""
-
-        logging.info('"%s" %s %s',
-                     self.requestline, str(code), str(size))
-
-    def log_error(self, *args):
-        """Override BaseHTTPServer.log_error."""
-
-        # Despite the name, this method is for warnings than for errors.
-        # For example, HTTP status code is logged by this method.
-        logging.warn('%s - %s' % (self.address_string(), (args[0] % args[1:])))
-
-    def is_cgi(self):
-        """Test whether self.path corresponds to a CGI script.
-
-        Add extra check that self.path doesn't contains ..
-        Also check if the file is a executable file or not.
-        If the file is not executable, it is handled as static file or dir
-        rather than a CGI script.
-        """
-        if CGIHTTPServer.CGIHTTPRequestHandler.is_cgi(self):
-            if '..' in self.path:
-                return False
-            # strip query parameter from request path
-            resource_name = self.path.split('?', 2)[0]
-            # convert resource_name into real path name in filesystem.
-            scriptfile = self.translate_path(resource_name)
-            if not os.path.isfile(scriptfile):
-                return False
-            if not self.is_executable(scriptfile):
-                return False
-            return True
-        return False
-
-
-def _configure_logging(options):
-    logger = logging.getLogger()
-    logger.setLevel(logging.getLevelName(options.log_level.upper()))
-    if options.log_file:
-        handler = logging.handlers.RotatingFileHandler(
-                options.log_file, 'a', options.log_max, options.log_count)
-    else:
-        handler = logging.StreamHandler()
-    formatter = logging.Formatter(
-            '[%(asctime)s] [%(levelname)s] %(name)s: %(message)s')
-    handler.setFormatter(formatter)
-    logger.addHandler(handler)
-
-
-def _alias_handlers(dispatcher, websock_handlers_map_file):
-    """Set aliases specified in websock_handler_map_file in dispatcher.
-
-    Args:
-        dispatcher: dispatch.Dispatcher instance
-        websock_handler_map_file: alias map file
-    """
-    fp = open(websock_handlers_map_file)
-    try:
-        for line in fp:
-            if line[0] == '#' or line.isspace():
-                continue
-            m = re.match('(\S+)\s+(\S+)', line)
-            if not m:
-                logging.warning('Wrong format in map file:' + line)
-                continue
-            try:
-                dispatcher.add_resource_path_alias(
-                    m.group(1), m.group(2))
-            except dispatch.DispatchError, e:
-                logging.error(str(e))
-    finally:
-        fp.close()
-
-
-def _main():
-    parser = optparse.OptionParser()
-    parser.add_option('-H', '--server-host', '--server_host',
-                      dest='server_host',
-                      default='',
-                      help='server hostname to listen to')
-    parser.add_option('-p', '--port', dest='port', type='int',
-                      default=common.DEFAULT_WEB_SOCKET_PORT,
-                      help='port to listen to')
-    parser.add_option('-w', '--websock-handlers', '--websock_handlers',
-                      dest='websock_handlers',
-                      default='.',
-                      help='WebSocket handlers root directory.')
-    parser.add_option('-m', '--websock-handlers-map-file',
-                      '--websock_handlers_map_file',
-                      dest='websock_handlers_map_file',
-                      default=None,
-                      help=('WebSocket handlers map file. '
-                            'Each line consists of alias_resource_path and '
-                            'existing_resource_path, separated by spaces.'))
-    parser.add_option('-s', '--scan-dir', '--scan_dir', dest='scan_dir',
-                      default=None,
-                      help=('WebSocket handlers scan directory. '
-                            'Must be a directory under websock_handlers.'))
-    parser.add_option('-d', '--document-root', '--document_root',
-                      dest='document_root', default='.',
-                      help='Document root directory.')
-    parser.add_option('-x', '--cgi-paths', '--cgi_paths', dest='cgi_paths',
-                      default=None,
-                      help=('CGI paths relative to document_root.'
-                            'Comma-separated. (e.g -x /cgi,/htbin) '
-                            'Files under document_root/cgi_path are handled '
-                            'as CGI programs. Must be executable.'))
-    parser.add_option('-t', '--tls', dest='use_tls', action='store_true',
-                      default=False, help='use TLS (wss://)')
-    parser.add_option('-k', '--private-key', '--private_key',
-                      dest='private_key',
-                      default='', help='TLS private key file.')
-    parser.add_option('-c', '--certificate', dest='certificate',
-                      default='', help='TLS certificate file.')
-    parser.add_option('-l', '--log-file', '--log_file', dest='log_file',
-                      default='', help='Log file.')
-    parser.add_option('--log-level', '--log_level', type='choice',
-                      dest='log_level', default='warn',
-                      choices=['debug', 'info', 'warning', 'warn', 'error',
-                               'critical'],
-                      help='Log level.')
-    parser.add_option('--log-max', '--log_max', dest='log_max', type='int',
-                      default=_DEFAULT_LOG_MAX_BYTES,
-                      help='Log maximum bytes')
-    parser.add_option('--log-count', '--log_count', dest='log_count',
-                      type='int', default=_DEFAULT_LOG_BACKUP_COUNT,
-                      help='Log backup count')
-    parser.add_option('--allow-draft75', dest='allow_draft75',
-                      action='store_true', default=False,
-                      help='Allow draft 75 handshake')
-    parser.add_option('--strict', dest='strict', action='store_true',
-                      default=False, help='Strictly check handshake request')
-    parser.add_option('-q', '--queue', dest='request_queue_size', type='int',
-                      default=_DEFAULT_REQUEST_QUEUE_SIZE,
-                      help='request queue size')
-    options = parser.parse_args()[0]
-
-    os.chdir(options.document_root)
-
-    _configure_logging(options)
-
-    SocketServer.TCPServer.request_queue_size = options.request_queue_size
-    CGIHTTPServer.CGIHTTPRequestHandler.cgi_directories = []
-
-    if options.cgi_paths:
-        CGIHTTPServer.CGIHTTPRequestHandler.cgi_directories = \
-            options.cgi_paths.split(',')
-        if sys.platform in ('cygwin', 'win32'):
-            cygwin_path = None
-            # For Win32 Python, it is expected that CYGWIN_PATH
-            # is set to a directory of cygwin binaries.
-            # For example, websocket_server.py in Chromium sets CYGWIN_PATH to
-            # full path of third_party/cygwin/bin.
-            if 'CYGWIN_PATH' in os.environ:
-                cygwin_path = os.environ['CYGWIN_PATH']
-            util.wrap_popen3_for_win(cygwin_path)
-            def __check_script(scriptpath):
-                return util.get_script_interp(scriptpath, cygwin_path)
-            CGIHTTPServer.executable = __check_script
-
-    if options.use_tls:
-        if not _HAS_OPEN_SSL:
-            logging.critical('To use TLS, install pyOpenSSL.')
-            sys.exit(1)
-        if not options.private_key or not options.certificate:
-            logging.critical(
-                    'To use TLS, specify private_key and certificate.')
-            sys.exit(1)
-
-    if not options.scan_dir:
-        options.scan_dir = options.websock_handlers
-
-    try:
-        # Share a Dispatcher among request handlers to save time for
-        # instantiation.  Dispatcher can be shared because it is thread-safe.
-        options.dispatcher = dispatch.Dispatcher(options.websock_handlers,
-                                                 options.scan_dir)
-        if options.websock_handlers_map_file:
-            _alias_handlers(options.dispatcher,
-                            options.websock_handlers_map_file)
-        _print_warnings_if_any(options.dispatcher)
-
-        WebSocketRequestHandler.options = options
-        WebSocketServer.options = options
-
-        server = WebSocketServer((options.server_host, options.port),
-                                 WebSocketRequestHandler)
-        server.serve_forever()
-    except Exception, e:
-        logging.critical('mod_pywebsocket: %s' % e)
-        logging.critical('mod_pywebsocket: %s' % util.get_stack_trace())
-        sys.exit(1)
-
-
-if __name__ == '__main__':
-    _main()
-
-
-# vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/util.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/util.py
@@ -1,9 +1,9 @@
-# Copyright 2009, Google Inc.
+# Copyright 2011, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
 # met:
 #
 #     * Redistributions of source code must retain the above copyright
 # notice, this list of conditions and the following disclaimer.
@@ -113,43 +113,51 @@ def get_script_interp(script_path, cygwi
     fp = open(script_path)
     line = fp.readline()
     fp.close()
     m = re.match('^#!(.*)', line)
     if m:
         return __translate_interp(m.group(1), cygwin_path)
     return None
 
+
 def wrap_popen3_for_win(cygwin_path):
     """Wrap popen3 to support #!-script on Windows.
 
     Args:
       cygwin_path:  path for cygwin binary if command path is needed to be
                     translated.  None if no translation required.
     """
+
     __orig_popen3 = os.popen3
+
     def __wrap_popen3(cmd, mode='t', bufsize=-1):
         cmdline = cmd.split(' ')
         interp = get_script_interp(cmdline[0], cygwin_path)
         if interp:
             cmd = interp + ' ' + cmd
         return __orig_popen3(cmd, mode, bufsize)
+
     os.popen3 = __wrap_popen3
 
 
 def hexify(s):
     return ' '.join(map(lambda x: '%02x' % ord(x), s))
 
 
 def get_class_logger(o):
     return logging.getLogger(
         '%s.%s' % (o.__class__.__module__, o.__class__.__name__))
 
 
 class NoopMasker(object):
+    """A masking object that has the same interface as RepeatedXorMasker but
+    just returns the string passed in without making any change.
+    """
+
     def __init__(self):
         pass
 
     def mask(self, s):
         return s
 
 
 class RepeatedXorMasker(object):
@@ -200,130 +208,185 @@ class DeflateRequest(object):
 # For decompression, we can just use 32K to cover any windows size. For
 # compression, we use 32K so receivers must use 32K.
 #
 # Compression level is Z_DEFAULT_COMPRESSION. We don't have to match level
 # to decode.
 #
 # See zconf.h, deflate.cc, inflate.cc of zlib library, and zlibmodule.c of
 # Python. See also RFC1950 (ZLIB 3.3).
+
+
+class _Deflater(object):
+    def __init__(self):
+        self._logger = get_class_logger(self)
+
+        self._compress = zlib.compressobj(
+            zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
+
+    def compress_and_flush(self, bytes):
+        compressed_bytes = self._compress.compress(bytes)
+        compressed_bytes += self._compress.flush(zlib.Z_SYNC_FLUSH)
+        self._logger.debug('Compress input %r', bytes)
+        self._logger.debug('Compress result %r', compressed_bytes)
+        return compressed_bytes
+
+
+class _Inflater(object):
+    def __init__(self):
+        self._logger = get_class_logger(self)
+
+        self._unconsumed = ''
+
+        self.reset()
+
+    def decompress(self, size):
+        if not (size == -1 or size > 0):
+            raise Exception('size must be -1 or positive')
+
+        data = ''
+
+        while True:
+            if size == -1:
+                data += self._decompress.decompress(self._unconsumed)
+                # See Python bug http://bugs.python.org/issue12050 to
+                # understand why the same code cannot be used for updating
+                # self._unconsumed for here and else block.
+                self._unconsumed = ''
+            else:
+                data += self._decompress.decompress(
+                    self._unconsumed, size - len(data))
+                self._unconsumed = self._decompress.unconsumed_tail
+            if self._decompress.unused_data:
+                # Encountered a last block (i.e. a block with BFINAL = 1) and
+                # found a new stream (unused_data). We cannot use the same
+                # zlib.Decompress object for the new stream. Create a new
+                # Decompress object to decompress the new one.
+                #
+                # It's fine to ignore unconsumed_tail if unused_data is not
+                # empty.
+                self._unconsumed = self._decompress.unused_data
+                self.reset()
+                if size >= 0 and len(data) == size:
+                    # data is filled. Don't call decompress again.
+                    break
+                else:
+                    # Re-invoke Decompress.decompress to try to decompress all
+                    # available bytes before invoking read which blocks until
+                    # any new byte is available.
+                    continue
+            else:
+                # Here, since unused_data is empty, even if unconsumed_tail is
+                # not empty, bytes of requested length are already in data. We
+                # don't have to "continue" here.
+                break
+
+        if data:
+            self._logger.debug('Decompressed %r', data)
+        return data
+
+    def append(self, data):
+        self._logger.debug('Appended %r', data)
+        self._unconsumed += data
+
+    def reset(self):
+        self._logger.debug('Reset')
+        self._decompress = zlib.decompressobj(-zlib.MAX_WBITS)
+
+
 class DeflateSocket(object):
     """A wrapper class for socket object to intercept send and recv to perform
     deflate compression and decompression transparently.
     """
 
     # Size of the buffer passed to recv to receive compressed data.
     _RECV_SIZE = 4096
 
     def __init__(self, socket):
         self._socket = socket
 
-        self._logger = logging.getLogger(
-            'mod_pywebsocket.util.DeflateSocket')
+        self._logger = get_class_logger(self)
 
-        self._compress = zlib.compressobj(
-            zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-        self._decompress = zlib.decompressobj(-zlib.MAX_WBITS)
-        self._unconsumed = ''
+        self._deflater = _Deflater()
+        self._inflater = _Inflater()
 
     def recv(self, size):
+        """Receives data from the socket specified on the construction up
+        to the specified size. Once any data is available, returns it even if
+        it's smaller than the specified size.
+        """
+
         # TODO(tyoshino): Allow call with size=0. It should block until any
         # decompressed data is available.
         if size <= 0:
-           raise Exception('Non-positive size passed')
-        data = ''
+            raise Exception('Non-positive size passed')
         while True:
-            data += self._decompress.decompress(
-                self._unconsumed, size - len(data))
-            self._unconsumed = self._decompress.unconsumed_tail
-            if self._decompress.unused_data:
-                raise Exception('Non-decompressible data found: %r' %
-                                self._decompress.unused_data)
+            data = self._inflater.decompress(size)
             if len(data) != 0:
-                break
+                return data
 
             read_data = self._socket.recv(DeflateSocket._RECV_SIZE)
-            self._logger.debug('Received compressed: %r' % read_data)
             if not read_data:
-                break
-            self._unconsumed += read_data
-        self._logger.debug('Received: %r' % data)
-        return data
+                return ''
+            self._inflater.append(read_data)
 
     def sendall(self, bytes):
         self.send(bytes)
 
     def send(self, bytes):
-        compressed_bytes = self._compress.compress(bytes)
-        compressed_bytes += self._compress.flush(zlib.Z_SYNC_FLUSH)
-        self._socket.sendall(compressed_bytes)
-        self._logger.debug('Wrote: %r' % bytes)
-        self._logger.debug('Wrote compressed: %r' % compressed_bytes)
+        self._socket.sendall(self._deflater.compress_and_flush(bytes))
         return len(bytes)
 
 
 class DeflateConnection(object):
     """A wrapper class for request object to intercept write and read to
     perform deflate compression and decompression transparently.
     """
 
-    # Size of the buffer passed to recv to receive compressed data.
-    _RECV_SIZE = 4096
-
     def __init__(self, connection):
         self._connection = connection
 
-        self._logger = logging.getLogger(
-            'mod_pywebsocket.util.DeflateConnection')
+        self._logger = get_class_logger(self)
 
-        self._compress = zlib.compressobj(
-            zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
-        self._decompress = zlib.decompressobj(-zlib.MAX_WBITS)
-        self._unconsumed = ''
+        self._deflater = _Deflater()
+        self._inflater = _Inflater()
 
     def put_bytes(self, bytes):
         self.write(bytes)
 
     def read(self, size=-1):
+        """Reads at most size bytes. Blocks until there's at least one byte
+        available.
+        """
+
         # TODO(tyoshino): Allow call with size=0.
-        if size == 0 or size < -1:
-           raise Exception('size must be -1 or positive')
+        if not (size == -1 or size > 0):
+            raise Exception('size must be -1 or positive')
 
         data = ''
         while True:
-            if size < 0:
-                data += self._decompress.decompress(self._unconsumed)
+            if size == -1:
+                data += self._inflater.decompress(-1)
             else:
-                data += self._decompress.decompress(
-                    self._unconsumed, size - len(data))
-            self._unconsumed = self._decompress.unconsumed_tail
-            if self._decompress.unused_data:
-                raise Exception('Non-decompressible data found: %r' %
-                                self._decompress.unused_data)
+                data += self._inflater.decompress(size - len(data))
 
             if size >= 0 and len(data) != 0:
                 break
 
             # TODO(tyoshino): Make this read efficient by some workaround.
             #
             # In 3.0.3 and prior of mod_python, read blocks until length bytes
             # was read. We don't know the exact size to read while using
             # deflate, so read byte-by-byte.
             #
             # _StandaloneRequest.read that ultimately performs
             # socket._fileobject.read also blocks until length bytes was read
             read_data = self._connection.read(1)
-            self._logger.debug('Read compressed: %r' % read_data)
             if not read_data:
                 break
-            self._unconsumed += read_data
-        self._logger.debug('Read: %r' % data)
+            self._inflater.append(read_data)
         return data
 
     def write(self, bytes):
-        compressed_bytes = self._compress.compress(bytes)
-        compressed_bytes += self._compress.flush(zlib.Z_SYNC_FLUSH)
-        self._logger.debug('Wrote compressed: %r' % compressed_bytes)
-        self._logger.debug('Wrote: %r' % bytes)
-        self._connection.write(compressed_bytes)
+        self._connection.write(self._deflater.compress_and_flush(bytes))
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/standalone.py
+++ b/testing/mochitest/pywebsocket/standalone.py
@@ -172,21 +172,24 @@ class _StandaloneRequest(object):
     def is_https(self):
         """Mimic request.is_https()."""
         return self._use_tls
 
 
 class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
     """HTTPServer specialized for WebSocket."""
 
-    SocketServer.ThreadingMixIn.daemon_threads = True
-    SocketServer.TCPServer.allow_reuse_address = True
+    daemon_threads = True
+    allow_reuse_address = True
 
     def __init__(self, server_address, RequestHandlerClass):
-        """Override SocketServer.BaseServer.__init__."""
+        """Override SocketServer.TCPServer.__init__ to set SSL enabled socket
+        object to self.socket before server_bind and server_activate, if
+        necessary.
+        """
 
         SocketServer.BaseServer.__init__(
                 self, server_address, RequestHandlerClass)
         self.socket = self._create_socket()
         self.server_bind()
         self.server_activate()
 
     def _create_socket(self):
@@ -254,17 +257,18 @@ class WebSocketRequestHandler(CGIHTTPSer
                 self._handshaker.do_handshake()
                 try:
                     self._dispatcher.transfer_data(self._request)
                 except Exception, e:
                     # Catch exception in transfer_data.
                     # In this case, handshake has been successful, so just log
                     # the exception and return False.
                     logging.info('mod_pywebsocket: %s' % e)
-                    logging.info('mod_pywebsocket: %s' % util.get_stack_trace())
+                    logging.info(
+                        'mod_pywebsocket: %s' % util.get_stack_trace())
                 return False
             except handshake.HandshakeError, e:
                 # Handshake for ws(s) failed. Assume http(s).
                 logging.info('mod_pywebsocket: %s' % e)
                 return True
             except dispatch.DispatchError, e:
                 logging.warning('mod_pywebsocket: %s' % e)
                 return False
@@ -426,18 +430,20 @@ def _main():
             cygwin_path = None
             # For Win32 Python, it is expected that CYGWIN_PATH
             # is set to a directory of cygwin binaries.
             # For example, websocket_server.py in Chromium sets CYGWIN_PATH to
             # full path of third_party/cygwin/bin.
             if 'CYGWIN_PATH' in os.environ:
                 cygwin_path = os.environ['CYGWIN_PATH']
             util.wrap_popen3_for_win(cygwin_path)
+
             def __check_script(scriptpath):
                 return util.get_script_interp(scriptpath, cygwin_path)
+
             CGIHTTPServer.executable = __check_script
 
     if options.use_tls:
         if not _HAS_OPEN_SSL:
             logging.critical('To use TLS, install pyOpenSSL.')
             sys.exit(1)
         if not options.private_key or not options.certificate:
             logging.critical(