Bug 689006: Upgrade pywebsocket to binary API (version 553, also supports Python > 2.5) r=mcmanus
authorJason Duell <jduell.mcbugs@gmail.com>
Thu, 15 Dec 2011 15:21:55 -0800
changeset 82722 508b748801b73f2abc667deb65bf2d05590b578c
parent 82721 557f2ae6e8aaba6d68965f5137cc4752da85716e
child 82723 95b76549de825df7fed3ed90f4331adc823b8544
push id4082
push userjduell@mozilla.com
push dateThu, 15 Dec 2011 23:27:03 +0000
treeherdermozilla-inbound@95b76549de82 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs689006
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 689006: Upgrade pywebsocket to binary API (version 553, also supports Python > 2.5) r=mcmanus
testing/mochitest/Makefile.in
testing/mochitest/pywebsocket/README
testing/mochitest/pywebsocket/README-MOZILLA
testing/mochitest/pywebsocket/mod_pywebsocket/__init__.py
testing/mochitest/pywebsocket/mod_pywebsocket/_stream_base.py
testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hixie75.py
testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hybi.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/extensions.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/hybi.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/msgutil.py
testing/mochitest/pywebsocket/mod_pywebsocket/stream.py
testing/mochitest/pywebsocket/mod_pywebsocket/util.py
testing/mochitest/pywebsocket/standalone.py
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -103,33 +103,34 @@ include $(topsrcdir)/build/automation-bu
 _PYWEBSOCKET_FILES = \
 		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/extensions.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/_stream_hybi.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 \
-		pywebsocket/mod_pywebsocket/handshake/hybi06.py \
+		pywebsocket/mod_pywebsocket/handshake/hybi.py \
 		$(NULL)
 
 _DEST_DIR = $(DEPTH)/_tests/$(relativesrcdir)
 
 libs:: 
 	(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - mochijar) | (cd $(_DEST_DIR) && tar -xf -)
 
 libs:: $(_PYWEBSOCKET_FILES)
--- a/testing/mochitest/pywebsocket/README
+++ b/testing/mochitest/pywebsocket/README
@@ -1,64 +1,9 @@
-mod_pywebsocket http://pywebsocket.googlecode.com/svn
-version 489
-supporting ietf-07
-
-includes the following minor patch:: (first bit supports symlinked wsh
-files, the second allows python 2.5 to work)
-
-also includes patch for 663096 to drain input buffers before closing
-in order to avoid RST
-
-also updates blindly version 7 to be version 8 until upstream makes
-real version 8 available
-
-also includes changeset 491 from mod_pywebsocket repo - necessary to
-enable wss:// testing
+Install this package by:
+$ python setup.py build
+$ sudo python setup.py install
 
-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.
-     The input path can use either '\\' or '/' as the separator.
-     The normalized path always uses '/' regardless of the platform.
-     """
- 
-     path = path.replace('\\', os.path.sep)
--    path = os.path.realpath(path)
-+    # do not normalize away symlinks in mochitest
-+    # path = os.path.realpath(path)
-     path = path.replace('\\', '/')
-     return path
- 
- 
- def _create_path_to_resource_converter(base_dir):
-     base_dir = _normalize_path(base_dir)
- 
-     base_len = len(base_dir)
+If you're going to use this package as a normal user, run this instead:
+$ python setup.py install --user
 
-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:
+Then read document by:
+$ pydoc mod_pywebsocket
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/pywebsocket/README-MOZILLA
@@ -0,0 +1,51 @@
+This pywebsocket code is mostly unchanged from the source at
+
+  svn checkout http://pywebsocket.googlecode.com/svn/trunk/ pywebsocket-read-only
+
+The current Mozilla code is based on
+
+  svnversion:  553 (AKA pywebsocket version='0.6b1')
+
+--------------------------------------------------------------------------------
+STEPS TO UPDATE MOZILLA TO NEWER PYWEBSOCKET VERSION
+--------------------------------------------------------------------------------
+- Get new pywebsocket checkout from googlecode (into, for instance, 'src')
+
+  svn checkout http://pywebsocket.googlecode.com/svn/trunk/ pywebsocket-read-only
+
+- Export a version w/o SVN files:
+
+    svn export src dist
+
+- rsync new version into our tree, deleting files that aren't needed any more
+  (NOTE: this will blow away this file!  hg revert it or keep a copy.)
+
+    rsync -r --delete dist/ $MOZ_SRC/testing/mochitest/pywebsocket
+
+- Manually move the 'standalone.py' file from the mmod_pywebsocket/ directory to
+  the parent directory (not sure why we moved it: probably no reason)
+
+- hg add/rm appropriate files, and add/remove them from _MOD_PYWEBSOCKET_FILES
+  and/or _HANDSHAKE_FILES in testing/mochitest/Makefile.am
+
+- Edit the _normalize_path() function in dispatch.py and MAKE SURE THIS LINE IS
+  COMMENTED OUT:
+
+    # MOZILLA: do not normalize away symlinks in mochitest
+    # path = os.path.realpath(path)
+
+- There's also some code in mod_pywebsocket/_stream_base.py that may or may not
+  need to change to support Python 2.5:
+
+    #raise ConnectionTerminatedException(
+    #    'Receiving %d byte failed. Peer (%r) closed connection' %
+    #    (length, (self._request.connection.remote_addr,)))
+    raise ConnectionTerminatedException('connection terminated: read failed')
+
+- Test and make sure the code works:
+
+    make mochitest-plain TEST_PATH=content/base/test/test_websocket.html
+
+- If this doesn't take a look at the pywebsocket server log,
+  $OBJDIR/_tests/testing/mochitest/websock.log
+
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/__init__.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/__init__.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.
@@ -71,98 +71,108 @@ 1. Specify the following Apache HTTP Ser
 
        <IfModule python_module>
          PythonPath "sys.path+['/websock_lib']"
          PythonOption mod_pywebsocket.handler_root /websock_handlers
          PythonHeaderParserHandler mod_pywebsocket.headerparserhandler
        </IfModule>
 
 2. Tune Apache parameters for serving WebSocket. We'd like to note that at
-   least TimeOut directive from core features and RequestReadTimeout directive
-   from mod_reqtimeout should be modified not to kill connections in only a few
-   seconds of idle time.
+   least TimeOut directive from core features and RequestReadTimeout
+   directive from mod_reqtimeout should be modified not to kill connections
+   in only a few seconds of idle time.
 
 3. Verify installation. You can use example/console.html to poke the server.
 
 
 Writing WebSocket handlers:
 
 When a WebSocket request comes in, the resource name
 specified in the handshake is considered as if it is a file path under
 <websock_handlers> and the handler defined in
 <websock_handlers>/<resource_name>_wsh.py is invoked.
 
 For example, if the resource name is /example/chat, the handler defined in
 <websock_handlers>/example/chat_wsh.py is invoked.
 
-A WebSocket handler is composed of the following two functions:
+A WebSocket handler is composed of the following three functions:
 
     web_socket_do_extra_handshake(request)
     web_socket_transfer_data(request)
+    web_socket_passive_closing_handshake(request)
 
 where:
     request: mod_python request.
 
 web_socket_do_extra_handshake is called during the handshake after the
 headers are successfully parsed and WebSocket properties (ws_location,
 ws_origin, and ws_resource) are added to request. A handler
 can reject the request by raising an exception.
 
-A request object has the following properties that you can use during the extra
-handshake (web_socket_do_extra_handshake):
+A request object has the following properties that you can use during the
+extra handshake (web_socket_do_extra_handshake):
 - ws_resource
 - ws_origin
 - ws_version
 - ws_location (Hixie 75 and HyBi 00 only)
 - ws_extensions (Hybi 06 and later)
 - ws_deflate (HyBi 06 and later)
 - ws_protocol
 - ws_requested_protocols (HyBi 06 and later)
 
 The last two are a bit tricky.
 
 For HyBi 06 and later, ws_protocol is always set to None when
 web_socket_do_extra_handshake is called. If ws_requested_protocols is not
-None, you must choose one subprotocol from this list and set it to ws_protocol.
+None, you must choose one subprotocol from this list and set it to
+ws_protocol.
 
 For Hixie 75 and HyBi 00, when web_socket_do_extra_handshake is called,
-ws_protocol is set to the value given by the client in Sec-WebSocket-Protocol
-(WebSocket-Protocol for Hixie 75) header or None if such header was not found
-in the opening handshake request. Finish extra handshake with ws_protocol
-untouched to accept the request subprotocol. Then, Sec-WebSocket-Protocol
-(or WebSocket-Protocol) header will be sent to the client in response with the
-same value as requested. Raise an exception in web_socket_do_extra_handshake to
-reject the requested subprotocol.
+ws_protocol is set to the value given by the client in
+Sec-WebSocket-Protocol (WebSocket-Protocol for Hixie 75) header or None if
+such header was not found in the opening handshake request. Finish extra
+handshake with ws_protocol untouched to accept the request subprotocol.
+Then, Sec-WebSocket-Protocol (or WebSocket-Protocol) header will be sent to
+the client in response with the same value as requested. Raise an exception
+in web_socket_do_extra_handshake to reject the requested subprotocol.
 
 web_socket_transfer_data is called after the handshake completed
 successfully. A handler can receive/send messages from/to the client
 using request. mod_pywebsocket.msgutil module provides utilities
 for data transfer.
 
 You can receive a message by the following statement.
 
     message = request.ws_stream.receive_message()
 
-This call blocks until any complete text frame arrives, and the payload data of
-the incoming frame will be stored into message. When you're using IETF HyBi 00
-or later protocol, receive_message() will return None on receiving
+This call blocks until any complete text frame arrives, and the payload data
+of the incoming frame will be stored into message. When you're using IETF
+HyBi 00 or later protocol, receive_message() will return None on receiving
 client-initiated closing handshake. When any error occurs, receive_message()
 will raise some exception.
 
 You can send a message by the following statement.
 
     request.ws_stream.send_message(message)
 
 Executing the following statement or just return-ing from
 web_socket_transfer_data cause connection close.
 
     request.ws_stream.close_connection()
 
 When you're using IETF HyBi 00 or later protocol, close_connection will wait
-for closing handshake acknowledgement coming from the client. When it couldn't
-receive a valid acknowledgement, raises an exception.
+for closing handshake acknowledgement coming from the client. When it
+couldn't receive a valid acknowledgement, raises an exception.
+
+web_socket_passive_closing_handshake is called after the server receives
+incoming closing frame from the client peer immediately. You can specify
+code and reason by return values. They are sent as a outgoing closing frame
+from the server. A request object has the following properties that you can
+use in web_socket_passive_closing_handshake.
+- ws_close_code
+- ws_close_reason
 
 A WebSocket handler must be thread-safe if the server (Apache or
 standalone.py) is configured to use threads.
 """
 
 
 # vi:sts=4 sw=4 et tw=72
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_base.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_base.py
@@ -38,43 +38,57 @@
 # request.write/read are not suitable because they don't allow direct raw bytes
 # writing/reading.
 
 
 from mod_pywebsocket import util
 
 
 # Exceptions
+
+
 class ConnectionTerminatedException(Exception):
     """This exception will be raised when a connection is terminated
     unexpectedly.
     """
+
     pass
 
 
 class InvalidFrameException(ConnectionTerminatedException):
     """This exception will be raised when we received an invalid frame we
     cannot parse.
     """
+
     pass
 
 
-class BadOperationException(RuntimeError):
+class BadOperationException(Exception):
     """This exception will be raised when send_message() is called on
     server-terminated connection or receive_message() is called on
     client-terminated connection.
     """
+
     pass
 
 
-class UnsupportedFrameException(RuntimeError):
+class UnsupportedFrameException(Exception):
     """This exception will be raised when we receive a frame with flag, opcode
     we cannot handle. Handlers can just catch and ignore this exception and
     call receive_message() again to continue processing the next frame.
     """
+
+    pass
+
+
+class InvalidUTF8Exception(Exception):
+    """This exception will be raised when we receive a text frame which
+    contains invalid UTF-8 strings.
+    """
+
     pass
 
 
 class StreamBase(object):
     """Base stream class."""
 
     def __init__(self, request):
         """Construct an instance.
@@ -92,16 +106,23 @@ 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:
+            # MOZILLA: Patrick McManus found we needed this for Python 2.5 to
+            # work.  Not sure which tests he meant: I found that
+            # content/base/test/test_websocket* all worked fine with 2.5 with
+            # the original Google code. JDuell
+            #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.
         """
 
@@ -124,22 +145,16 @@ class StreamBase(object):
 
         bytes = []
         while length > 0:
             new_bytes = self._read(length)
             bytes.append(new_bytes)
             length -= len(new_bytes)
         return ''.join(bytes)
 
-    def flushread(self):
-        try:
-          self._request.connection.flushread()
-        except:
-          pass
-
     def _read_until(self, delim_char):
         """Reads bytes until we encounter delim_char. The result will not
         contain delim_char.
 
         Raises:
             ConnectionTerminatedException: when read returns empty string.
         """
 
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hixie75.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hixie75.py
@@ -59,31 +59,36 @@ class StreamHixie75(StreamBase):
 
         self._logger = util.get_class_logger(self)
 
         self._enable_closing_handshake = enable_closing_handshake
 
         self._request.client_terminated = False
         self._request.server_terminated = False
 
-    def send_message(self, message, end=True):
+    def send_message(self, message, end=True, binary=False):
         """Send message.
 
         Args:
             message: unicode string to send.
+            binary: not used in hixie75.
 
         Raises:
             BadOperationException: when called on a server-terminated
                 connection.
         """
 
         if not end:
             raise BadOperationException(
                 'StreamHixie75 doesn\'t support send_message with end=False')
 
+        if binary:
+            raise BadOperationException(
+                'StreamHixie75 doesn\'t support send_message with binary=True')
+
         if self._request.server_terminated:
             raise BadOperationException(
                 'Requested send_message after sending out a closing handshake')
 
         self._write(''.join(['\x00', message.encode('utf-8'), '\xff']))
 
     def _read_payload_length_hixie75(self):
         """Reads a length header in a Hixie75 version frame with length.
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hybi.py
@@ -0,0 +1,647 @@
+# 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.
+
+
+"""Stream class for IETF HyBi latest WebSocket protocol.
+"""
+
+
+from collections import deque
+import os
+import struct
+
+from mod_pywebsocket import common
+from mod_pywebsocket import util
+from mod_pywebsocket._stream_base import BadOperationException
+from mod_pywebsocket._stream_base import ConnectionTerminatedException
+from mod_pywebsocket._stream_base import InvalidFrameException
+from mod_pywebsocket._stream_base import InvalidUTF8Exception
+from mod_pywebsocket._stream_base import StreamBase
+from mod_pywebsocket._stream_base import UnsupportedFrameException
+
+
+_NOOP_MASKER = util.NoopMasker()
+
+
+class Frame(object):
+
+    def __init__(self, fin=1, rsv1=0, rsv2=0, rsv3=0,
+                 opcode=None, payload=''):
+        self.fin = fin
+        self.rsv1 = rsv1
+        self.rsv2 = rsv2
+        self.rsv3 = rsv3
+        self.opcode = opcode
+        self.payload = payload
+
+
+# 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:
+        ValueError: when bad data is given.
+    """
+
+    if mask:
+        mask_bit = 1 << 7
+    else:
+        mask_bit = 0
+
+    if length < 0:
+        raise ValueError('length must be non negative integer')
+    elif length <= 125:
+        return chr(mask_bit | length)
+    elif length < (1 << 16):
+        return chr(mask_bit | 126) + struct.pack('!H', length)
+    elif length < (1 << 63):
+        return chr(mask_bit | 127) + struct.pack('!Q', length)
+    else:
+        raise ValueError('Payload is too big for one frame')
+
+
+def create_header(opcode, payload_length, fin, rsv1, rsv2, rsv3, mask):
+    """Creates a frame header.
+
+    Raises:
+        Exception: when bad data is given.
+    """
+
+    if opcode < 0 or 0xf < opcode:
+        raise ValueError('Opcode out of range')
+
+    if payload_length < 0 or (1 << 63) <= payload_length:
+        raise ValueError('payload_length out of range')
+
+    if (fin | rsv1 | rsv2 | rsv3) & ~1:
+        raise ValueError('FIN bit and Reserved bit parameter must be 0 or 1')
+
+    header = ''
+
+    first_byte = ((fin << 7)
+                  | (rsv1 << 6) | (rsv2 << 5) | (rsv3 << 4)
+                  | opcode)
+    header += chr(first_byte)
+    header += create_length_header(payload_length, mask)
+
+    return header
+
+
+def _build_frame(header, body, mask):
+    if not mask:
+        return header + body
+
+    masking_nonce = os.urandom(4)
+    masker = util.RepeatedXorMasker(masking_nonce)
+
+    return header + masking_nonce + masker.mask(body)
+
+
+def _filter_and_format_frame_object(frame, mask, frame_filters):
+    for frame_filter in frame_filters:
+        frame_filter.filter(frame)
+
+    header = create_header(
+        frame.opcode, len(frame.payload), frame.fin,
+        frame.rsv1, frame.rsv2, frame.rsv3, mask)
+    return _build_frame(header, frame.payload, mask)
+
+
+def create_binary_frame(
+    message, opcode=common.OPCODE_BINARY, fin=1, mask=False, frame_filters=[]):
+    """Creates a simple binary frame with no extension, reserved bit."""
+
+    frame = Frame(fin=fin, opcode=opcode, payload=message)
+    return _filter_and_format_frame_object(frame, mask, frame_filters)
+
+
+def create_text_frame(
+    message, opcode=common.OPCODE_TEXT, fin=1, mask=False, frame_filters=[]):
+    """Creates a simple text frame with no extension, reserved bit."""
+
+    encoded_message = message.encode('utf-8')
+    return create_binary_frame(encoded_message, opcode, fin, mask,
+                               frame_filters)
+
+
+class FragmentedFrameBuilder(object):
+    """A stateful class to send a message as fragments."""
+
+    def __init__(self, mask, frame_filters=[]):
+        """Constructs an instance."""
+
+        self._mask = mask
+        self._frame_filters = frame_filters
+
+        self._started = False
+
+        # Hold opcode of the first frame in messages to verify types of other
+        # frames in the message are all the same.
+        self._opcode = common.OPCODE_TEXT
+
+    def build(self, message, end, binary):
+        if binary:
+            frame_type = common.OPCODE_BINARY
+        else:
+            frame_type = common.OPCODE_TEXT
+        if self._started:
+            if self._opcode != frame_type:
+                raise ValueError('Message types are different in frames for '
+                                 'the same message')
+            opcode = common.OPCODE_CONTINUATION
+        else:
+            opcode = frame_type
+            self._opcode = frame_type
+
+        if end:
+            self._started = False
+            fin = 1
+        else:
+            self._started = True
+            fin = 0
+
+        if binary:
+            return create_binary_frame(
+                message, opcode, fin, self._mask, self._frame_filters)
+        else:
+            return create_text_frame(
+                message, opcode, fin, self._mask, self._frame_filters)
+
+
+def _create_control_frame(opcode, body, mask, frame_filters):
+    frame = Frame(opcode=opcode, payload=body)
+
+    return _filter_and_format_frame_object(frame, mask, frame_filters)
+
+
+def create_ping_frame(body, mask=False, frame_filters=[]):
+    return _create_control_frame(common.OPCODE_PING, body, mask, frame_filters)
+
+
+def create_pong_frame(body, mask=False, frame_filters=[]):
+    return _create_control_frame(common.OPCODE_PONG, body, mask, frame_filters)
+
+
+def create_close_frame(body, mask=False, frame_filters=[]):
+    return _create_control_frame(
+        common.OPCODE_CLOSE, body, mask, frame_filters)
+
+
+class StreamOptions(object):
+    """Holds option values to configure Stream objects."""
+
+    def __init__(self):
+        """Constructs StreamOptions."""
+
+        # Enables deflate-stream extension.
+        self.deflate_stream = False
+
+        # Filters applied to frames.
+        self.outgoing_frame_filters = []
+        self.incoming_frame_filters = []
+
+        self.mask_send = False
+        self.unmask_receive = True
+
+
+class Stream(StreamBase):
+    """Stream of WebSocket messages."""
+
+    def __init__(self, request, options):
+        """Constructs an instance.
+
+        Args:
+            request: mod_python request.
+        """
+
+        StreamBase.__init__(self, request)
+
+        self._logger = util.get_class_logger(self)
+
+        self._options = options
+
+        if self._options.deflate_stream:
+            self._logger.debug('Setup filter for deflate-stream')
+            self._request = util.DeflateRequest(self._request)
+
+        self._request.client_terminated = False
+        self._request.server_terminated = False
+
+        # Holds body of received fragments.
+        self._received_fragments = []
+        # Holds the opcode of the first fragment.
+        self._original_opcode = None
+
+        self._writer = FragmentedFrameBuilder(
+            self._options.mask_send, self._options.outgoing_frame_filters)
+
+        self._ping_queue = deque()
+
+    def _receive_frame(self):
+        """Receives a frame and return data in the frame as a tuple containing
+        each header field and payload separately.
+
+        Raises:
+            ConnectionTerminatedException: when read returns empty
+                string.
+            InvalidFrameException: when the frame contains invalid data.
+        """
+
+        received = self.receive_bytes(2)
+
+        first_byte = ord(received[0])
+        fin = (first_byte >> 7) & 1
+        rsv1 = (first_byte >> 6) & 1
+        rsv2 = (first_byte >> 5) & 1
+        rsv3 = (first_byte >> 4) & 1
+        opcode = first_byte & 0xf
+
+        second_byte = ord(received[1])
+        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:
+            extended_payload_length = self.receive_bytes(2)
+            payload_length = struct.unpack(
+                '!H', extended_payload_length)[0]
+
+        if mask == 1:
+            masking_nonce = self.receive_bytes(4)
+            masker = util.RepeatedXorMasker(masking_nonce)
+        else:
+            masker = _NOOP_MASKER
+
+        bytes = masker.mask(self.receive_bytes(payload_length))
+
+        return opcode, bytes, fin, rsv1, rsv2, rsv3
+
+    def _receive_frame_as_frame_object(self):
+        opcode, bytes, fin, rsv1, rsv2, rsv3 = self._receive_frame()
+
+        return Frame(fin=fin, rsv1=rsv1, rsv2=rsv2, rsv3=rsv3,
+                     opcode=opcode, payload=bytes)
+
+    def send_message(self, message, end=True, binary=False):
+        """Send message.
+
+        Args:
+            message: text in unicode or binary in str to send.
+            binary: send message as binary frame.
+
+        Raises:
+            BadOperationException: when called on a server-terminated
+                connection or called with inconsistent message type or binary
+                parameter.
+        """
+
+        if self._request.server_terminated:
+            raise BadOperationException(
+                'Requested send_message after sending out a closing handshake')
+
+        if binary and isinstance(message, unicode):
+            raise BadOperationException(
+                'Message for binary frame must be instance of str')
+
+        try:
+            self._write(self._writer.build(message, end, binary))
+        except ValueError, e:
+            raise BadOperationException(e)
+
+    def receive_message(self):
+        """Receive a WebSocket frame and return its payload as a text in
+        unicode or a binary in str.
+
+        Returns:
+            payload data of the frame
+            - as unicode instance if received text frame
+            - as str instance if received binary frame
+            or None iff received closing handshake.
+        Raises:
+            BadOperationException: when called on a client-terminated
+                connection.
+            ConnectionTerminatedException: when read returns empty
+                string.
+            InvalidFrameException: when the frame contains invalid
+                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')
+
+        while True:
+            # mp_conn.read will block if no bytes are available.
+            # Timeout is controlled by TimeOut directive of Apache.
+
+            frame = self._receive_frame_as_frame_object()
+
+            for frame_filter in self._options.incoming_frame_filters:
+                frame_filter.filter(frame)
+
+            if frame.rsv1 or frame.rsv2 or frame.rsv3:
+                raise UnsupportedFrameException(
+                    'Unsupported flag is set (rsv = %d%d%d)' %
+                    (frame.rsv1, frame.rsv2, frame.rsv3))
+
+            if frame.opcode == common.OPCODE_CONTINUATION:
+                if not self._received_fragments:
+                    if frame.fin:
+                        raise InvalidFrameException(
+                            'Received a termination frame but fragmentation '
+                            'not started')
+                    else:
+                        raise InvalidFrameException(
+                            'Received an intermediate frame but '
+                            'fragmentation not started')
+
+                if frame.fin:
+                    # End of fragmentation frame
+                    self._received_fragments.append(frame.payload)
+                    message = ''.join(self._received_fragments)
+                    self._received_fragments = []
+                else:
+                    # Intermediate frame
+                    self._received_fragments.append(frame.payload)
+                    continue
+            else:
+                if self._received_fragments:
+                    if frame.fin:
+                        raise InvalidFrameException(
+                            'Received an unfragmented frame without '
+                            'terminating existing fragmentation')
+                    else:
+                        raise InvalidFrameException(
+                            'New fragmentation started without terminating '
+                            'existing fragmentation')
+
+                if frame.fin:
+                    # Unfragmented frame
+
+                    if (common.is_control_opcode(frame.opcode) and
+                        len(frame.payload) > 125):
+                        raise InvalidFrameException(
+                            'Application data size of control frames must be '
+                            '125 bytes or less')
+
+                    self._original_opcode = frame.opcode
+                    message = frame.payload
+                else:
+                    # Start of fragmentation frame
+
+                    if common.is_control_opcode(frame.opcode):
+                        raise InvalidFrameException(
+                            'Control frames must not be fragmented')
+
+                    self._original_opcode = frame.opcode
+                    self._received_fragments.append(frame.payload)
+                    continue
+
+            if self._original_opcode == common.OPCODE_TEXT:
+                # The WebSocket protocol section 4.4 specifies that invalid
+                # characters must be replaced with U+fffd REPLACEMENT
+                # CHARACTER.
+                try:
+                    return message.decode('utf-8')
+                except UnicodeDecodeError, e:
+                    raise InvalidUTF8Exception(e)
+            elif self._original_opcode == common.OPCODE_BINARY:
+                return message
+            elif self._original_opcode == common.OPCODE_CLOSE:
+                self._request.client_terminated = True
+
+                # Status code is optional. We can have status reason only if we
+                # have status code. Status reason can be empty string. So,
+                # allowed cases are
+                # - no application data: no code no reason
+                # - 2 octet of application data: has code but no reason
+                # - 3 or more octet of application data: both code and reason
+                if len(message) == 1:
+                    raise InvalidFrameException(
+                        'If a close frame has status code, the length of '
+                        'status code must be 2 octet')
+                elif len(message) >= 2:
+                    self._request.ws_close_code = struct.unpack(
+                        '!H', message[0:2])[0]
+                    self._request.ws_close_reason = message[2:].decode(
+                        'utf-8', 'replace')
+                    self._logger.debug(
+                        'Received close frame (code=%d, reason=%r)',
+                        self._request.ws_close_code,
+                        self._request.ws_close_reason)
+
+                # Drain junk data after the close frame if necessary.
+                self._drain_received_data()
+
+                if self._request.server_terminated:
+                    self._logger.debug(
+                        'Received ack for server-initiated closing '
+                        'handshake')
+                    return None
+
+                self._logger.debug(
+                    'Received client-initiated closing handshake')
+
+                code = common.STATUS_NORMAL
+                reason = ''
+                if hasattr(self._request, '_dispatcher'):
+                    dispatcher = self._request._dispatcher
+                    code, reason = dispatcher.passive_closing_handshake(
+                        self._request)
+                self._send_closing_handshake(code, reason)
+                self._logger.debug(
+                    'Sent ack for client-initiated closing handshake')
+                return None
+            elif self._original_opcode == common.OPCODE_PING:
+                try:
+                    handler = self._request.on_ping_handler
+                    if handler:
+                        handler(self._request, message)
+                        continue
+                except AttributeError, e:
+                    pass
+                self._send_pong(message)
+            elif self._original_opcode == common.OPCODE_PONG:
+                # TODO(tyoshino): Add ping timeout handling.
+
+                inflight_pings = deque()
+
+                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))
+                            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')
+                        break
+
+                try:
+                    handler = self._request.on_pong_handler
+                    if handler:
+                        handler(self._request, message)
+                        continue
+                except AttributeError, e:
+                    pass
+
+                continue
+            else:
+                raise UnsupportedFrameException(
+                    'Opcode %d is not supported' % self._original_opcode)
+
+    def _send_closing_handshake(self, code, reason):
+        if code >= (1 << 16) or code < 0:
+            raise BadOperationException('Status code is out of range')
+
+        encoded_reason = reason.encode('utf-8')
+        if len(encoded_reason) + 2 > 125:
+            raise BadOperationException(
+                'Application data size of close frames must be 125 bytes or '
+                'less')
+
+        frame = create_close_frame(
+            struct.pack('!H', code) + encoded_reason,
+            self._options.mask_send,
+            self._options.outgoing_frame_filters)
+
+        self._request.server_terminated = True
+
+        self._write(frame)
+
+    def close_connection(self, code=common.STATUS_NORMAL, reason=''):
+        """Closes a WebSocket connection."""
+
+        if self._request.server_terminated:
+            self._logger.debug(
+                'Requested close_connection but server is already terminated')
+            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.
+            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()
+        if message is not None:
+            raise ConnectionTerminatedException(
+                'Didn\'t receive valid ack for closing handshake')
+        # TODO: 3. close the WebSocket connection.
+        # note: mod_python Connection (mp_conn) doesn't have close method.
+
+    def send_ping(self, body=''):
+        if len(body) > 125:
+            raise ValueError(
+                'Application data size of control frames must be 125 bytes or '
+                'less')
+        frame = create_ping_frame(
+            body,
+            self._options.mask_send,
+            self._options.outgoing_frame_filters)
+        self._write(frame)
+
+        self._ping_queue.append(body)
+
+    def _send_pong(self, body):
+        if len(body) > 125:
+            raise ValueError(
+                'Application data size of control frames must be 125 bytes or '
+                'less')
+        frame = create_pong_frame(
+            body,
+            self._options.mask_send,
+            self._options.outgoing_frame_filters)
+        self._write(frame)
+
+    def _drain_received_data(self):
+        """Drains unread data in the receive buffer to avoid sending out TCP
+        RST packet. This is because when deflate-stream is enabled, some
+        DEFLATE block for flushing data may follow a close frame. If any data
+        remains in the receive buffer of a socket when the socket is closed,
+        it sends out TCP RST packet to the other peer.
+
+        Since mod_python's mp_conn object doesn't support non-blocking read,
+        we perform this only when pywebsocket is running in standalone mode.
+        """
+
+        # If self._options.deflate_stream is true, self._request is
+        # DeflateRequest, so we can get wrapped request object by
+        # self._request._request.
+        #
+        # Only _StandaloneRequest has _drain_received_data method.
+        if (self._options.deflate_stream and
+            ('_drain_received_data' in dir(self._request._request))):
+            self._request._request._drain_received_data()
+
+
+# vi:sts=4 sw=4 et
deleted file mode 100644
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hybi06.py
+++ /dev/null
@@ -1,523 +0,0 @@
-# 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.
-
-
-"""Stream class for IETF HyBi 07 WebSocket protocol.
-"""
-
-
-from collections import deque
-import os
-import struct
-
-from mod_pywebsocket import common
-from mod_pywebsocket import util
-from mod_pywebsocket._stream_base import BadOperationException
-from mod_pywebsocket._stream_base import ConnectionTerminatedException
-from mod_pywebsocket._stream_base import InvalidFrameException
-from mod_pywebsocket._stream_base import StreamBase
-from mod_pywebsocket._stream_base import UnsupportedFrameException
-
-
-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:
-        ValueError: when bad data is given.
-    """
-
-    if mask:
-        mask_bit = 1 << 7
-    else:
-        mask_bit = 0
-
-    if length < 0:
-        raise ValueError('length must be non negative integer')
-    elif length <= 125:
-        return chr(mask_bit | length)
-    elif length < (1 << 16):
-        return chr(mask_bit | 126) + struct.pack('!H', length)
-    elif length < (1 << 63):
-        return chr(mask_bit | 127) + struct.pack('!Q', length)
-    else:
-        raise ValueError('Payload is too big for one frame')
-
-
-def create_header(opcode, payload_length, fin, rsv1, rsv2, rsv3, mask):
-    """Creates a frame header.
-
-    Raises:
-        Exception: when bad data is given.
-    """
-
-    if opcode < 0 or 0xf < opcode:
-        raise ValueError('Opcode out of range')
-
-    if payload_length < 0 or (1 << 63) <= payload_length:
-        raise ValueError('payload_length out of range')
-
-    if (fin | rsv1 | rsv2 | rsv3) & ~1:
-        raise ValueError('FIN bit and Reserved bit parameter must be 0 or 1')
-
-    header = ''
-
-    first_byte = ((fin << 7)
-                  | (rsv1 << 6) | (rsv2 << 5) | (rsv3 << 4)
-                  | opcode)
-    header += chr(first_byte)
-    header += create_length_header(payload_length, mask)
-
-    return header
-
-
-def _build_frame(header, body, mask):
-    if not mask:
-        return header + body
-
-    masking_nonce = os.urandom(4)
-    masker = util.RepeatedXorMasker(masking_nonce)
-
-    return header + masking_nonce + masker.mask(body)
-
-
-def create_text_frame(message, opcode=common.OPCODE_TEXT, fin=1, mask=False):
-    """Creates a simple text frame with no extension, reserved bit."""
-
-    encoded_message = message.encode('utf-8')
-    header = create_header(opcode, len(encoded_message), fin, 0, 0, 0, mask)
-    return _build_frame(header, encoded_message, mask)
-
-
-class FragmentedTextFrameBuilder(object):
-    """A stateful class to send a message as fragments."""
-
-    def __init__(self, mask):
-        """Constructs an instance."""
-
-        self._mask = mask
-
-        self._started = False
-
-    def build(self, message, end):
-        if self._started:
-            opcode = common.OPCODE_CONTINUATION
-        else:
-            opcode = common.OPCODE_TEXT
-
-        if end:
-            self._started = False
-            fin = 1
-        else:
-            self._started = True
-            fin = 0
-
-        return create_text_frame(message, opcode, fin, self._mask)
-
-
-def create_ping_frame(body, mask=False):
-    header = create_header(common.OPCODE_PING, len(body), 1, 0, 0, 0, mask)
-    return _build_frame(header, body, mask)
-
-
-def create_pong_frame(body, mask=False):
-    header = create_header(common.OPCODE_PONG, len(body), 1, 0, 0, 0, mask)
-    return _build_frame(header, body, mask)
-
-
-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."""
-
-    def __init__(self, request, options):
-        """Constructs an instance.
-
-        Args:
-            request: mod_python request.
-        """
-
-        StreamBase.__init__(self, request)
-
-        self._logger = util.get_class_logger(self)
-
-        self._options = options
-
-        if self._options.deflate:
-            self._logger.debug('Deflated stream')
-            self._request = util.DeflateRequest(self._request)
-
-        self._request.client_terminated = False
-        self._request.server_terminated = False
-
-        # Holds body of received fragments.
-        self._received_fragments = []
-        # Holds the opcode of the first fragment.
-        self._original_opcode = None
-
-        self._writer = FragmentedTextFrameBuilder(self._options.mask_send)
-
-        self._ping_queue = deque()
-
-    def _receive_frame(self):
-        """Receives a frame and return data in the frame as a tuple containing
-        each header field and payload separately.
-
-        Raises:
-            ConnectionTerminatedException: when read returns empty
-                string.
-            InvalidFrameException: when the frame contains invalid data.
-        """
-
-        received = self.receive_bytes(2)
-
-        first_byte = ord(received[0])
-        fin = (first_byte >> 7) & 1
-        rsv1 = (first_byte >> 6) & 1
-        rsv2 = (first_byte >> 5) & 1
-        rsv3 = (first_byte >> 4) & 1
-        opcode = first_byte & 0xf
-
-        second_byte = ord(received[1])
-        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:
-            extended_payload_length = self.receive_bytes(2)
-            payload_length = struct.unpack(
-                '!H', extended_payload_length)[0]
-
-        if mask == 1:
-            masking_nonce = self.receive_bytes(4)
-            masker = util.RepeatedXorMasker(masking_nonce)
-        else:
-            masker = _NOOP_MASKER
-
-        bytes = masker.mask(self.receive_bytes(payload_length))
-
-        return opcode, bytes, fin, rsv1, rsv2, rsv3
-
-    def send_message(self, message, end=True):
-        """Send message.
-
-        Args:
-            message: unicode string to send.
-
-        Raises:
-            BadOperationException: when called on a server-terminated
-                connection.
-        """
-
-        if self._request.server_terminated:
-            raise BadOperationException(
-                'Requested send_message after sending out a closing handshake')
-
-        self._write(self._writer.build(message, end))
-
-    def receive_message(self):
-        """Receive a WebSocket frame and return its payload an unicode string.
-
-        Returns:
-            payload unicode string in a WebSocket frame. None iff received
-            closing handshake.
-        Raises:
-            BadOperationException: when called on a client-terminated
-                connection.
-            ConnectionTerminatedException: when read returns empty
-                string.
-            InvalidFrameException: when the frame contains invalid
-                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')
-
-        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(
-                    'Unsupported flag is set (rsv = %d%d%d)' %
-                    (rsv1, rsv2, rsv3))
-
-            if opcode == common.OPCODE_CONTINUATION:
-                if not self._received_fragments:
-                    if fin:
-                        raise InvalidFrameException(
-                            'Received a termination frame but fragmentation '
-                            'not started')
-                    else:
-                        raise InvalidFrameException(
-                            'Received an intermediate frame but '
-                            'fragmentation not started')
-
-                if fin:
-                    # End of fragmentation frame
-                    self._received_fragments.append(bytes)
-                    message = ''.join(self._received_fragments)
-                    self._received_fragments = []
-                else:
-                    # Intermediate frame
-                    self._received_fragments.append(bytes)
-                    continue
-            else:
-                if self._received_fragments:
-                    if fin:
-                        raise InvalidFrameException(
-                            'Received an unfragmented frame without '
-                            'terminating existing fragmentation')
-                    else:
-                        raise InvalidFrameException(
-                            'New fragmentation started without terminating '
-                            'existing fragmentation')
-
-                if fin:
-                    # Unfragmented frame
-                    self._original_opcode = opcode
-                    message = bytes
-
-                    if is_control_opcode(opcode) and len(message) > 125:
-                        raise InvalidFrameException(
-                            'Application data size of control frames must be '
-                            '125 bytes or less')
-                else:
-                    # Start of fragmentation frame
-
-                    if is_control_opcode(opcode):
-                        raise InvalidFrameException(
-                            'Control frames must not be fragmented')
-
-                    self._original_opcode = opcode
-                    self._received_fragments.append(bytes)
-                    continue
-
-            if self._original_opcode == common.OPCODE_TEXT:
-                # The WebSocket protocol section 4.4 specifies that invalid
-                # characters must be replaced with U+fffd REPLACEMENT
-                # CHARACTER.
-                return message.decode('utf-8', 'replace')
-            elif self._original_opcode == common.OPCODE_CLOSE:
-                self._request.client_terminated = True
-
-                # Status code is optional. We can have status reason only if we
-                # have status code. Status reason can be empty string. So,
-                # allowed cases are
-                # - no application data: no code no reason
-                # - 2 octet of application data: has code but no reason
-                # - 3 or more octet of application data: both code and reason
-                if len(message) == 1:
-                    raise InvalidFrameException(
-                        'If a close frame has status code, the length of '
-                        'status code must be 2 octet')
-                elif len(message) >= 2:
-                    self._request.ws_close_code = struct.unpack(
-                        '!H', message[0:2])[0]
-                    self._request.ws_close_reason = message[2:].decode(
-                        'utf-8', 'replace')
-
-                self._logger.debug('Initiated flush read')
-                self.flushread()
-
-                if self._request.server_terminated:
-                    self._logger.debug(
-                        'Received ack for server-initiated closing '
-                        'handshake')
-                    return None
-
-                self._logger.debug(
-                    'Received client-initiated closing handshake')
-
-                self._send_closing_handshake(common.STATUS_NORMAL, '')
-                self._logger.debug(
-                    'Sent ack for client-initiated closing handshake')
-                return None
-            elif self._original_opcode == common.OPCODE_PING:
-                try:
-                    handler = self._request.on_ping_handler
-                    if handler:
-                        handler(self._request, message)
-                        continue
-                except AttributeError, e:
-                    pass
-                self._send_pong(message)
-            elif self._original_opcode == common.OPCODE_PONG:
-                # TODO(tyoshino): Add ping timeout handling.
-
-                inflight_pings = deque()
-
-                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))
-                            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')
-                        break
-
-                try:
-                    handler = self._request.on_pong_handler
-                    if handler:
-                        handler(self._request, message)
-                        continue
-                except AttributeError, e:
-                    pass
-
-                continue
-            else:
-                raise UnsupportedFrameException(
-                    'Opcode %d is not supported' % self._original_opcode)
-
-    def _send_closing_handshake(self, code, reason):
-        if code >= (1 << 16) or code < 0:
-            raise BadOperationException('Status code is out of range')
-
-        encoded_reason = reason.encode('utf-8')
-        if len(encoded_reason) + 2 > 125:
-            raise BadOperationException(
-                'Application data size of close frames must be 125 bytes or '
-                'less')
-
-        frame = create_close_frame(
-            struct.pack('!H', code) + encoded_reason, self._options.mask_send)
-
-        self._request.server_terminated = True
-
-        self._write(frame)
-
-    def close_connection(self, code=common.STATUS_NORMAL, reason=''):
-        """Closes a WebSocket connection."""
-
-        if self._request.server_terminated:
-            self._logger.debug(
-                'Requested close_connection but server is already terminated')
-            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.
-            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()
-        if message is not None:
-            raise ConnectionTerminatedException(
-                'Didn\'t receive valid ack for closing handshake')
-        # TODO: 3. close the WebSocket connection.
-        # note: mod_python Connection (mp_conn) doesn't have close method.
-
-    def send_ping(self, body=''):
-        if len(body) > 125:
-            raise ValueError(
-                'Application data size of control frames must be 125 bytes or '
-                'less')
-        frame = create_ping_frame(body, self._options.mask_send)
-        self._write(frame)
-
-        self._ping_queue.append(body)
-
-    def _send_pong(self, body):
-        if len(body) > 125:
-            raise ValueError(
-                'Application data size of control frames must be 125 bytes or '
-                'less')
-        frame = create_pong_frame(body, self._options.mask_send)
-        self._write(frame)
-
-
-# vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/common.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/common.py
@@ -24,19 +24,31 @@
 # 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.
 
 
 # Constants indicating WebSocket protocol version.
-VERSION_HYBI07 = 8
+VERSION_HIXIE75 = -1
 VERSION_HYBI00 = 0
-VERSION_HIXIE75 = -1
+VERSION_HYBI01 = 1
+VERSION_HYBI02 = 2
+VERSION_HYBI03 = 2
+VERSION_HYBI04 = 4
+VERSION_HYBI05 = 5
+VERSION_HYBI06 = 6
+VERSION_HYBI07 = 7
+VERSION_HYBI08 = 8
+VERSION_HYBI09 = 8
+VERSION_HYBI10 = 8
+
+# Constants indicating WebSocket protocol latest version.
+VERSION_HYBI_LATEST = VERSION_HYBI10
 
 # Port numbers
 DEFAULT_WEB_SOCKET_PORT = 80
 DEFAULT_WEB_SOCKET_SECURE_PORT = 443
 
 # Schemes
 WEB_SOCKET_SCHEME = 'ws'
 WEB_SOCKET_SECURE_SCHEME = 'wss'
@@ -44,34 +56,90 @@ WEB_SOCKET_SECURE_SCHEME = 'wss'
 # Frame opcodes defined in the spec.
 OPCODE_CONTINUATION = 0x0
 OPCODE_TEXT = 0x1
 OPCODE_BINARY = 0x2
 OPCODE_CLOSE = 0x8
 OPCODE_PING = 0x9
 OPCODE_PONG = 0xa
 
-# UUIDs used by HyBi 07 opening handshake and frame masking.
+# UUIDs used by HyBi 04 and later opening handshake and frame masking.
 WEBSOCKET_ACCEPT_UUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
 
 # Opening handshake header names and expected values.
 UPGRADE_HEADER = 'Upgrade'
 WEBSOCKET_UPGRADE_TYPE = 'websocket'
 WEBSOCKET_UPGRADE_TYPE_HIXIE75 = 'WebSocket'
 CONNECTION_HEADER = 'Connection'
 UPGRADE_CONNECTION_TYPE = 'Upgrade'
 HOST_HEADER = 'Host'
+ORIGIN_HEADER = 'Origin'
 SEC_WEBSOCKET_ORIGIN_HEADER = 'Sec-WebSocket-Origin'
 SEC_WEBSOCKET_KEY_HEADER = 'Sec-WebSocket-Key'
 SEC_WEBSOCKET_ACCEPT_HEADER = 'Sec-WebSocket-Accept'
 SEC_WEBSOCKET_VERSION_HEADER = 'Sec-WebSocket-Version'
 SEC_WEBSOCKET_PROTOCOL_HEADER = 'Sec-WebSocket-Protocol'
 SEC_WEBSOCKET_EXTENSIONS_HEADER = 'Sec-WebSocket-Extensions'
+SEC_WEBSOCKET_DRAFT_HEADER = 'Sec-WebSocket-Draft'
+SEC_WEBSOCKET_KEY1_HEADER = 'Sec-WebSocket-Key1'
+SEC_WEBSOCKET_KEY2_HEADER = 'Sec-WebSocket-Key2'
+SEC_WEBSOCKET_LOCATION_HEADER = 'Sec-WebSocket-Location'
+
+# Extensions
+DEFLATE_STREAM_EXTENSION = 'deflate-stream'
+DEFLATE_FRAME_EXTENSION = 'deflate-frame'
 
 # Status codes
+# Code STATUS_CODE_NOT_AVAILABLE should not be used in actual frames. This code
+# is exposed to JavaScript API as pseudo status code which represent actual
+# frame does not have status code.
 STATUS_NORMAL = 1000
 STATUS_GOING_AWAY = 1001
 STATUS_PROTOCOL_ERROR = 1002
 STATUS_UNSUPPORTED = 1003
 STATUS_TOO_LARGE = 1004
+STATUS_CODE_NOT_AVAILABLE = 1005
+STATUS_ABNORMAL_CLOSE = 1006
+STATUS_INVALID_UTF8 = 1007
+
+
+def is_control_opcode(opcode):
+    return (opcode >> 3) == 1
+
+
+class ExtensionParameter(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_parameters(self):
+        return self._parameters
+
+    def get_parameter_names(self):
+        return [name for name, unused_value in self._parameters]
+
+    def has_parameter(self, name):
+        for param_name, param_value in self._parameters:
+            if param_name == name:
+                return True
+        return False
+
+    def get_parameter_value(self, name):
+        for param_name, param_value in self._parameters:
+            if param_name == name:
+                return param_value
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py
@@ -32,46 +32,59 @@
 """
 
 
 import logging
 import os
 import re
 
 from mod_pywebsocket import common
+from mod_pywebsocket import handshake
 from mod_pywebsocket import msgutil
+from mod_pywebsocket import stream
 from mod_pywebsocket import util
 
 
 _SOURCE_PATH_PATTERN = re.compile(r'(?i)_wsh\.py$')
 _SOURCE_SUFFIX = '_wsh.py'
 _DO_EXTRA_HANDSHAKE_HANDLER_NAME = 'web_socket_do_extra_handshake'
 _TRANSFER_DATA_HANDLER_NAME = 'web_socket_transfer_data'
+_PASSIVE_CLOSING_HANDSHAKE_HANDLER_NAME = (
+    'web_socket_passive_closing_handshake')
 
 
-class DispatchError(Exception):
+class DispatchException(Exception):
     """Exception in dispatching WebSocket request."""
 
-    pass
+    def __init__(self, name, status=404):
+        super(DispatchException, self).__init__(name)
+        self.status = status
+
+
+def _default_passive_closing_handshake_handler(request):
+    """Default web_socket_passive_closing_handshake handler."""
+    return common.STATUS_NORMAL, ''
 
 
 def _normalize_path(path):
     """Normalize path.
 
     Args:
         path: the path to normalize.
 
     Path is converted to the absolute path.
     The input path can use either '\\' or '/' as the separator.
     The normalized path always uses '/' regardless of the platform.
     """
 
     path = path.replace('\\', os.path.sep)
-    # do not normalize away symlinks in mochitest
-    # path = os.path.realpath(path)
+
+    # MOZILLA: do not normalize away symlinks in mochitest
+    #path = os.path.realpath(path)
+
     path = path.replace('\\', '/')
     return path
 
 
 def _create_path_to_resource_converter(base_dir):
     base_dir = _normalize_path(base_dir)
 
     base_len = len(base_dir)
@@ -98,50 +111,60 @@ def _enumerate_handler_file_paths(direct
             path = os.path.join(root, base)
             if _SOURCE_PATH_PATTERN.search(path):
                 yield path
 
 
 class _HandlerSuite(object):
     """A handler suite holder class."""
 
-    def __init__(self, do_extra_handshake, transfer_data):
+    def __init__(self, do_extra_handshake, transfer_data,
+                 passive_closing_handshake):
         self.do_extra_handshake = do_extra_handshake
         self.transfer_data = transfer_data
+        self.passive_closing_handshake = passive_closing_handshake
 
 
 def _source_handler_file(handler_definition):
     """Source a handler definition string.
 
     Args:
         handler_definition: a string containing Python statements that define
                             handler functions.
     """
 
     global_dic = {}
     try:
         exec handler_definition in global_dic
     except Exception:
-        raise DispatchError('Error in sourcing handler:' +
-                            util.get_stack_trace())
+        raise DispatchException('Error in sourcing handler:' +
+                                util.get_stack_trace())
+    passive_closing_handshake_handler = None
+    try:
+        passive_closing_handshake_handler = _extract_handler(
+            global_dic, _PASSIVE_CLOSING_HANDSHAKE_HANDLER_NAME)
+    except Exception:
+        passive_closing_handshake_handler = (
+            _default_passive_closing_handshake_handler)
     return _HandlerSuite(
         _extract_handler(global_dic, _DO_EXTRA_HANDSHAKE_HANDLER_NAME),
-        _extract_handler(global_dic, _TRANSFER_DATA_HANDLER_NAME))
+        _extract_handler(global_dic, _TRANSFER_DATA_HANDLER_NAME),
+        passive_closing_handshake_handler)
 
 
 def _extract_handler(dic, name):
     """Extracts a callable with the specified name from the given dictionary
     dic.
     """
 
     if name not in dic:
-        raise DispatchError('%s is not defined.' % name)
+        raise DispatchException('%s is not defined.' % name)
     handler = dic[name]
     if not callable(handler):
-        raise DispatchError('%s is not callable.' % name)
+        raise DispatchException('%s is not callable.' % name)
     return handler
 
 
 class Dispatcher(object):
     """Dispatches WebSocket requests.
 
     This class maintains a map from resource name to handlers.
     """
@@ -149,31 +172,32 @@ class Dispatcher(object):
     def __init__(self, root_dir, scan_dir=None):
         """Construct an instance.
 
         Args:
             root_dir: The directory where handler definition files are
                       placed.
             scan_dir: The directory where handler definition files are
                       searched. scan_dir must be a directory under root_dir,
-                      including root_dir itself.  If scan_dir is None, root_dir
-                      is used as scan_dir. scan_dir can be useful in saving
-                      scan time when root_dir contains many subdirectories.
+                      including root_dir itself.  If scan_dir is None,
+                      root_dir is used as scan_dir. scan_dir can be useful
+                      in saving scan time when root_dir contains many
+                      subdirectories.
         """
 
         self._logger = util.get_class_logger(self)
 
         self._handler_suite_map = {}
         self._source_warnings = []
         if scan_dir is None:
             scan_dir = root_dir
         if not os.path.realpath(scan_dir).startswith(
                 os.path.realpath(root_dir)):
-            raise DispatchError('scan_dir:%s must be a directory under '
-                                'root_dir:%s.' % (scan_dir, root_dir))
+            raise DispatchException('scan_dir:%s must be a directory under '
+                                    'root_dir:%s.' % (scan_dir, root_dir))
         self._source_handler_files_in_dir(root_dir, scan_dir)
 
     def add_resource_path_alias(self,
                                 alias_resource_path, existing_resource_path):
         """Add resource path alias.
 
         Once added, request to alias_resource_path would be handled by
         handler registered for existing_resource_path.
@@ -181,102 +205,140 @@ class Dispatcher(object):
         Args:
             alias_resource_path: alias resource path
             existing_resource_path: existing resource path
         """
         try:
             handler_suite = self._handler_suite_map[existing_resource_path]
             self._handler_suite_map[alias_resource_path] = handler_suite
         except KeyError:
-            raise DispatchError('No handler for: %r' % existing_resource_path)
+            raise DispatchException('No handler for: %r' %
+                                    existing_resource_path)
 
     def source_warnings(self):
         """Return warnings in sourcing handlers."""
 
         return self._source_warnings
 
     def do_extra_handshake(self, request):
         """Do extra checking in WebSocket handshake.
 
         Select a handler based on request.uri and call its
         web_socket_do_extra_handshake function.
 
         Args:
             request: mod_python request.
+
+        Raises:
+            DispatchException: when handler was not found
+            AbortedByUserException: when user handler abort connection
+            HandshakeException: when opening handshake failed
         """
 
-        do_extra_handshake_ = self._get_handler_suite(
-            request).do_extra_handshake
+        handler_suite = self.get_handler_suite(request.ws_resource)
+        if handler_suite is None:
+            raise DispatchException('No handler for: %r' % request.ws_resource)
+        do_extra_handshake_ = handler_suite.do_extra_handshake
         try:
             do_extra_handshake_(request)
+        except handshake.AbortedByUserException, e:
+            raise
         except Exception, e:
             util.prepend_message_to_exception(
                     '%s raised exception for %s: ' % (
                             _DO_EXTRA_HANDSHAKE_HANDLER_NAME,
                             request.ws_resource),
                     e)
-            raise
+            raise handshake.HandshakeException(e, 403)
 
     def transfer_data(self, request):
         """Let a handler transfer_data with a WebSocket client.
 
         Select a handler based on request.ws_resource and call its
         web_socket_transfer_data function.
 
         Args:
             request: mod_python request.
+
+        Raises:
+            DispatchException: when handler was not found
+            AbortedByUserException: when user handler abort connection
         """
 
-        transfer_data_ = self._get_handler_suite(request).transfer_data
+        handler_suite = self.get_handler_suite(request.ws_resource)
+        if handler_suite is None:
+            raise DispatchException('No handler for: %r' % request.ws_resource)
+        transfer_data_ = handler_suite.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 handshake.AbortedByUserException, e:
+            self._logger.debug('%s', e)
+            raise
         except msgutil.BadOperationException, e:
             self._logger.debug('%s', e)
-            request.ws_stream.close_connection(common.STATUS_GOING_AWAY)
+            request.ws_stream.close_connection(common.STATUS_ABNORMAL_CLOSE)
         except msgutil.InvalidFrameException, e:
             # InvalidFrameException must be caught before
             # ConnectionTerminatedException that catches InvalidFrameException.
             self._logger.debug('%s', e)
             request.ws_stream.close_connection(common.STATUS_PROTOCOL_ERROR)
         except msgutil.UnsupportedFrameException, e:
             self._logger.debug('%s', e)
             request.ws_stream.close_connection(common.STATUS_UNSUPPORTED)
+        except stream.InvalidUTF8Exception, e:
+            self._logger_debug('%s', e)
+            request.ws_stream.close_connection(common.STATUS_INVALID_UTF8)
         except msgutil.ConnectionTerminatedException, 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):
+    def passive_closing_handshake(self, request):
+        """Prepare code and reason for responding client initiated closing
+        handshake.
+        """
+
+        handler_suite = self.get_handler_suite(request.ws_resource)
+        if handler_suite is None:
+            return _default_passive_closing_handshake_handler(request)
+        return handler_suite.passive_closing_handshake(request)
+
+    def get_handler_suite(self, resource):
         """Retrieves two handlers (one for extra handshake processing, and one
         for data transfer) for the given request as a HandlerSuite object.
         """
 
-        try:
-            ws_resource_path = request.ws_resource.split('?', 1)[0]
-            return self._handler_suite_map[ws_resource_path]
-        except KeyError:
-            raise DispatchError('No handler for: %r' % request.ws_resource)
+        fragment = None
+        if '#' in resource:
+            resource, fragment = resource.split('#', 1)
+        if '?' in resource:
+            resource = resource.split('?', 1)[0]
+        handler_suite = self._handler_suite_map.get(resource)
+        if handler_suite and fragment:
+            raise DispatchException('Fragment identifiers MUST NOT be used on '
+                                    'WebSocket URIs', 400);
+        return handler_suite
 
     def _source_handler_files_in_dir(self, root_dir, scan_dir):
         """Source all the handler source files in the scan_dir directory.
 
         The resource path is determined relative to root_dir.
         """
 
         convert = _create_path_to_resource_converter(root_dir)
         for path in _enumerate_handler_file_paths(scan_dir):
             try:
                 handler_suite = _source_handler_file(open(path).read())
-            except DispatchError, e:
+            except DispatchException, e:
                 self._source_warnings.append('%s: %s' % (path, e))
                 continue
             self._handler_suite_map[convert(path)] = handler_suite
 
 
 # vi:sts=4 sw=4 et
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/extensions.py
@@ -0,0 +1,201 @@
+# 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.
+
+
+from mod_pywebsocket import common
+from mod_pywebsocket import util
+
+
+_available_processors = {}
+
+
+class ExtensionProcessorInterface(object):
+    def get_extension_response(self):
+        return None
+
+    def setup_stream_options(self, stream_options):
+        pass
+
+
+class DeflateStreamExtensionProcessor(ExtensionProcessorInterface):
+    """WebSocket DEFLATE stream extension processor."""
+
+    def __init__(self, request):
+        self._logger = util.get_class_logger(self)
+
+        self._request = request
+
+    def get_extension_response(self):
+        if len(self._request.get_parameter_names()) != 0:
+            return None
+
+        self._logger.debug(
+            'Enable %s extension', common.DEFLATE_STREAM_EXTENSION)
+
+        return common.ExtensionParameter(common.DEFLATE_STREAM_EXTENSION)
+
+    def setup_stream_options(self, stream_options):
+        stream_options.deflate_stream = True
+
+
+_available_processors[common.DEFLATE_STREAM_EXTENSION] = (
+    DeflateStreamExtensionProcessor)
+
+
+class DeflateFrameExtensionProcessor(ExtensionProcessorInterface):
+    """WebSocket Per-frame DEFLATE extension processor."""
+
+    _WINDOW_BITS_PARAM = 'window_bits'
+    _NO_CONTEXT_TAKEOVER_PARAM = 'no_context_takeover'
+
+    def __init__(self, request):
+        self._logger = util.get_class_logger(self)
+
+        self._request = request
+
+        self._response_window_bits = None
+        self._response_no_context_takeover = False
+
+    def get_extension_response(self):
+        # Any unknown parameter will be just ignored.
+
+        window_bits = self._request.get_parameter_value(
+            self._WINDOW_BITS_PARAM)
+        no_context_takeover = self._request.has_parameter(
+            self._NO_CONTEXT_TAKEOVER_PARAM)
+        if (no_context_takeover and
+            self._request.get_parameter_value(
+                self._NO_CONTEXT_TAKEOVER_PARAM) is not None):
+            return None
+
+        if window_bits is not None:
+            try:
+                window_bits = int(window_bits)
+            except ValueError, e:
+                return None
+            if window_bits < 8 or window_bits > 15:
+                return None
+
+        self._deflater = util._RFC1979Deflater(
+            window_bits, no_context_takeover)
+
+        self._inflater = util._RFC1979Inflater()
+
+        self._compress_outgoing = True
+
+        response = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
+
+        if self._response_window_bits is not None:
+            response.add_parameter(
+                self._WINDOW_BITS_PARAM, str(self._response_window_bits))
+        if self._response_no_context_takeover:
+            response.add_parameter(
+                self._NO_CONTEXT_TAKEOVER_PARAM, None)
+
+        self._logger.debug(
+            'Enable %s extension ('
+            'request: window_bits=%s; no_context_takeover=%r, '
+            'response: window_wbits=%s; no_context_takeover=%r)' %
+            (common.DEFLATE_STREAM_EXTENSION,
+             window_bits,
+             no_context_takeover,
+             self._response_window_bits,
+             self._response_no_context_takeover))
+
+        return response
+
+    def setup_stream_options(self, stream_options):
+        class _OutgoingFilter(object):
+            def __init__(self, parent):
+                self._parent = parent
+
+            def filter(self, frame):
+                self._parent._outgoing_filter(frame)
+
+        class _IncomingFilter(object):
+            def __init__(self, parent):
+                self._parent = parent
+
+            def filter(self, frame):
+                self._parent._incoming_filter(frame)
+
+        stream_options.outgoing_frame_filters.append(
+            _OutgoingFilter(self))
+        stream_options.incoming_frame_filters.insert(
+            0, _IncomingFilter(self))
+
+    def set_response_window_bits(self, value):
+        self._response_window_bits = value
+
+    def set_response_no_context_takeover(self, value):
+        self._response_no_context_takeover = value
+
+    def enable_outgoing_compression(self):
+        self._compress_outgoing = True
+
+    def disable_outgoing_compression(self):
+        self._compress_outgoing = False
+
+    def _outgoing_filter(self, frame):
+        """Transform outgoing frames. This method is called only by
+        an _OutgoingFilter instance.
+        """
+
+        if (not self._compress_outgoing or
+            common.is_control_opcode(frame.opcode)):
+            return
+
+        frame.payload = self._deflater.filter(frame.payload)
+        frame.rsv1 = 1
+
+    def _incoming_filter(self, frame):
+        """Transform incoming frames. This method is called only by
+        an _IncomingFilter instance.
+        """
+
+        if frame.rsv1 != 1 or common.is_control_opcode(frame.opcode):
+            return
+
+        frame.payload = self._inflater.filter(frame.payload)
+        frame.rsv1 = 0
+
+
+_available_processors[common.DEFLATE_FRAME_EXTENSION] = (
+    DeflateFrameExtensionProcessor)
+
+
+def get_extension_processor(extension_request):
+    global _available_processors
+    processor_class = _available_processors.get(extension_request.name())
+    if processor_class is None:
+        return None
+    return processor_class(extension_request)
+
+
+# vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/__init__.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/__init__.py
@@ -31,86 +31,78 @@
 """WebSocket opening handshake processor. This class try to apply available
 opening handshake processors for each protocol version until a connection is
 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
+from mod_pywebsocket.handshake import hybi
+# Export AbortedByUserException and HandshakeException symbol from this module.
+from mod_pywebsocket.handshake._base import AbortedByUserException
+from mod_pywebsocket.handshake._base import HandshakeException
+
+
+_LOGGER = logging.getLogger(__name__)
 
 
-class Handshaker(object):
-    """This class performs WebSocket handshake."""
-
-    def __init__(self, request, dispatcher, allowDraft75=False, strict=False):
-        """Construct an instance.
-
-        Args:
-            request: mod_python request.
-            dispatcher: Dispatcher (dispatch.Dispatcher).
-            allowDraft75: allow draft 75 handshake protocol.
-            strict: Strictly check handshake request in draft 75.
-                Default: False. If True, request.connection must provide
-                get_memorized_lines method.
+def do_handshake(request, dispatcher, allowDraft75=False, strict=False):
+    """Performs WebSocket handshake.
 
-        Handshaker will add attributes such as ws_resource in performing
-        handshake.
-        """
-
-        self._logger = util.get_class_logger(self)
+    Args:
+        request: mod_python request.
+        dispatcher: Dispatcher (dispatch.Dispatcher).
+        allowDraft75: allow draft 75 handshake protocol.
+        strict: Strictly check handshake request in draft 75.
+            Default: False. If True, request.connection must provide
+            get_memorized_lines method.
 
-        self._request = request
-        self._dispatcher = dispatcher
-        self._strict = strict
-        self._hybi07Handshaker = hybi06.Handshaker(request, dispatcher)
-        self._hybi00Handshaker = hybi00.Handshaker(request, dispatcher)
-        self._hixie75Handshaker = None
-        if allowDraft75:
-            self._hixie75Handshaker = draft75.Handshaker(
-                request, dispatcher, strict)
-
-    def do_handshake(self):
-        """Perform WebSocket Handshake."""
+    Handshaker will add attributes such as ws_resource in performing
+    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 request headers: %r',
-            dict(self._request.headers_in))
+    _LOGGER.debug('Opening handshake resource: %r', 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.
+    _LOGGER.debug(
+        'Opening handshake request headers: %r', dict(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)
-                try:
-                    handshaker.do_handshake()
-                    return
-                except HandshakeError, e:
-                    self._logger.info('%s handshake failed: %s', name, e)
-                    last_error = e
-        raise last_error
+    handshakers = []
+    handshakers.append(
+        ('IETF HyBi latest', hybi.Handshaker(request, dispatcher)))
+    handshakers.append(
+        ('IETF HyBi 00', hybi00.Handshaker(request, dispatcher)))
+    if allowDraft75:
+        handshakers.append(
+            ('IETF Hixie 75', draft75.Handshaker(request, dispatcher, strict)))
+
+    for name, handshaker in handshakers:
+        _LOGGER.info('Trying %s protocol', name)
+        try:
+            handshaker.do_handshake()
+            return
+        except HandshakeException, e:
+            _LOGGER.info(
+                'Failed to complete opening handshake as %s protocol: %r',
+                name, e)
+            if e.status:
+                raise e
+        except AbortedByUserException, e:
+            raise
+
+    raise HandshakeException(
+        'Failed to complete opening handshake for all available protocols')
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py
@@ -32,96 +32,86 @@
 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.
+class AbortedByUserException(Exception):
+    """Exception for aborting a connection intentionally.
+
+    If this exception is raised in do_extra_handshake handler, the connection
+    will be abandoned. No other WebSocket or HTTP(S) handler will be invoked.
+
+    If this exception is raised in transfer_data_handler, the connection will
+    be closed without closing handshake. No other WebSocket or HTTP(S) handler
+    will be invoked.
     """
 
-    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)
+    pass
 
 
-class HandshakeError(Exception):
+class HandshakeException(Exception):
     """This exception will be raised when an error occurred while processing
     WebSocket initial handshake.
     """
-    pass
+
+    def __init__(self, name, status=None):
+        super(HandshakeException, self).__init__(name)
+        self.status = status
 
 
 def get_default_port(is_secure):
     if is_secure:
         return common.DEFAULT_WEB_SOCKET_SECURE_PORT
     else:
         return common.DEFAULT_WEB_SOCKET_PORT
 
 
-# TODO(tyoshino): Have stricter validator for HyBi 07.
-def validate_subprotocol(subprotocol):
+def validate_subprotocol(subprotocol, hixie):
     """Validate a value in subprotocol fields such as WebSocket-Protocol,
     Sec-WebSocket-Protocol.
 
     See
-    - HyBi 06: Section 5.2.2.
+    - HyBi 10: Section 5.1. and 5.2.2.
     - HyBi 00: Section 4.1. Opening handshake
     - Hixie 75: Section 4.1. Handshake
     """
 
     if not subprotocol:
-        raise HandshakeError('Invalid subprotocol name: empty')
-    for c in subprotocol:
-        if not 0x20 <= ord(c) <= 0x7e:
-            raise HandshakeError(
-                'Illegal character in subprotocol name: %r' % c)
+        raise HandshakeException('Invalid subprotocol name: empty')
+    if hixie:
+        # Parameter should be in the range U+0020 to U+007E.
+        for c in subprotocol:
+            if not 0x20 <= ord(c) <= 0x7e:
+                raise HandshakeException(
+                    'Illegal character in subprotocol name: %r' % c)
+    else:
+        # Parameter should be encoded HTTP token.
+        state = http_header_util.ParsingState(subprotocol)
+        token = http_header_util.consume_token(state)
+        rest = http_header_util.peek(state)
+        # If |rest| is not None, |subprotocol| is not one token or invalid. If
+        # |rest| is None, |token| must not be None because |subprotocol| is
+        # concatenation of |token| and |rest| and is not None.
+        if rest is not None:
+            raise HandshakeException('Invalid non-token string in subprotocol '
+                                     'name: %r' % rest)
 
 
 def parse_host_header(request):
     fields = request.headers_in['Host'].split(':', 1)
     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)
+        raise HandshakeException('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."""
@@ -129,47 +119,47 @@ def build_location(request):
     if request.is_https():
         location_parts.append(common.WEB_SOCKET_SECURE_SCHEME)
     else:
         location_parts.append(common.WEB_SOCKET_SCHEME)
     location_parts.append('://')
     host, port = parse_host_header(request)
     connection_port = request.connection.local_addr[1]
     if port != connection_port:
-        raise HandshakeError('Header/connection port mismatch: %d/%d' %
-                             (port, connection_port))
+        raise HandshakeException('Header/connection port mismatch: %d/%d' %
+                                 (port, connection_port))
     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):
     value = request.headers_in.get(key)
     if value is None:
-        raise HandshakeError('Header %s is not defined' % key)
+        raise HandshakeException('Header %s is not defined' % key)
     return value
 
 
-def validate_mandatory_header(request, key, expected_value):
+def validate_mandatory_header(request, key, expected_value, fail_status=None):
     value = get_mandatory_header(request, key)
 
     if value.lower() != expected_value.lower():
-        raise HandshakeError(
+        raise HandshakeException(
             'Expected %r for header %s but found %r (case-insensitive)' %
-            (expected_value, key, value))
+            (expected_value, key, value), status=fail_status)
 
 
 def check_request_line(request):
     # 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')
+        raise HandshakeException('Method is not GET')
 
 
 def check_header_lines(request, mandatory_headers):
     check_request_line(request)
 
     # The expected field names, and the meaning of their corresponding
     # values, are as follows.
     #  |Upgrade| and |Connection|
@@ -194,79 +184,79 @@ def parse_token_list(data):
             token_list.append(token)
 
         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(
+            raise HandshakeException(
                 'Expected a comma but found %r' % http_header_util.peek(state))
 
         http_header_util.consume_lwses(state)
 
     if len(token_list) == 0:
-        raise HandshakeError('No valid token found')
+        raise HandshakeException('No valid token found')
 
     return token_list
 
 
 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')
+        raise HandshakeException('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(
+        raise HandshakeException(
             '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)
+    extension = common.ExtensionParameter(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(
+        except HandshakeException, e:
+            raise HandshakeException(
                 '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.
+    common.ExtensionParameter objects.
 
     Leading LWSes must be trimmed.
     """
 
     state = http_header_util.ParsingState(data)
 
     extension_list = []
     while True:
@@ -275,31 +265,39 @@ def parse_extensions(data):
             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(
+            raise HandshakeException(
                 '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(
+        raise HandshakeException(
             '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())
+        formatted_params = [extension.name()]
+        for param_name, param_value in extension.get_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))
+
+        formatted_extension_list.append('; '.join(formatted_params))
 
     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
@@ -38,17 +38,17 @@
 
 
 import logging
 import re
 
 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 HandshakeException
 from mod_pywebsocket.handshake._base import build_location
 from mod_pywebsocket.handshake._base import validate_subprotocol
 
 
 _MANDATORY_HEADERS = [
     # key, expected value or None
     ['Upgrade', 'WebSocket'],
     ['Connection', 'Upgrade'],
@@ -126,17 +126,17 @@ class Handshaker(object):
         self._request.ws_origin = self._request.headers_in['Origin']
 
     def _set_location(self):
         self._request.ws_location = build_location(self._request)
 
     def _set_subprotocol(self):
         subprotocol = self._request.headers_in.get('WebSocket-Protocol')
         if subprotocol is not None:
-            validate_subprotocol(subprotocol)
+            validate_subprotocol(subprotocol, hixie=True)
         self._request.ws_protocol = subprotocol
 
     def _set_protocol_version(self):
         self._logger.debug('IETF Hixie 75 protocol')
         self._request.ws_version = common.VERSION_HIXIE75
         self._request.ws_stream = StreamHixie75(self._request)
 
     def _sendall(self, data):
@@ -152,38 +152,39 @@ class Handshaker(object):
             self._sendall(
                 'WebSocket-Protocol: %s\r\n' % self._request.ws_protocol)
         self._sendall('\r\n')
 
     def _check_header_lines(self):
         for key, expected_value in _MANDATORY_HEADERS:
             actual_value = self._request.headers_in.get(key)
             if not actual_value:
-                raise HandshakeError('Header %s is not defined' % key)
+                raise HandshakeException('Header %s is not defined' % key)
             if expected_value:
                 if actual_value != expected_value:
-                    raise HandshakeError(
+                    raise HandshakeException(
                         'Expected %r for header %s but found %r' %
                         (expected_value, key, actual_value))
         if self._strict:
             try:
                 lines = self._request.connection.get_memorized_lines()
             except AttributeError, e:
                 raise AttributeError(
                     'Strict handshake is specified but the connection '
                     'doesn\'t provide get_memorized_lines()')
             self._check_first_lines(lines)
 
     def _check_first_lines(self, lines):
         if len(lines) < len(_FIRST_FIVE_LINES):
-            raise HandshakeError('Too few header lines: %d' % len(lines))
+            raise HandshakeException('Too few header lines: %d' % len(lines))
         for line, regexp in zip(lines, _FIRST_FIVE_LINES):
             if not regexp.search(line):
-                raise HandshakeError('Unexpected header: %r doesn\'t match %r'
-                                     % (line, regexp.pattern))
+                raise HandshakeException(
+                    'Unexpected header: %r doesn\'t match %r'
+                    % (line, regexp.pattern))
         sixth_and_later = ''.join(lines[5:])
         if not _SIXTH_AND_LATER.search(sixth_and_later):
-            raise HandshakeError('Unexpected header: %r doesn\'t match %r'
-                                 % (sixth_and_later,
-                                    _SIXTH_AND_LATER.pattern))
+            raise HandshakeException(
+                'Unexpected header: %r doesn\'t match %r'
+                % (sixth_and_later, _SIXTH_AND_LATER.pattern))
 
 
 # vi:sts=4 sw=4 et
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi.py
@@ -0,0 +1,326 @@
+# 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.
+
+
+"""WebSocket HyBi latest opening handshake processor."""
+
+
+# Note: request.connection.write is used in this module, even though mod_python
+# document says that it should be used only in connection handlers.
+# Unfortunately, we have no other options. For example, request.write is not
+# suitable because it doesn't allow direct raw bytes writing.
+
+
+import base64
+import logging
+import os
+import re
+
+from mod_pywebsocket import common
+from mod_pywebsocket.extensions import get_extension_processor
+from mod_pywebsocket.handshake._base import check_request_line
+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 HandshakeException
+from mod_pywebsocket.handshake._base import parse_extensions
+from mod_pywebsocket.handshake._base import parse_token_list
+from mod_pywebsocket.handshake._base import validate_mandatory_header
+from mod_pywebsocket.handshake._base import validate_subprotocol
+from mod_pywebsocket.stream import Stream
+from mod_pywebsocket.stream import StreamOptions
+from mod_pywebsocket import util
+
+
+_BASE64_REGEX = re.compile('^[+/0-9A-Za-z]*=*$')
+
+
+def compute_accept(key):
+    """Computes value for the Sec-WebSocket-Accept header from value of the
+    Sec-WebSocket-Key header.
+    """
+
+    accept_binary = util.sha1_hash(
+        key + common.WEBSOCKET_ACCEPT_UUID).digest()
+    accept = base64.b64encode(accept_binary)
+
+    return (accept, accept_binary)
+
+
+class Handshaker(object):
+    """This class performs WebSocket handshake."""
+
+    def __init__(self, request, dispatcher):
+        """Construct an instance.
+
+        Args:
+            request: mod_python request.
+            dispatcher: Dispatcher (dispatch.Dispatcher).
+
+        Handshaker will add attributes such as ws_resource during handshake.
+        """
+
+        self._logger = util.get_class_logger(self)
+
+        self._request = request
+        self._dispatcher = dispatcher
+
+    def _validate_connection_header(self):
+        connection = get_mandatory_header(
+            self._request, common.CONNECTION_HEADER)
+
+        try:
+            connection_tokens = parse_token_list(connection)
+        except HandshakeException, e:
+            raise HandshakeException(
+                'Failed to parse %s: %s' % (common.CONNECTION_HEADER, e))
+
+        connection_is_valid = False
+        for token in connection_tokens:
+            if token.lower() == common.UPGRADE_CONNECTION_TYPE.lower():
+                connection_is_valid = True
+                break
+        if not connection_is_valid:
+            raise HandshakeException(
+                '%s header doesn\'t contain "%s"' %
+                (common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
+
+    def do_handshake(self):
+        self._request.ws_close_code = None
+        self._request.ws_close_reason = None
+
+        # Parsing.
+
+        check_request_line(self._request)
+
+        validate_mandatory_header(
+            self._request,
+            common.UPGRADE_HEADER,
+            common.WEBSOCKET_UPGRADE_TYPE)
+
+        self._validate_connection_header()
+
+        self._request.ws_resource = self._request.uri
+
+        unused_host = get_mandatory_header(self._request, common.HOST_HEADER)
+
+        self._check_version()
+
+        # This handshake must be based on latest hybi. We are responsible to
+        # fallback to HTTP on handshake failure as latest hybi handshake
+        # specifies.
+        try:
+            self._get_origin()
+            self._set_protocol()
+            self._parse_extensions()
+
+            # Key validation, response generation.
+
+            key = self._get_key()
+            (accept, accept_binary) = compute_accept(key)
+            self._logger.debug(
+                '%s: %r (%s)',
+                common.SEC_WEBSOCKET_ACCEPT_HEADER,
+                accept,
+                util.hexify(accept_binary))
+
+            self._logger.debug('IETF HyBi protocol')
+            self._request.ws_version = common.VERSION_HYBI_LATEST
+
+            # Setup extension processors.
+
+            processors = []
+            if self._request.ws_requested_extensions is not None:
+                for extension_request in self._request.ws_requested_extensions:
+                    processor = get_extension_processor(extension_request)
+                    # Unknown extension requests are just ignored.
+                    if processor is not None:
+                        processors.append(processor)
+            self._request.ws_extension_processors = processors
+
+            # Extra handshake handler may modify/remove processors.
+            self._dispatcher.do_extra_handshake(self._request)
+
+            stream_options = StreamOptions()
+
+            self._request.ws_extensions = None
+            for processor in self._request.ws_extension_processors:
+                if processor is None:
+                    # Some processors may be removed by extra handshake
+                    # handler.
+                    continue
+
+                extension_response = processor.get_extension_response()
+                if extension_response is None:
+                    # Rejected.
+                    continue
+
+                if self._request.ws_extensions is None:
+                    self._request.ws_extensions = []
+                self._request.ws_extensions.append(extension_response)
+
+                processor.setup_stream_options(stream_options)
+
+            if self._request.ws_extensions is not None:
+                self._logger.debug(
+                    'Extensions accepted: %r',
+                    map(common.ExtensionParameter.name,
+                        self._request.ws_extensions))
+
+            self._request.ws_stream = Stream(self._request, stream_options)
+
+            if self._request.ws_requested_protocols is not None:
+                if self._request.ws_protocol is None:
+                    raise HandshakeException(
+                        'do_extra_handshake must choose one subprotocol from '
+                        'ws_requested_protocols and set it to ws_protocol')
+                validate_subprotocol(self._request.ws_protocol, hixie=False)
+
+                self._logger.debug(
+                    'Subprotocol accepted: %r',
+                    self._request.ws_protocol)
+            else:
+                if self._request.ws_protocol is not None:
+                    raise HandshakeException(
+                        'ws_protocol must be None when the client didn\'t '
+                        'request any subprotocol')
+
+            self._send_handshake(accept)
+
+            self._logger.debug('Sent opening handshake response')
+        except HandshakeException, e:
+            if not e.status:
+                # Fallback to 400 bad request by default.
+                e.status = 400
+            raise e
+
+    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 = validate_mandatory_header(
+            self._request, common.SEC_WEBSOCKET_VERSION_HEADER,
+            str(common.VERSION_HYBI_LATEST), fail_status=426)
+
+    def _set_protocol(self):
+        self._request.ws_protocol = None
+
+        protocol_header = self._request.headers_in.get(
+            common.SEC_WEBSOCKET_PROTOCOL_HEADER)
+
+        if not protocol_header:
+            self._request.ws_requested_protocols = None
+            return
+
+        self._request.ws_requested_protocols = parse_token_list(
+            protocol_header)
+        self._logger.debug('Subprotocols requested: %r',
+                           self._request.ws_requested_protocols)
+
+    def _parse_extensions(self):
+        extensions_header = self._request.headers_in.get(
+            common.SEC_WEBSOCKET_EXTENSIONS_HEADER)
+        if not extensions_header:
+            self._request.ws_requested_extensions = None
+            return
+
+        self._request.ws_requested_extensions = parse_extensions(
+            extensions_header)
+
+        self._logger.debug(
+            'Extensions requested: %r',
+            map(common.ExtensionParameter.name,
+                self._request.ws_requested_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
+            # keys.
+            if _BASE64_REGEX.match(key):
+                decoded_key = base64.b64decode(key)
+                if len(decoded_key) == 16:
+                    key_is_valid = True
+        except TypeError, e:
+            pass
+
+        if not key_is_valid:
+            raise HandshakeException(
+                'Illegal value for header %s: %r' %
+                (common.SEC_WEBSOCKET_KEY_HEADER, key))
+
+        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(
+            '%s: %r (%s)',
+            common.SEC_WEBSOCKET_KEY_HEADER,
+            key,
+            util.hexify(decoded_key))
+
+        return key
+
+    def _send_handshake(self, accept):
+        response = []
+
+        response.append('HTTP/1.1 101 Switching Protocols\r\n')
+
+        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))
+        if self._request.ws_protocol is not None:
+            response.append(format_header(
+                common.SEC_WEBSOCKET_PROTOCOL_HEADER,
+                self._request.ws_protocol))
+        if (self._request.ws_extensions is not None and
+            len(self._request.ws_extensions) != 0):
+            response.append(format_header(
+                common.SEC_WEBSOCKET_EXTENSIONS_HEADER,
+                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/handshake/hybi00.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi00.py
@@ -40,17 +40,17 @@
 
 import logging
 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 HandshakeException
 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 = [
@@ -82,16 +82,20 @@ class Handshaker(object):
     def do_handshake(self):
         """Perform WebSocket Handshake.
 
         On _request, we set
             ws_resource, ws_protocol, ws_location, ws_origin, ws_challenge,
             ws_challenge_md5: WebSocket handshake information.
             ws_stream: Frame generation/parsing class.
             ws_version: Protocol version.
+
+        Raises:
+            HandshakeException: when any error happened in parsing the opening
+                                handshake request.
         """
 
         # 5.1 Reading the client's opening handshake.
         # dispatcher sets it in self._request.
         check_header_lines(self._request, _MANDATORY_HEADERS)
         self._set_resource()
         self._set_subprotocol()
         self._set_location()
@@ -108,51 +112,38 @@ class Handshaker(object):
     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:
-            validate_subprotocol(subprotocol)
+            validate_subprotocol(subprotocol, hixie=True)
         self._request.ws_protocol = subprotocol
 
     def _set_location(self):
         # |Host|
         host = self._request.headers_in.get(common.HOST_HEADER)
         if host is not None:
             self._request.ws_location = build_location(self._request)
         # TODO(ukai): check host is this host.
 
     def _set_origin(self):
         # |Origin|
-        origin = self._request.headers_in['Origin']
+        origin = self._request.headers_in.get(common.ORIGIN_HEADER)
         if origin is not None:
             self._request.ws_origin = origin
 
     def _set_protocol_version(self):
         # |Sec-WebSocket-Draft|
-        draft = self._request.headers_in.get('Sec-WebSocket-Draft')
-        if draft is not None:
-            try:
-                draft_int = int(draft)
-
-                # Draft value 2 is used by HyBi 02 and 03 which we no longer
-                # support. draft >= 3 and <= 1 are never defined in the spec.
-                # 0 might be used to mean HyBi 00 by somebody. 1 might be used
-                # to mean HyBi 01 by somebody but we no longer support it.
-
-                if draft_int == 1 or draft_int == 2:
-                    raise HandshakeError('HyBi 01-03 are not supported')
-                elif draft_int != 0:
-                    raise ValueError
-            except ValueError, e:
-                raise HandshakeError(
-                    'Illegal value for Sec-WebSocket-Draft: %s' % draft)
+        draft = self._request.headers_in.get(common.SEC_WEBSOCKET_DRAFT_HEADER)
+        if draft is not None and draft != '0':
+            raise HandshakeException('Illegal value for %s: %s' %
+                                     (common.SEC_WEBSOCKET_DRAFT_HEADER, draft))
 
         self._logger.debug('IETF HyBi 00 protocol')
         self._request.ws_version = common.VERSION_HYBI00
         self._request.ws_stream = StreamHixie75(self._request, True)
 
     def _set_challenge_response(self):
         # 5.2 4-8.
         self._request.ws_challenge = self._get_challenge()
@@ -175,42 +166,42 @@ class Handshaker(object):
 
         # 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)
+            raise HandshakeException('%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)
+            raise HandshakeException('%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(
+            raise HandshakeException(
                 '%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: 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')
+        key1 = self._get_key_value(common.SEC_WEBSOCKET_KEY1_HEADER)
+        key2 = self._get_key_value(common.SEC_WEBSOCKET_KEY2_HEADER)
         # 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 _send_handshake(self):
@@ -220,17 +211,17 @@ class Handshaker(object):
         response.append('HTTP/1.1 101 WebSocket Protocol Handshake\r\n')
 
         # 5.2 11. send the following fields to the client.
         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))
+            common.SEC_WEBSOCKET_LOCATION_HEADER, self._request.ws_location))
         response.append(format_header(
             common.SEC_WEBSOCKET_ORIGIN_HEADER, self._request.ws_origin))
         if self._request.ws_protocol:
             response.append(format_header(
                 common.SEC_WEBSOCKET_PROTOCOL_HEADER,
                 self._request.ws_protocol))
         # 5.2 12. send two bytes 0x0D 0x0A.
         response.append('\r\n')
deleted file mode 100644
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi06.py
+++ /dev/null
@@ -1,286 +0,0 @@
-# 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.
-
-
-"""WebSocket HyBi 07 opening handshake processor."""
-
-
-# Note: request.connection.write is used in this module, even though mod_python
-# document says that it should be used only in connection handlers.
-# Unfortunately, we have no other options. For example, request.write is not
-# suitable because it doesn't allow direct raw bytes writing.
-
-
-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_request_line
-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 parse_extensions
-from mod_pywebsocket.handshake._base import parse_token_list
-from mod_pywebsocket.handshake._base import validate_mandatory_header
-
-
-_BASE64_REGEX = re.compile('^[+/0-9A-Za-z]*=*$')
-
-
-def compute_accept(key):
-    """Computes value for the Sec-WebSocket-Accept header from value of the
-    Sec-WebSocket-Key header.
-    """
-
-    accept_binary = util.sha1_hash(
-        key + common.WEBSOCKET_ACCEPT_UUID).digest()
-    accept = base64.b64encode(accept_binary)
-
-    return (accept, accept_binary)
-
-
-class Handshaker(object):
-    """This class performs WebSocket handshake."""
-
-    def __init__(self, request, dispatcher):
-        """Construct an instance.
-
-        Args:
-            request: mod_python request.
-            dispatcher: Dispatcher (dispatch.Dispatcher).
-
-        Handshaker will add attributes such as ws_resource during handshake.
-        """
-
-        self._logger = util.get_class_logger(self)
-
-        self._request = request
-        self._dispatcher = dispatcher
-
-    def do_handshake(self):
-        check_request_line(self._request)
-
-        validate_mandatory_header(
-            self._request,
-            common.UPGRADE_HEADER,
-            common.WEBSOCKET_UPGRADE_TYPE)
-
-        connection = get_mandatory_header(
-            self._request, common.CONNECTION_HEADER)
-
-        try:
-            connection_tokens = parse_token_list(connection)
-        except HandshakeError, e:
-            raise HandshakeError(
-                'Failed to parse %s: %s' % (common.CONNECTION_HEADER, e))
-
-        connection_is_valid = False
-        for token in connection_tokens:
-            if token.lower() == common.UPGRADE_CONNECTION_TYPE.lower():
-                connection_is_valid = True
-                break
-        if not connection_is_valid:
-            raise HandshakeError(
-                '%s header doesn\'t contain "%s"' %
-                (common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
-
-        self._request.ws_resource = self._request.uri
-
-        unused_host = get_mandatory_header(self._request, common.HOST_HEADER)
-
-        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('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
-        self._request.ws_close_reason = None
-
-        self._dispatcher.do_extra_handshake(self._request)
-
-        if self._request.ws_requested_protocols is not None:
-            if self._request.ws_protocol is None:
-                raise HandshakeError(
-                    'do_extra_handshake must choose one subprotocol from '
-                    'ws_requested_protocols and set it to ws_protocol')
-
-            # TODO(tyoshino): Validate selected subprotocol value.
-
-            self._logger.debug(
-                'Subprotocol accepted: %r',
-                self._request.ws_protocol)
-        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 = validate_mandatory_header(
-            self._request, common.SEC_WEBSOCKET_VERSION_HEADER, '8')
-
-    def _set_protocol(self):
-        self._request.ws_protocol = None
-
-        protocol_header = self._request.headers_in.get(
-            common.SEC_WEBSOCKET_PROTOCOL_HEADER)
-
-        if not protocol_header:
-            self._request.ws_requested_protocols = None
-            return
-
-        # TODO(tyoshino): Validate the header value.
-
-        requested_protocols = protocol_header.split(',')
-        self._request.ws_requested_protocols = [
-            s.strip() for s in requested_protocols]
-
-        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 = 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_name == 'deflate-stream' and
-                len(extension.get_parameter_names()) == 0):
-                self._request.ws_extensions.append(extension)
-                self._request.ws_deflate = True
-
-        self._request.ws_requested_extensions = requested_extensions
-
-        self._logger.debug(
-            '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
-            # keys.
-            if _BASE64_REGEX.match(key):
-                decoded_key = base64.b64decode(key)
-                if len(decoded_key) == 16:
-                    key_is_valid = True
-        except TypeError, e:
-            pass
-
-        if not key_is_valid:
-            raise HandshakeError(
-                'Illegal value for header %s: %r' %
-                (common.SEC_WEBSOCKET_KEY_HEADER, key))
-
-        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))
-
-        return key
-
-    def _send_handshake(self, accept):
-        response = []
-
-        response.append('HTTP/1.1 101 Switching Protocols\r\n')
-
-        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:
-            response.append(format_header(
-                common.SEC_WEBSOCKET_PROTOCOL_HEADER,
-                self._request.ws_protocol))
-        if self._request.ws_extensions is not None:
-            response.append(format_header(
-                common.SEC_WEBSOCKET_EXTENSIONS_HEADER,
-                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
@@ -129,42 +129,47 @@ def _create_dispatcher():
 
 
 def headerparserhandler(request):
     """Handle request.
 
     Args:
         request: mod_python request.
 
-    This function is named headerparserhandler because it is the default name
-    for a PythonHeaderParserHandler.
+    This function is named headerparserhandler because it is the default
+    name for a PythonHeaderParserHandler.
     """
 
+    handshake_is_done = False
     try:
         allowDraft75 = apache.main_server.get_options().get(
             _PYOPT_ALLOW_DRAFT75, None)
-        handshaker = handshake.Handshaker(request, _dispatcher,
-                                          allowDraft75=allowDraft75)
-        handshaker.do_handshake()
+        handshake.do_handshake(
+            request, _dispatcher, allowDraft75=allowDraft75)
+        handshake_is_done = True
         request.log_error(
             'mod_pywebsocket: resource: %r' % request.ws_resource,
             apache.APLOG_DEBUG)
-        try:
-            _dispatcher.transfer_data(request)
-        except Exception, e:
-            # Catch exception in transfer_data.
-            # In this case, handshake has been successful, so just log the
-            # exception and return apache.DONE
-            request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_WARNING)
-    except handshake.HandshakeError, e:
+        request._dispatcher = _dispatcher
+        _dispatcher.transfer_data(request)
+    except dispatch.DispatchException, e:
+        request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_WARNING)
+        if not handshake_is_done:
+            return e.status
+    except handshake.AbortedByUserException, e:
+        request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO)
+    except handshake.HandshakeException, e:
         # Handshake for ws/wss failed.
-        # But the request can be valid http/https request.
+        # The request handling fallback into http/https.
         request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO)
-        return apache.DECLINED
-    except dispatch.DispatchError, e:
+        return e.status
+    except Exception, e:
         request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_WARNING)
-        return apache.DECLINED
+        # Unknown exceptions before handshake mean Apache must handle its
+        # request with another handler.
+        if not handshake_is_done:
+            return apache.DECLINE
     # Set assbackwards to suppress response header generation by Apache.
     request.assbackwards = 1
     return apache.DONE  # Return DONE such that no other handlers are invoked.
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/http_header_util.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/http_header_util.py
@@ -28,16 +28,19 @@
 # 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.
 """
 
 
+import urlparse
+
+
 _SEPARATORS = '()<>@,;:\\"/[]?={} \t'
 
 
 def _is_char(c):
     """Returns true iff c is in CHAR as specified in HTTP RFC."""
 
     return ord(c) <= 127
 
@@ -73,17 +76,18 @@ def consume(state, amount=1):
 
     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.
+    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
@@ -205,9 +209,46 @@ def quote_if_necessary(s):
             result.append(c)
 
     if quote:
         return '"' + ''.join(result) + '"';
     else:
         return ''.join(result)
 
 
+def parse_uri(uri):
+    """Parse absolute URI then return host, port and resource."""
+
+    parsed = urlparse.urlsplit(uri)
+    if parsed.scheme != 'wss' and parsed.scheme != 'ws':
+        # |uri| must be a relative URI.
+        # TODO(toyoshim): Should validate |uri|.
+        return None, None, uri
+
+    if parsed.hostname is None:
+        return None, None, None
+
+    port = None
+    try:
+        port = parsed.port
+    except ValueError, e:
+        # port property cause ValueError on invalid null port description like
+        # 'ws://host:/path'.
+        return None, None, None
+
+    if port is None:
+        if parsed.scheme == 'ws':
+            port = 80
+        else:
+            port = 443
+
+    path = parsed.path
+    if not path:
+        path += '/'
+    if parsed.query:
+        path += '?' + parsed.query
+    if parsed.fragment:
+        path += '#' + parsed.fragment
+
+    return parsed.hostname, port, path
+
+
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/memorizingfile.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/memorizingfile.py
@@ -55,29 +55,45 @@ class MemorizingFile(object):
             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
+        self._buffered = False
+        self._buffered_line = None
 
     def __getattribute__(self, name):
         if name in ('_file', '_memorized_lines', '_max_memorized_lines',
-                    'readline', 'get_memorized_lines'):
+                    '_buffered', '_buffered_line', 'readline',
+                    'get_memorized_lines'):
             return object.__getattribute__(self, name)
         return self._file.__getattribute__(name)
 
-    def readline(self):
-        """Override file.readline and memorize the line read."""
+    def readline(self, size=-1):
+        """Override file.readline and memorize the line read.
+
+        Note that even if size is specified and smaller than actual size,
+        the whole line will be read out from underlying file object by
+        subsequent readline calls.
+        """
 
-        line = self._file.readline()
-        if line and len(self._memorized_lines) < self._max_memorized_lines:
-            self._memorized_lines.append(line)
+        if self._buffered:
+            line = self._buffered_line
+            self._buffered = False
+        else:
+            line = self._file.readline()
+            if line and len(self._memorized_lines) < self._max_memorized_lines:
+                self._memorized_lines.append(line)
+        if size >= 0 and size < len(line):
+            self._buffered = True
+            self._buffered_line = line[size:]
+            return line[:size]
         return line
 
     def get_memorized_lines(self):
         """Get lines memorized so far."""
         return self._memorized_lines
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/msgutil.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/msgutil.py
@@ -26,19 +26,20 @@
 # 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.
 
 
 """Message related utilities.
 
 Note: request.connection.write/read are used in this module, even though
-mod_python document says that they should be used only in connection handlers.
-Unfortunately, we have no other options. For example, request.write/read are
-not suitable because they don't allow direct raw bytes writing/reading.
+mod_python document says that they should be used only in connection
+handlers. Unfortunately, we have no other options. For example,
+request.write/read are not suitable because they don't allow direct raw
+bytes writing/reading.
 """
 
 
 import Queue
 import threading
 
 
 # Export Exception symbols from msgutil for backward compatibility
@@ -53,67 +54,69 @@ def close_connection(request):
     """Close connection.
 
     Args:
         request: mod_python request.
     """
     request.ws_stream.close_connection()
 
 
-def send_message(request, message, end=True):
+def send_message(request, message, end=True, binary=False):
     """Send message.
 
     Args:
         request: mod_python request.
-        message: unicode string to send.
-        end: False to send message as a fragment. All messages until the first
-             call with end=True (inclusive) will be delivered to the client
-             in separate frames but as one WebSocket message.
+        message: unicode text or str binary to send.
+        end: False to send message as a fragment. All messages until the
+             first call with end=True (inclusive) will be delivered to the
+             client in separate frames but as one WebSocket message.
+        binary: send message as binary frame.
     Raises:
         BadOperationException: when server already terminated.
     """
-    request.ws_stream.send_message(message, end)
+    request.ws_stream.send_message(message, end, binary)
 
 
 def receive_message(request):
-    """Receive a WebSocket frame and return its payload as unicode string.
+    """Receive a WebSocket frame and return its payload as a text in
+    unicode or a binary in str.
 
     Args:
         request: mod_python request.
     Raises:
         BadOperationException: when client already terminated.
     """
     return request.ws_stream.receive_message()
 
 
 def send_ping(request, body=''):
     request.ws_stream.send_ping(body)
 
 
 class MessageReceiver(threading.Thread):
     """This class receives messages from the client.
 
-    This class provides three ways to receive messages: blocking, non-blocking,
-    and via callback. Callback has the highest precedence.
+    This class provides three ways to receive messages: blocking,
+    non-blocking, and via callback. Callback has the highest precedence.
 
     Note: This class should not be used with the standalone server for wss
     because pyOpenSSL used by the server raises a fatal error if the socket
     is accessed from multiple threads.
     """
 
     def __init__(self, request, onmessage=None):
         """Construct an instance.
 
         Args:
             request: mod_python request.
             onmessage: a function to be called when a message is received.
                        May be None. If not None, the function is called on
                        another thread. In that case, MessageReceiver.receive
-                       and MessageReceiver.receive_nowait are useless because
-                       they will never return any messages.
+                       and MessageReceiver.receive_nowait are useless
+                       because they will never return any messages.
         """
 
         threading.Thread.__init__(self)
         self._request = request
         self._queue = Queue.Queue()
         self._onmessage = onmessage
         self._stop_requested = False
         self.setDaemon(True)
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/stream.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/stream.py
@@ -1,9 +1,9 @@
-# Copyright 2010, 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.
@@ -30,24 +30,27 @@
 
 """This file exports public symbols.
 """
 
 
 from mod_pywebsocket._stream_base import BadOperationException
 from mod_pywebsocket._stream_base import ConnectionTerminatedException
 from mod_pywebsocket._stream_base import InvalidFrameException
+from mod_pywebsocket._stream_base import InvalidUTF8Exception
 from mod_pywebsocket._stream_base import UnsupportedFrameException
 from mod_pywebsocket._stream_hixie75 import StreamHixie75
-from mod_pywebsocket._stream_hybi06 import Stream
-from mod_pywebsocket._stream_hybi06 import StreamOptions
+from mod_pywebsocket._stream_hybi import Frame
+from mod_pywebsocket._stream_hybi import Stream
+from mod_pywebsocket._stream_hybi import StreamOptions
 
 # These methods are intended to be used by WebSocket client developers to have
 # their implementations receive broken data in tests.
-from mod_pywebsocket._stream_hybi06 import create_close_frame
-from mod_pywebsocket._stream_hybi06 import create_header
-from mod_pywebsocket._stream_hybi06 import create_length_header
-from mod_pywebsocket._stream_hybi06 import create_ping_frame
-from mod_pywebsocket._stream_hybi06 import create_pong_frame
-from mod_pywebsocket._stream_hybi06 import create_text_frame
+from mod_pywebsocket._stream_hybi import create_close_frame
+from mod_pywebsocket._stream_hybi import create_header
+from mod_pywebsocket._stream_hybi import create_length_header
+from mod_pywebsocket._stream_hybi import create_ping_frame
+from mod_pywebsocket._stream_hybi import create_pong_frame
+from mod_pywebsocket._stream_hybi import create_binary_frame
+from mod_pywebsocket._stream_hybi import create_text_frame
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/util.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/util.py
@@ -28,16 +28,17 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 """WebSocket utilities.
 """
 
 
 import array
+import errno
 
 # Import hash classes from a module available and recommended for each Python
 # version and re-export those symbol. Use sha and md5 module in Python 2.4, and
 # hashlib module in Python 2.6.
 try:
     import hashlib
     md5_hash = hashlib.md5
     sha1_hash = hashlib.sha1
@@ -46,16 +47,17 @@ except ImportError:
     import sha
     md5_hash = md5.md5
     sha1_hash = sha.sha
 
 import StringIO
 import logging
 import os
 import re
+import socket
 import traceback
 import zlib
 
 
 def get_stack_trace():
     """Get the current stack trace as string.
 
     This is needed to support Python 2.3.
@@ -158,18 +160,18 @@ class NoopMasker(object):
 
     def mask(self, s):
         return s
 
 
 class RepeatedXorMasker(object):
     """A masking object that applies XOR on the string given to mask method
     with the masking bytes given to the constructor repeatedly. This object
-    remembers the position in the masking bytes the last mask method call ended
-    and resumes from that point on the next mask method call.
+    remembers the position in the masking bytes the last mask method call
+    ended and resumes from that point on the next mask method call.
     """
 
     def __init__(self, mask):
         self._mask = map(ord, mask)
         self._mask_size = len(self._mask)
         self._count = 0
 
     def mask(self, s):
@@ -211,21 +213,21 @@ class DeflateRequest(object):
 # 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):
+    def __init__(self, window_bits):
         self._logger = get_class_logger(self)
 
         self._compress = zlib.compressobj(
-            zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS)
+            zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -window_bits)
 
     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
 
@@ -287,36 +289,75 @@ class _Inflater(object):
         self._logger.debug('Appended %r', data)
         self._unconsumed += data
 
     def reset(self):
         self._logger.debug('Reset')
         self._decompress = zlib.decompressobj(-zlib.MAX_WBITS)
 
 
+# Compresses/decompresses given octets using the method introduced in RFC1979.
+
+
+class _RFC1979Deflater(object):
+    """A compressor class that applies DEFLATE to given byte sequence and
+    flushes using the algorithm described in the RFC1979 section 2.1.
+    """
+
+    def __init__(self, window_bits, no_context_takeover):
+        self._deflater = None
+        if window_bits is None:
+            window_bits = zlib.MAX_WBITS
+        self._window_bits = window_bits
+        self._no_context_takeover = no_context_takeover
+
+    def filter(self, bytes):
+        if self._deflater is None or self._no_context_takeover:
+            self._deflater = _Deflater(self._window_bits)
+
+        # Strip last 4 octets which is LEN and NLEN field of a non-compressed
+        # block added for Z_SYNC_FLUSH.
+        return self._deflater.compress_and_flush(bytes)[:-4]
+
+
+class _RFC1979Inflater(object):
+    """A decompressor class for byte sequence compressed and flushed following
+    the algorithm described in the RFC1979 section 2.1.
+    """
+
+    def __init__(self):
+        self._inflater = _Inflater()
+
+    def filter(self, bytes):
+        # Restore stripped LEN and NLEN field of a non-compressed block added
+        # for Z_SYNC_FLUSH.
+        self._inflater.append(bytes + '\x00\x00\xff\xff')
+        return self._inflater.decompress(-1)
+
+
 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 = get_class_logger(self)
 
-        self._deflater = _Deflater()
+        self._deflater = _Deflater(zlib.MAX_WBITS)
         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.
+        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')
         while True:
             data = self._inflater.decompress(size)
@@ -341,17 +382,17 @@ class DeflateConnection(object):
     perform deflate compression and decompression transparently.
     """
 
     def __init__(self, connection):
         self._connection = connection
 
         self._logger = get_class_logger(self)
 
-        self._deflater = _Deflater()
+        self._deflater = _Deflater(zlib.MAX_WBITS)
         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.
@@ -383,20 +424,60 @@ class DeflateConnection(object):
             if not read_data:
                 break
             self._inflater.append(read_data)
         return data
 
     def write(self, bytes):
         self._connection.write(self._deflater.compress_and_flush(bytes))
 
-    def flushread(self):
-        self._connection.setblocking(0)
-        while True:
+
+def _is_ewouldblock_errno(error_number):
+    """Returns True iff error_number indicates that receive operation would
+    block. To make this portable, we check availability of errno and then
+    compare them.
+    """
+
+    for error_name in ['WSAEWOULDBLOCK', 'EWOULDBLOCK', 'EAGAIN']:
+        if (error_name in dir(errno) and
+            error_number == getattr(errno, error_name)):
+            return True
+    return False
+
+
+def drain_received_data(raw_socket):
+    # Set the socket non-blocking.
+    original_timeout = raw_socket.gettimeout()
+    raw_socket.settimeout(0.0)
+
+    drained_data = []
+
+    # Drain until the socket is closed or no data is immediately
+    # available for read.
+    while True:
+        try:
+            data = raw_socket.recv(1)
+            if not data:
+                break
+            drained_data.append(data)
+        except socket.error, e:
+            # e can be either a pair (errno, string) or just a string (or
+            # something else) telling what went wrong. We suppress only
+            # the errors that indicates that the socket blocks. Those
+            # exceptions can be parsed as a pair (errno, string).
             try:
-              data = self._connection.read(1)
-              self._logger.debug('flushing unused byte %r', data)
-              if len(data) < 1:
+                error_number, message = e
+            except:
+                # Failed to parse socket.error.
+                raise e
+
+            if _is_ewouldblock_errno(error_number):
                 break
-            except:
-              break
+            else:
+                raise e
+
+    # Rollback timeout value.
+    raw_socket.settimeout(original_timeout)
+
+    return ''.join(drained_data)
+
 
 # vi:sts=4 sw=4 et
old mode 100644
new mode 100755
--- a/testing/mochitest/pywebsocket/standalone.py
+++ b/testing/mochitest/pywebsocket/standalone.py
@@ -44,18 +44,18 @@ Usage:
 <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.
+<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.
@@ -65,29 +65,32 @@ import BaseHTTPServer
 import CGIHTTPServer
 import SimpleHTTPServer
 import SocketServer
 import logging
 import logging.handlers
 import optparse
 import os
 import re
+import select
 import socket
 import sys
+import threading
 
 _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 http_header_util
 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
@@ -107,205 +110,390 @@ 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()
 
-    def setblocking(self, blocking): 
-        self._request_handler.rfile._file._sock.setblocking(0)
 
 class _StandaloneRequest(object):
     """Mimic mod_python request."""
 
     def __init__(self, request_handler, use_tls):
         """Construct an instance.
 
         Args:
             request_handler: A WebSocketRequestHandler instance.
         """
+
+        self._logger = util.get_class_logger(self)
+
         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
 
+    def _drain_received_data(self):
+        """Don't use this method from WebSocket handler. Drains unread data
+        in the receive buffer.
+        """
+
+        raw_socket = self._request_handler.connection
+        drained_data = util.drain_received_data(raw_socket)
+
+        if drained_data:
+            self._logger.debug(
+                'Drained data following close frame: %r', drained_data)
+
 
 class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
     """HTTPServer specialized for WebSocket."""
 
+    # Overrides SocketServer.ThreadingMixIn.daemon_threads
     daemon_threads = True
+    # Overrides BaseHTTPServer.HTTPServer.allow_reuse_address
     allow_reuse_address = True
 
-    def __init__(self, server_address, RequestHandlerClass):
-        """Override SocketServer.TCPServer.__init__ to set SSL enabled socket
-        object to self.socket before server_bind and server_activate, if
-        necessary.
+    def __init__(self, options):
+        """Override SocketServer.TCPServer.__init__ to set SSL enabled
+        socket object to self.socket before server_bind and server_activate,
+        if necessary.
         """
 
+        self.request_queue_size = options.request_queue_size
+        self.__ws_is_shut_down = threading.Event()
+        self.__ws_serving = False
+
         SocketServer.BaseServer.__init__(
-                self, server_address, RequestHandlerClass)
-        self.socket = self._create_socket()
+            self, (options.server_host, options.port), WebSocketRequestHandler)
+
+        # Expose the options object to allow handler objects access it. We name
+        # it with websocket_ prefix to avoid conflict.
+        self.websocket_server_options = options
+
+        self._create_sockets()
         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 _create_sockets(self):
+        self.server_name, self.server_port = self.server_address
+        self._sockets = []
+        if not self.server_name:
+            addrinfo_array = [
+                (self.address_family, self.socket_type, '', '', '')]
+        else:
+            addrinfo_array = socket.getaddrinfo(self.server_name,
+                                                self.server_port,
+                                                socket.AF_UNSPEC,
+                                                socket.SOCK_STREAM,
+                                                socket.IPPROTO_TCP)
+        for addrinfo in addrinfo_array:
+            logging.info('Create socket on: %r', addrinfo)
+            family, socktype, proto, canonname, sockaddr = addrinfo
+            try:
+                socket_ = socket.socket(family, socktype)
+            except Exception, e:
+                logging.info('Skip by failure: %r', e)
+                continue
+            if self.websocket_server_options.use_tls:
+                ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
+                ctx.use_privatekey_file(
+                    self.websocket_server_options.private_key)
+                ctx.use_certificate_file(
+                    self.websocket_server_options.certificate)
+                socket_ = OpenSSL.SSL.Connection(ctx, socket_)
+            self._sockets.append((socket_, addrinfo))
+
+    def server_bind(self):
+        """Override SocketServer.TCPServer.server_bind to enable multiple
+        sockets bind.
+        """
+
+        for socket_, addrinfo in self._sockets:
+            logging.info('Bind on: %r', addrinfo)
+            if self.allow_reuse_address:
+                socket_.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+            socket_.bind(self.server_address)
+
+    def server_activate(self):
+        """Override SocketServer.TCPServer.server_activate to enable multiple
+        sockets listen.
+        """
+
+        failed_sockets = []
+
+        for socketinfo in self._sockets:
+            socket_, addrinfo = socketinfo
+            logging.info('Listen on: %r', addrinfo)
+            try:
+                socket_.listen(self.request_queue_size)
+            except Exception, e:
+                logging.info('Skip by failure: %r', e)
+                socket_.close()
+                failed_sockets.append(socketinfo)
+
+        for socketinfo in failed_sockets:
+            self._sockets.remove(socketinfo)
+
+    def server_close(self):
+        """Override SocketServer.TCPServer.server_close to enable multiple
+        sockets close.
+        """
+
+        for socketinfo in self._sockets:
+            socket_, addrinfo = socketinfo
+            logging.info('Close on: %r', addrinfo)
+            socket_.close()
+
+    def fileno(self):
+        """Override SocketServer.TCPServer.fileno."""
+
+        logging.critical('Not supported: fileno')
+        return self._sockets[0][0].fileno()
 
     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.
 
+    def serve_forever(self, poll_interval=0.5):
+        """Override SocketServer.BaseServer.serve_forever."""
+
+        self.__ws_serving = True
+        self.__ws_is_shut_down.clear()
+        handle_request = self.handle_request
+        if hasattr(self, '_handle_request_noblock'):
+            handle_request = self._handle_request_noblock
+        else:
+            logging.warning('mod_pywebsocket: fallback to blocking request '
+                            'handler')
+        try:
+            while self.__ws_serving:
+                r, w, e = select.select(
+                    [socket_[0] for socket_ in self._sockets],
+                    [], [], poll_interval)
+                for socket_ in r:
+                    self.socket = socket_
+                    handle_request()
+                self.socket = None
+        finally:
+            self.__ws_is_shut_down.set()
+
+    def shutdown(self):
+        """Override SocketServer.BaseServer.shutdown."""
+
+        self.__ws_serving = False
+        self.__ws_is_shut_down.wait()
+
 
 class WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
     """CGIHTTPRequestHandler specialized for WebSocket."""
 
     def setup(self):
-        """Override SocketServer.StreamRequestHandler.setup to wrap rfile with
-        MemorizingFile.
+        """Override SocketServer.StreamRequestHandler.setup to wrap rfile
+        with MemorizingFile.
+
+        This method will be called by BaseRequestHandler's constructor
+        before calling BaseHTTPRequestHandler.handle.
+        BaseHTTPRequestHandler.handle will call
+        BaseHTTPRequestHandler.handle_one_request and it will call
+        WebSocketRequestHandler.parse_request.
         """
 
         # 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)
+    def __init__(self, request, client_address, server):
+        self._options = server.websocket_server_options
+
+        # Overrides CGIHTTPServerRequestHandler.cgi_directories.
+        self.cgi_directories = self._options.cgi_directories
+        # Replace CGIHTTPRequestHandler.is_executable method.
+        if self._options.is_executable_method is not None:
+            self.is_executable = self._options.is_executable_method
+
+        self._request = _StandaloneRequest(self, self._options.use_tls)
+
+        _print_warnings_if_any(self._options.dispatcher)
+
+        # This actually calls BaseRequestHandler.__init__.
         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)
+            self, request, client_address, server)
 
     def parse_request(self):
         """Override BaseHTTPServer.BaseHTTPRequestHandler.parse_request.
 
         Return True to continue processing for HTTP(S), False otherwise.
+
+        See BaseHTTPRequestHandler.handle_one_request method which calls
+        this method to understand how the return value will be handled.
         """
-        result = CGIHTTPServer.CGIHTTPRequestHandler.parse_request(self)
-        if result:
+
+        # We hook parse_request method, but also call the original
+        # CGIHTTPRequestHandler.parse_request since when we return False,
+        # CGIHTTPRequestHandler.handle_one_request continues processing and
+        # it needs variables set by CGIHTTPRequestHandler.parse_request.
+        #
+        # Variables set by this method will be also used by WebSocket request
+        # handling. See _StandaloneRequest.get_request, etc.
+        if not CGIHTTPServer.CGIHTTPRequestHandler.parse_request(self):
+            return False
+        host, port, resource = http_header_util.parse_uri(self.path)
+        if resource is None:
+            logging.info('mod_pywebsocket: invalid uri %r' % self.path)
+            return True
+        server_options = self.server.websocket_server_options
+        if host is not None:
+            validation_host = server_options.validation_host
+            if validation_host is not None and host != validation_host:
+                logging.info('mod_pywebsocket: invalid host %r '
+                             '(expected: %r)' % (host, validation_host))
+                return True
+        if port is not None:
+            validation_port = server_options.validation_port
+            if validation_port is not None and port != validation_port:
+                logging.info('mod_pywebsocket: invalid port %r '
+                             '(expected: %r)' % (port, validation_port))
+                return True
+        self.path = resource
+
+        try:
+            # Fallback to default http handler for request paths for which
+            # we don't have request handlers.
+            if not self._options.dispatcher.get_handler_suite(self.path):
+                logging.info('No handlers for request: %s' % self.path)
+                return True
+
             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())
+                handshake.do_handshake(
+                    self._request,
+                    self._options.dispatcher,
+                    allowDraft75=self._options.allow_draft75,
+                    strict=self._options.strict)
+            except handshake.AbortedByUserException, e:
+                logging.info('mod_pywebsocket: %s' % e)
                 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:
+            try:
+                self._request._dispatcher = self._options.dispatcher
+                self._options.dispatcher.transfer_data(self._request)
+            except dispatch.DispatchException, e:
                 logging.warning('mod_pywebsocket: %s' % e)
                 return False
+            except handshake.AbortedByUserException, e:
+                logging.info('mod_pywebsocket: %s' % e)
             except Exception, e:
-                logging.warning('mod_pywebsocket: %s' % e)
-                logging.warning('mod_pywebsocket: %s' % util.get_stack_trace())
-                return False
-        return result
+                # 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())
+        except dispatch.DispatchException, e:
+            logging.warning('mod_pywebsocket: %s' % e)
+            self.send_error(e.status)
+        except handshake.HandshakeException, e:
+            # Handshake for ws(s) failed. Assume http(s).
+            logging.info('mod_pywebsocket: %s' % e)
+            self.send_error(e.status)
+        except Exception, e:
+            logging.warning('mod_pywebsocket: %s' % e)
+            logging.warning('mod_pywebsocket: %s' % util.get_stack_trace())
+        return False
 
     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:])))
+        logging.warning('%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):
@@ -332,43 +520,52 @@ def _configure_logging(options):
 
 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:
+            except dispatch.DispatchException, 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('-V', '--validation-host', '--validation_host',
+                      dest='validation_host',
+                      default=None,
+                      help='server hostname to validate in absolute path.')
     parser.add_option('-p', '--port', dest='port', type='int',
                       default=common.DEFAULT_WEB_SOCKET_PORT,
                       help='port to listen to')
+    parser.add_option('-P', '--validation-port', '--validation_port',
+                      dest='validation_port', type='int',
+                      default=None,
+                      help='server port to validate in absolute path.')
     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,
@@ -417,36 +614,36 @@ def _main():
                       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 = []
-
+    # TODO(tyoshino): Clean up initialization of CGI related values. Move some
+    # of code here to WebSocketRequestHandler class if it's better.
+    options.cgi_directories = []
+    options.is_executable_method = None
     if options.cgi_paths:
-        CGIHTTPServer.CGIHTTPRequestHandler.cgi_directories = \
-            options.cgi_paths.split(',')
+        options.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
+            options.is_executable_method = __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.')
@@ -460,21 +657,17 @@ def _main():
         # 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 = WebSocketServer(options)
         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__':