Bug 792831 - WebSocket permessage compression extension, r=jduell
authorMichal Novotny <michal.novotny@gmail.com>
Mon, 29 Dec 2014 12:43:40 +0100
changeset 221506 724f0a71d62171da1357e6c1f93453359e54206b
parent 221505 1c5b4332e2f1b73fe03977b69371e9a08503bff3
child 221507 09606692e15489a6d18a4f11404e9d99cf2a0afa
push id28032
push userkwierso@gmail.com
push dateTue, 30 Dec 2014 01:28:14 +0000
treeherdermozilla-central@67872ce17918 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjduell
bugs792831
milestone37.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 792831 - WebSocket permessage compression extension, r=jduell
dom/base/test/file_websocket_permessage_deflate_disabled_wsh.py
dom/base/test/file_websocket_permessage_deflate_rejected_wsh.py
dom/base/test/file_websocket_permessage_deflate_wsh.py
dom/base/test/mochitest.ini
dom/base/test/test_websocket_permessage_deflate.html
modules/libpref/init/all.js
netwerk/protocol/websocket/WebSocketChannel.cpp
netwerk/protocol/websocket/WebSocketChannel.h
testing/mochitest/moz.build
testing/mochitest/pywebsocket/COPYING
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/common.py
testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py
testing/mochitest/pywebsocket/mod_pywebsocket/extensions.py
testing/mochitest/pywebsocket/mod_pywebsocket/fast_masking.i
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/headerparserhandler.py
testing/mochitest/pywebsocket/mod_pywebsocket/msgutil.py
testing/mochitest/pywebsocket/mod_pywebsocket/mux.py
testing/mochitest/pywebsocket/mod_pywebsocket/stream.py
testing/mochitest/pywebsocket/mod_pywebsocket/util.py
testing/mochitest/pywebsocket/mod_pywebsocket/xhr_benchmark_handler.py
testing/mochitest/pywebsocket/standalone.py
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_websocket_permessage_deflate_disabled_wsh.py
@@ -0,0 +1,17 @@
+from mod_pywebsocket import msgutil
+from mod_pywebsocket import common
+
+def web_socket_do_extra_handshake(request):
+  if request.ws_requested_extensions is not None:
+    for extension_request in request.ws_requested_extensions:
+      if extension_request.name() == "permessage-deflate":
+        raise ValueError('permessage-deflate should not be offered')
+
+def web_socket_transfer_data(request):
+  while True:
+    rcvd = msgutil.receive_message(request)
+    opcode = request.ws_stream.get_last_received_opcode()
+    if (opcode == common.OPCODE_BINARY):
+      msgutil.send_message(request, rcvd, binary=True)
+    elif (opcode == common.OPCODE_TEXT):
+      msgutil.send_message(request, rcvd)
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_websocket_permessage_deflate_rejected_wsh.py
@@ -0,0 +1,23 @@
+from mod_pywebsocket import msgutil
+from mod_pywebsocket import common
+
+def web_socket_do_extra_handshake(request):
+  deflate_removed = False
+
+  if request.ws_extension_processors is not None:
+    for extension_processor in request.ws_extension_processors:
+      if extension_processor.name() == "deflate":
+        request.ws_extension_processors.remove(extension_processor)
+        deflate_removed = True
+
+  if deflate_removed is False:
+    raise ValueError('deflate extension processor not found')
+
+def web_socket_transfer_data(request):
+  while True:
+    rcvd = msgutil.receive_message(request)
+    opcode = request.ws_stream.get_last_received_opcode()
+    if (opcode == common.OPCODE_BINARY):
+      msgutil.send_message(request, rcvd, binary=True)
+    elif (opcode == common.OPCODE_TEXT):
+      msgutil.send_message(request, rcvd)
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_websocket_permessage_deflate_wsh.py
@@ -0,0 +1,22 @@
+from mod_pywebsocket import msgutil
+from mod_pywebsocket import common
+
+def web_socket_do_extra_handshake(request):
+  pmce_offered = False
+
+  if request.ws_requested_extensions is not None:
+    for extension_request in request.ws_requested_extensions:
+      if extension_request.name() == "permessage-deflate":
+        pmce_offered = True
+
+  if pmce_offered is False:
+    raise ValueError('permessage-deflate not offered')
+
+def web_socket_transfer_data(request):
+  while True:
+    rcvd = msgutil.receive_message(request)
+    opcode = request.ws_stream.get_last_received_opcode()
+    if (opcode == common.OPCODE_BINARY):
+      msgutil.send_message(request, rcvd, binary=True)
+    elif (opcode == common.OPCODE_TEXT):
+      msgutil.send_message(request, rcvd)
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -189,16 +189,19 @@ support-files =
   file_mozfiledataurl_doc.html
   file_mozfiledataurl_img.jpg
   file_mozfiledataurl_inner.html
   file_mozfiledataurl_text.txt
   file_restrictedEventSource.sjs
   file_websocket_basic_wsh.py
   file_websocket_hello_wsh.py
   file_websocket_http_resource.txt
+  file_websocket_permessage_deflate_wsh.py
+  file_websocket_permessage_deflate_disabled_wsh.py
+  file_websocket_permessage_deflate_rejected_wsh.py
   file_websocket_wsh.py
   file_x-frame-options_main.html
   file_x-frame-options_page.sjs
   file_xhtmlserializer_1.xhtml
   file_xhtmlserializer_1_bodyonly.xhtml
   file_xhtmlserializer_1_format.xhtml
   file_xhtmlserializer_1_linebreak.xhtml
   file_xhtmlserializer_1_links.xhtml
@@ -729,16 +732,18 @@ skip-if = toolkit == 'android' || e10s #
 [test_w3element_traversal.xhtml]
 [test_w3element_traversal_svg.html]
 [test_websocket.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s
 [test_websocket_basic.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_websocket_hello.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android'
+[test_websocket_permessage_deflate.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_x-frame-options.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(observerservice issue) b2g-debug(observerservice issue) b2g-desktop(observerservice issue)
 [test_xbl_userdata.xhtml]
 [test_xhr_abort_after_load.html]
 skip-if = toolkit == 'android'
 [test_xhr_forbidden_headers.html]
 [test_xhr_progressevents.html]
 skip-if = toolkit == 'android'
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_websocket_permessage_deflate.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Basic test of permessage compression websocket extension</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="testDeflate()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=792831">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var ws;
+var textMessage = "This is a text message";
+var binaryMessage = "This is a binary message";
+var testIdx = 0;
+var sendText = true;
+
+tests = [
+  // enable PMCE
+  [ true, true, "ws://mochi.test:8888/tests/dom/base/test/file_websocket_permessage_deflate" ],
+  // disable PMCE
+  [ false, false, "ws://mochi.test:8888/tests/dom/base/test/file_websocket_permessage_deflate_disabled" ],
+  // server rejects offered PMCE
+  [ true, false, "ws://mochi.test:8888/tests/dom/base/test/file_websocket_permessage_deflate_rejected" ] ]
+
+function ab2str(buf) {
+  return String.fromCharCode.apply(null, new Uint16Array(buf));
+}
+
+function str2ab(str) {
+  var buf = new ArrayBuffer(str.length*2);
+  var bufView = new Uint16Array(buf);
+  for (var i=0, strLen=str.length; i<strLen; i++) {
+    bufView[i] = str.charCodeAt(i);
+  }
+  return buf;
+}
+
+function sendMessage() {
+  if (sendText) {
+    ws.send(textMessage);
+  } else {
+    ws.binaryType = "arraybuffer";
+    ws.send(str2ab(binaryMessage));
+  }
+}
+
+function testDeflate() {
+  SpecialPowers.setBoolPref("network.websocket.extensions.permessage-deflate", tests[testIdx][0]);
+
+  ws = new WebSocket(tests[testIdx][2]);
+
+  ws.onopen = function(e) {
+    if (tests[testIdx][1]) {
+      is(ws.extensions, "permessage-deflate", "permessage-deflate not negotiated!");
+    } else {
+      is(ws.extensions, "", "permessage-deflate should not be negotiated!");
+    }
+
+    sendMessage();
+  }
+
+  ws.onclose = function(e) {
+    if (!e.wasClean) {
+      ok(false, "Connection should be closed cleanly!");
+      SimpleTest.finish();
+    }
+  }
+
+  ws.onerror = function(e) {
+    ok(false, "onerror called!");
+    SimpleTest.finish();
+  }
+
+  ws.onmessage = function(e) {
+    if (sendText) {
+      is(e.data, textMessage, "Text message not received successfully!");
+      sendText = false;
+      sendMessage();
+    } else {
+      ok(e.data instanceof ArrayBuffer, "Should receive an arraybuffer!");
+      is(ab2str(e.data), binaryMessage, "Binary message not received successfully!");
+      ws.close();
+
+      sendText = true;
+      testIdx++;
+      if (testIdx < tests.length) {
+        testDeflate();
+      } else {
+        SimpleTest.finish();
+      }
+    }
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1350,21 +1350,19 @@ pref("network.websocket.timeout.close", 
 // ping probe. 0 to disable.
 pref("network.websocket.timeout.ping.request", 0);
 
 // the deadline, expressed in seconds, for some read activity to occur after
 // generating a ping. If no activity happens then an error and unclean close
 // event is sent to the javascript websockets application
 pref("network.websocket.timeout.ping.response", 10);
 
-// Defines whether or not to try and negotiate the stream-deflate compression
-// extension with the websocket server. Stream-Deflate has been removed from
-// the standards track document, but can still be used by servers who opt
-// into it.
-pref("network.websocket.extensions.stream-deflate", false);
+// Defines whether or not to try to negotiate the permessage compression
+// extension with the websocket server.
+pref("network.websocket.extensions.permessage-deflate", true);
 
 // the maximum number of concurrent websocket sessions. By specification there
 // is never more than one handshake oustanding to an individual host at
 // one time.
 pref("network.websocket.max-connections", 200);
 
 // by default scripts loaded from a https:// origin can only open secure
 // (i.e. wss://) websockets.
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -709,16 +709,196 @@ private:
   nsRefPtr<WebSocketChannel>     mChannel;
   nsCOMPtr<nsISocketTransport>   mTransport;
   nsCOMPtr<nsIAsyncInputStream>  mSocketIn;
   nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
 };
 NS_IMPL_ISUPPORTS(CallOnTransportAvailable, nsIRunnable)
 
 //-----------------------------------------------------------------------------
+// PMCECompression
+//-----------------------------------------------------------------------------
+
+class PMCECompression
+{
+public:
+  explicit PMCECompression(bool aNoContextTakeover)
+    : mActive(false)
+    , mNoContextTakeover(aNoContextTakeover)
+    , mResetDeflater(false)
+    , mMessageDeflated(false)
+  {
+    MOZ_COUNT_CTOR(PMCECompression);
+
+    mDeflater.zalloc = mInflater.zalloc = Z_NULL;
+    mDeflater.zfree  = mInflater.zfree  = Z_NULL;
+    mDeflater.opaque = mInflater.opaque = Z_NULL;
+
+    if (deflateInit2(&mDeflater, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15,
+                     8, Z_DEFAULT_STRATEGY) == Z_OK) {
+      if (inflateInit2(&mInflater, -15) == Z_OK) {
+        mActive = true;
+      } else {
+        deflateEnd(&mDeflater);
+      }
+    }
+  }
+
+  ~PMCECompression()
+  {
+    MOZ_COUNT_DTOR(PMCECompression);
+
+    if (mActive) {
+      inflateEnd(&mInflater);
+      deflateEnd(&mDeflater);
+    }
+  }
+
+  bool Active()
+  {
+    return mActive;
+  }
+
+  void SetMessageDeflated()
+  {
+    MOZ_ASSERT(!mMessageDeflated);
+    mMessageDeflated = true;
+  }
+  bool IsMessageDeflated()
+  {
+    return mMessageDeflated;
+  }
+
+  bool UsingContextTakeover()
+  {
+    return !mNoContextTakeover;
+  }
+
+  nsresult Deflate(uint8_t *data, uint32_t dataLen, nsACString &_retval)
+  {
+    if (mResetDeflater || mNoContextTakeover) {
+      if (deflateReset(&mDeflater) != Z_OK) {
+        return NS_ERROR_UNEXPECTED;
+      }
+      mResetDeflater = false;
+    }
+
+    mDeflater.avail_out = kBufferLen;
+    mDeflater.next_out = mBuffer;
+    mDeflater.avail_in = dataLen;
+    mDeflater.next_in = data;
+
+    while (true) {
+      int zerr = deflate(&mDeflater, Z_SYNC_FLUSH);
+
+      if (zerr != Z_OK) {
+        mResetDeflater = true;
+        return NS_ERROR_UNEXPECTED;
+      }
+
+      uint32_t deflated = kBufferLen - mDeflater.avail_out;
+      if (deflated > 0) {
+        _retval.Append(reinterpret_cast<char *>(mBuffer), deflated);
+      }
+
+      mDeflater.avail_out = kBufferLen;
+      mDeflater.next_out = mBuffer;
+
+      if (mDeflater.avail_in > 0) {
+        continue; // There is still some data to deflate
+      }
+
+      if (deflated == kBufferLen) {
+        continue; // There was not enough space in the buffer
+      }
+
+      break;
+    }
+
+    if (_retval.Length() < 4) {
+      MOZ_ASSERT(false, "Expected trailing not found in deflated data!");
+      mResetDeflater = true;
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    _retval.Truncate(_retval.Length() - 4);
+
+    return NS_OK;
+  }
+
+  nsresult Inflate(uint8_t *data, uint32_t dataLen, nsACString &_retval)
+  {
+    mMessageDeflated = false;
+
+    Bytef trailingData[] = { 0x00, 0x00, 0xFF, 0xFF };
+    bool trailingDataUsed = false;
+
+    mInflater.avail_out = kBufferLen;
+    mInflater.next_out = mBuffer;
+    mInflater.avail_in = dataLen;
+    mInflater.next_in = data;
+
+    while (true) {
+      int zerr = inflate(&mInflater, Z_NO_FLUSH);
+
+      if (zerr == Z_STREAM_END) {
+        Bytef *saveNextIn = mInflater.next_in;
+        uint32_t saveAvailIn = mInflater.avail_in;
+        Bytef *saveNextOut = mInflater.next_out;
+        uint32_t saveAvailOut = mInflater.avail_out;
+
+        inflateReset(&mInflater);
+
+        mInflater.next_in = saveNextIn;
+        mInflater.avail_in = saveAvailIn;
+        mInflater.next_out = saveNextOut;
+        mInflater.avail_out = saveAvailOut;
+      } else if (zerr != Z_OK && zerr != Z_BUF_ERROR) {
+        return NS_ERROR_INVALID_CONTENT_ENCODING;
+      }
+
+      uint32_t inflated = kBufferLen - mInflater.avail_out;
+      if (inflated > 0) {
+        _retval.Append(reinterpret_cast<char *>(mBuffer), inflated);
+      }
+
+      mInflater.avail_out = kBufferLen;
+      mInflater.next_out = mBuffer;
+
+      if (mInflater.avail_in > 0) {
+        continue; // There is still some data to inflate
+      }
+
+      if (inflated == kBufferLen) {
+        continue; // There was not enough space in the buffer
+      }
+
+      if (!trailingDataUsed) {
+        trailingDataUsed = true;
+        mInflater.avail_in = sizeof(trailingData);
+        mInflater.next_in = trailingData;
+        continue;
+      }
+
+      return NS_OK;
+    }
+  }
+
+private:
+  bool                  mActive;
+  bool                  mNoContextTakeover;
+  bool                  mResetDeflater;
+  bool                  mMessageDeflated;
+  z_stream              mDeflater;
+  z_stream              mInflater;
+  const static uint32_t kBufferLen = 4096;
+  uint8_t               mBuffer[kBufferLen];
+};
+
+//-----------------------------------------------------------------------------
 // OutboundMessage
 //-----------------------------------------------------------------------------
 
 enum WsMsgType {
   kMsgTypeString = 0,
   kMsgTypeBinaryString,
   kMsgTypeStream,
   kMsgTypePing,
@@ -734,25 +914,26 @@ static const char* msgNames[] = {
   "pong",
   "close"
 };
 
 class OutboundMessage
 {
 public:
   OutboundMessage(WsMsgType type, nsCString *str)
-    : mMsgType(type)
+    : mMsgType(type), mDeflated(false), mOrigLength(0)
   {
     MOZ_COUNT_CTOR(OutboundMessage);
     mMsg.pString = str;
     mLength = str ? str->Length() : 0;
   }
 
   OutboundMessage(nsIInputStream *stream, uint32_t length)
-    : mMsgType(kMsgTypeStream), mLength(length)
+    : mMsgType(kMsgTypeStream), mLength(length), mDeflated(false)
+    , mOrigLength(0)
   {
     MOZ_COUNT_CTOR(OutboundMessage);
     mMsg.pStream = stream;
     mMsg.pStream->AddRef();
   }
 
  ~OutboundMessage() {
     MOZ_COUNT_DTOR(OutboundMessage);
@@ -772,16 +953,17 @@ public:
         break;
       case kMsgTypeFin:
         break;    // do-nothing: avoid compiler warning
     }
   }
 
   WsMsgType GetMsgType() const { return mMsgType; }
   int32_t Length() const { return mLength; }
+  int32_t OrigLength() const { return mDeflated ? mOrigLength : mLength; }
 
   uint8_t* BeginWriting() {
     NS_ABORT_IF_FALSE(mMsgType != kMsgTypeStream,
                       "Stream should have been converted to string by now");
     return (uint8_t *)(mMsg.pString ? mMsg.pString->BeginWriting() : nullptr);
   }
 
   uint8_t* BeginReading() {
@@ -809,23 +991,67 @@ public:
     mMsg.pStream->Close();
     mMsg.pStream->Release();
     mMsg.pString = temp.forget();
     mMsgType = kMsgTypeBinaryString;
 
     return NS_OK;
   }
 
+  bool DeflatePayload(PMCECompression *aCompressor)
+  {
+    NS_ABORT_IF_FALSE(mMsgType != kMsgTypeStream,
+                      "Stream should have been converted to string by now");
+    MOZ_ASSERT(!mDeflated);
+
+    nsresult rv;
+
+    if (mLength == 0) {
+      // Empty message
+      return false;
+    }
+
+    nsAutoPtr<nsCString> temp(new nsCString());
+    rv = aCompressor->Deflate(BeginReading(), mLength, *temp);
+    if (NS_FAILED(rv)) {
+      LOG(("WebSocketChannel::OutboundMessage: Deflating payload failed "
+           "[rv=0x%08x]\n", rv));
+      return false;
+    }
+
+    if (!aCompressor->UsingContextTakeover() && temp->Length() > mLength) {
+      // When "client_no_context_takeover" was negotiated, do not send deflated
+      // payload if it's larger that the original one. OTOH, it makes sense
+      // to send the larger deflated payload when the sliding window is not
+      // reset between messages because if we would skip some deflated block
+      // we would need to empty the sliding window which could affect the
+      // compression of the subsequent messages.
+      LOG(("WebSocketChannel::OutboundMessage: Not deflating message since the "
+           "deflated payload is larger than the original one [deflated=%d, "
+           "original=%d]", temp->Length(), mLength));
+      return false;
+    }
+
+    mOrigLength = mLength;
+    mDeflated = true;
+    mLength = temp->Length();
+    delete mMsg.pString;
+    mMsg.pString = temp.forget();
+    return true;
+  }
+
 private:
   union {
     nsCString      *pString;
     nsIInputStream *pStream;
   }                           mMsg;
   WsMsgType                   mMsgType;
   uint32_t                    mLength;
+  bool                        mDeflated;
+  uint32_t                    mOrigLength;
 };
 
 //-----------------------------------------------------------------------------
 // OutboundEnqueuer
 //-----------------------------------------------------------------------------
 
 class OutboundEnqueuer MOZ_FINAL : public nsIRunnable
 {
@@ -844,142 +1070,16 @@ public:
 private:
   ~OutboundEnqueuer() {}
 
   nsRefPtr<WebSocketChannel>  mChannel;
   OutboundMessage            *mMessage;
 };
 NS_IMPL_ISUPPORTS(OutboundEnqueuer, nsIRunnable)
 
-//-----------------------------------------------------------------------------
-// nsWSCompression
-//
-// similar to nsDeflateConverter except for the mandatory FLUSH calls
-// required by websocket and the absence of the deflate termination
-// block which is appropriate because it would create data bytes after
-// sending the websockets CLOSE message.
-//-----------------------------------------------------------------------------
-
-class nsWSCompression
-{
-public:
-  nsWSCompression(nsIStreamListener *aListener,
-                  nsISupports *aContext)
-    : mActive(false),
-      mContext(aContext),
-      mListener(aListener)
-  {
-    MOZ_COUNT_CTOR(nsWSCompression);
-
-    mZlib.zalloc = allocator;
-    mZlib.zfree = destructor;
-    mZlib.opaque = Z_NULL;
-
-    // Initialize the compressor - these are all the normal zlib
-    // defaults except window size is set to -15 instead of +15.
-    // This is the zlib way of specifying raw RFC 1951 output instead
-    // of the zlib rfc 1950 format which has a 2 byte header and
-    // adler checksum as a trailer
-
-    nsresult rv;
-    mStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
-    if (NS_SUCCEEDED(rv) && aContext && aListener &&
-      deflateInit2(&mZlib, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8,
-                   Z_DEFAULT_STRATEGY) == Z_OK) {
-      mActive = true;
-    }
-  }
-
-  ~nsWSCompression()
-  {
-    MOZ_COUNT_DTOR(nsWSCompression);
-
-    if (mActive)
-      deflateEnd(&mZlib);
-  }
-
-  bool Active()
-  {
-    return mActive;
-  }
-
-  nsresult Deflate(uint8_t *buf1, uint32_t buf1Len,
-                   uint8_t *buf2, uint32_t buf2Len)
-  {
-    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
-                          "not socket thread");
-    NS_ABORT_IF_FALSE(mActive, "not active");
-
-    mZlib.avail_out = kBufferLen;
-    mZlib.next_out = mBuffer;
-    mZlib.avail_in = buf1Len;
-    mZlib.next_in = buf1;
-
-    nsresult rv;
-
-    while (mZlib.avail_in > 0) {
-      deflate(&mZlib, (buf2Len > 0) ? Z_NO_FLUSH : Z_SYNC_FLUSH);
-      rv = PushData();
-      if (NS_FAILED(rv))
-        return rv;
-      mZlib.avail_out = kBufferLen;
-      mZlib.next_out = mBuffer;
-    }
-
-    mZlib.avail_in = buf2Len;
-    mZlib.next_in = buf2;
-
-    while (mZlib.avail_in > 0) {
-      deflate(&mZlib, Z_SYNC_FLUSH);
-      rv = PushData();
-      if (NS_FAILED(rv))
-        return rv;
-      mZlib.avail_out = kBufferLen;
-      mZlib.next_out = mBuffer;
-    }
-
-    return NS_OK;
-  }
-
-private:
-
-  // use zlib data types
-  static void *allocator(void *opaque, uInt items, uInt size)
-  {
-    return moz_xmalloc(items * size);
-  }
-
-  static void destructor(void *opaque, void *addr)
-  {
-    moz_free(addr);
-  }
-
-  nsresult PushData()
-  {
-    uint32_t bytesToWrite = kBufferLen - mZlib.avail_out;
-    if (bytesToWrite > 0) {
-      mStream->ShareData(reinterpret_cast<char *>(mBuffer), bytesToWrite);
-      nsresult rv =
-        mListener->OnDataAvailable(nullptr, mContext, mStream, 0, bytesToWrite);
-      if (NS_FAILED(rv))
-        return rv;
-    }
-    return NS_OK;
-  }
-
-  bool                            mActive;
-  z_stream                        mZlib;
-  nsCOMPtr<nsIStringInputStream>  mStream;
-
-  nsISupports                    *mContext;     /* weak ref */
-  nsIStreamListener              *mListener;    /* weak ref */
-
-  const static int32_t            kBufferLen = 4096;
-  uint8_t                         mBuffer[kBufferLen];
-};
 
 //-----------------------------------------------------------------------------
 // WebSocketChannel
 //-----------------------------------------------------------------------------
 
 uint32_t WebSocketChannel::sSerialSeed = 0;
 
 WebSocketChannel::WebSocketChannel() :
@@ -991,35 +1091,34 @@ WebSocketChannel::WebSocketChannel() :
   mGotUpgradeOK(0),
   mRecvdHttpUpgradeTransport(0),
   mRequestedClose(0),
   mClientClosed(0),
   mServerClosed(0),
   mStopped(0),
   mCalledOnStop(0),
   mPingOutstanding(0),
-  mAllowCompression(1),
   mAutoFollowRedirects(0),
   mReleaseOnTransmit(0),
   mTCPClosed(0),
   mOpenedHttpChannel(0),
   mDataStarted(0),
   mIncrementedSessionCount(0),
   mDecrementedSessionCount(0),
+  mAllowPMCE(1),
   mMaxMessageSize(INT32_MAX),
   mStopOnClose(NS_OK),
   mServerCloseCode(CLOSE_ABNORMAL),
   mScriptCloseCode(0),
   mFragmentOpcode(kContinuation),
   mFragmentAccumulator(0),
   mBuffered(0),
   mBufferSize(kIncomingBufferInitialSize),
   mCurrentOut(nullptr),
   mCurrentOutSent(0),
-  mCompressor(nullptr),
   mDynamicOutputSize(0),
   mDynamicOutput(nullptr),
   mPrivateBrowsing(false),
   mConnectionLogService(nullptr),
   mCountRecv(0),
   mCountSent(0),
   mAppId(NECKO_NO_APP_ID)
 {
@@ -1047,17 +1146,16 @@ WebSocketChannel::~WebSocketChannel()
     MOZ_ASSERT(mCalledOnStop, "WebSocket was opened but OnStop was not called");
     MOZ_ASSERT(mStopped, "WebSocket was opened but never stopped");
   }
   MOZ_ASSERT(!mCancelable, "DNS/Proxy Request still alive at destruction");
   MOZ_ASSERT(!mConnecting, "Should not be connecting in destructor");
 
   moz_free(mBuffer);
   moz_free(mDynamicOutput);
-  delete mCompressor;
   delete mCurrentOut;
 
   while ((mCurrentOut = (OutboundMessage *) mOutgoingPingMessages.PopFront()))
     delete mCurrentOut;
   while ((mCurrentOut = (OutboundMessage *) mOutgoingPongMessages.PopFront()))
     delete mCurrentOut;
   while ((mCurrentOut = (OutboundMessage *) mOutgoingMessages.PopFront()))
     delete mCurrentOut;
@@ -1310,16 +1408,18 @@ WebSocketChannel::UpdateReadBuffer(uint8
 }
 
 nsresult
 WebSocketChannel::ProcessInput(uint8_t *buffer, uint32_t count)
 {
   LOG(("WebSocketChannel::ProcessInput %p [%d %d]\n", this, count, mBuffered));
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
 
+  nsresult rv;
+
   // The purpose of ping/pong is to actively probe the peer so that an
   // unreachable peer is not mistaken for a period of idleness. This
   // implementation accepts any application level read activity as a sign of
   // life, it does not necessarily have to be a pong.
   ResetPingTimer();
 
   uint32_t avail;
 
@@ -1335,17 +1435,17 @@ WebSocketChannel::ProcessInput(uint8_t *
   }
 
   uint8_t *payload;
   uint32_t totalAvail = avail;
 
   while (avail >= 2) {
     int64_t payloadLength64 = mFramePtr[1] & 0x7F;
     uint8_t finBit  = mFramePtr[0] & kFinalFragBit;
-    uint8_t rsvBits = mFramePtr[0] & 0x70;
+    uint8_t rsvBits = mFramePtr[0] & kRsvBitsMask;
     uint8_t maskBit = mFramePtr[1] & kMaskBit;
     uint8_t opcode  = mFramePtr[0] & 0x0F;
 
     uint32_t framingLength = 2;
     if (maskBit)
       framingLength += 4;
 
     if (payloadLength64 < 126) {
@@ -1403,18 +1503,27 @@ WebSocketChannel::ProcessInput(uint8_t *
 
     // Control codes are required to have the fin bit set
     if (!finBit && (opcode & kControlFrameMask)) {
       LOG(("WebSocketChannel:: fragmented control frame code %d\n", opcode));
       return NS_ERROR_ILLEGAL_VALUE;
     }
 
     if (rsvBits) {
-      LOG(("WebSocketChannel:: unexpected reserved bits %x\n", rsvBits));
-      return NS_ERROR_ILLEGAL_VALUE;
+      // PMCE sets RSV1 bit in the first fragment when the non-control frame
+      // is deflated
+      if (mPMCECompressor && rsvBits == kRsv1Bit && mFragmentAccumulator == 0 &&
+          !(opcode & kControlFrameMask)) {
+        mPMCECompressor->SetMessageDeflated();
+        LOG(("WebSocketChannel::ProcessInput: received deflated frame\n"));
+      } else {
+        LOG(("WebSocketChannel::ProcessInput: unexpected reserved bits %x\n",
+             rsvBits));
+        return NS_ERROR_ILLEGAL_VALUE;
+      }
     }
 
     if (!finBit || opcode == kContinuation) {
       // This is part of a fragment response
 
       // Only the first frame has a non zero op code: Make sure we don't see a
       // first frame while some old fragments are open
       if ((mFragmentAccumulator != 0) && (opcode != kContinuation)) {
@@ -1468,22 +1577,37 @@ WebSocketChannel::ProcessInput(uint8_t *
     if (mServerClosed) {
       LOG(("WebSocketChannel:: ignoring read frame code %d after close\n",
                  opcode));
       // nop
     } else if (mStopped) {
       LOG(("WebSocketChannel:: ignoring read frame code %d after completion\n",
            opcode));
     } else if (opcode == kText) {
-      LOG(("WebSocketChannel:: text frame received\n"));
+      bool isDeflated = mPMCECompressor && mPMCECompressor->IsMessageDeflated();
+      LOG(("WebSocketChannel:: %stext frame received\n",
+           isDeflated ? "deflated " : ""));
+
       if (mListener) {
         nsCString utf8Data;
-        if (!utf8Data.Assign((const char *)payload, payloadLength,
-                             mozilla::fallible_t()))
-          return NS_ERROR_OUT_OF_MEMORY;
+
+        if (isDeflated) {
+          rv = mPMCECompressor->Inflate(payload, payloadLength, utf8Data);
+          if (NS_FAILED(rv)) {
+            return rv;
+          }
+          LOG(("WebSocketChannel:: message successfully inflated "
+               "[origLength=%d, newLength=%d]\n", payloadLength,
+               utf8Data.Length()));
+        } else {
+          if (!utf8Data.Assign((const char *)payload, payloadLength,
+                               mozilla::fallible_t())) {
+            return NS_ERROR_OUT_OF_MEMORY;
+          }
+        }
 
         // Section 8.1 says to fail connection if invalid utf-8 in text message
         if (!IsUTF8(utf8Data, false)) {
           LOG(("WebSocketChannel:: text frame invalid utf-8\n"));
           return NS_ERROR_CANNOT_CONVERT_DATA;
         }
 
         mTargetThread->Dispatch(new CallOnMessageAvailable(this, utf8Data, -1),
@@ -1563,22 +1687,41 @@ WebSocketChannel::ProcessInput(uint8_t *
         ::memmove(mFramePtr, payload + payloadLength, avail - payloadLength);
         payload = mFramePtr;
         avail -= payloadLength;
         if (mBuffered)
           mBuffered -= framingLength + payloadLength;
         payloadLength = 0;
       }
     } else if (opcode == kBinary) {
-      LOG(("WebSocketChannel:: binary frame received\n"));
+      bool isDeflated = mPMCECompressor && mPMCECompressor->IsMessageDeflated();
+      LOG(("WebSocketChannel:: %sbinary frame received\n",
+           isDeflated ? "deflated " : ""));
+
       if (mListener) {
-        nsCString binaryData((const char *)payload, payloadLength);
-        mTargetThread->Dispatch(new CallOnMessageAvailable(this, binaryData,
-                                                           payloadLength),
-                                NS_DISPATCH_NORMAL);
+        nsCString binaryData;
+
+        if (isDeflated) {
+          rv = mPMCECompressor->Inflate(payload, payloadLength, binaryData);
+          if (NS_FAILED(rv)) {
+            return rv;
+          }
+          LOG(("WebSocketChannel:: message successfully inflated "
+               "[origLength=%d, newLength=%d]\n", payloadLength,
+               binaryData.Length()));
+        } else {
+          if (!binaryData.Assign((const char *)payload, payloadLength,
+                                 mozilla::fallible_t())) {
+            return NS_ERROR_OUT_OF_MEMORY;
+          }
+        }
+
+        mTargetThread->Dispatch(
+          new CallOnMessageAvailable(this, binaryData, binaryData.Length()),
+          NS_DISPATCH_NORMAL);
         // To add the header to 'Networking Dashboard' log
         if (mConnectionLogService && !mPrivateBrowsing) {
           mConnectionLogService->NewMsgReceived(mHost, mSerial, count);
           LOG(("Added new received msg for %s", mHost.get()));
         }
       }
     } else if (opcode != kContinuation) {
       /* unknown opcode */
@@ -1853,16 +1996,29 @@ WebSocketChannel::PrimeNewOutgoingMessag
     case kMsgTypeBinaryString:
       mOutHeader[0] = kFinalFragBit | kBinary;
       break;
     case kMsgTypeFin:
       NS_ABORT_IF_FALSE(false, "unreachable");  // avoid compiler warning
       break;
     }
 
+    // deflate the payload if PMCE is negotiated
+    if (mPMCECompressor &&
+        (msgType == kMsgTypeString || msgType == kMsgTypeBinaryString)) {
+      if (mCurrentOut->DeflatePayload(mPMCECompressor)) {
+        // The payload was deflated successfully, set RSV1 bit
+        mOutHeader[0] |= kRsv1Bit;
+
+        LOG(("WebSocketChannel::PrimeNewOutgoingMessage %p current msg %p was "
+             "deflated [origLength=%d, newLength=%d].\n", this, mCurrentOut,
+             mCurrentOut->OrigLength(), mCurrentOut->Length()));
+      }
+    }
+
     if (mCurrentOut->Length() < 126) {
       mOutHeader[1] = mCurrentOut->Length() | kMaskBit;
       mHdrOutToSend = 6;
     } else if (mCurrentOut->Length() <= 0xffff) {
       mOutHeader[1] = 126 | kMaskBit;
       NetworkEndian::writeUint16(mOutHeader + sizeof(uint16_t),
                                  mCurrentOut->Length());
       mHdrOutToSend = 8;
@@ -1914,32 +2070,16 @@ WebSocketChannel::PrimeNewOutgoingMessag
 
   // for small frames, copy it all together for a contiguous write
   if (len && len <= kCopyBreak) {
     memcpy(mOutHeader + mHdrOutToSend, mCurrentOut->BeginWriting(), len);
     mHdrOutToSend += len;
     mCurrentOutSent = len;
   }
 
-  if (len && mCompressor) {
-    // assume a 1/3 reduction in size for sizing the buffer
-    // the buffer is used multiple times if necessary
-    uint32_t currentHeaderSize = mHdrOutToSend;
-    mHdrOutToSend = 0;
-
-    EnsureHdrOut(32 + (currentHeaderSize + len - mCurrentOutSent) / 2 * 3);
-    mCompressor->Deflate(mOutHeader, currentHeaderSize,
-                         mCurrentOut->BeginReading() + mCurrentOutSent,
-                         len - mCurrentOutSent);
-
-    // All of the compressed data now resides in {mHdrOut, mHdrOutToSend}
-    // so do not send the body again
-    mCurrentOutSent = len;
-  }
-
   // Transmitting begins - mHdrOutToSend bytes from mOutHeader and
   // mCurrentOut->Length() bytes from mCurrentOut. The latter may be
   // coaleseced into the former for small messages or as the result of the
   // compression process,
 }
 
 void
 WebSocketChannel::DeleteCurrentOutGoingMessage()
@@ -2116,21 +2256,17 @@ WebSocketChannel::StopSession(nsresult r
     CleanupConnection();
   }
 
   if (mCancelable) {
     mCancelable->Cancel(NS_ERROR_UNEXPECTED);
     mCancelable = nullptr;
   }
 
-  mInflateReader = nullptr;
-  mInflateStream = nullptr;
-
-  delete mCompressor;
-  mCompressor = nullptr;
+  mPMCECompressor = nullptr;
 
   if (!mCalledOnStop) {
     mCalledOnStop = 1;
     mTargetThread->Dispatch(new CallOnStop(this, reason),
                             NS_DISPATCH_NORMAL);
   }
 }
 
@@ -2213,65 +2349,81 @@ WebSocketChannel::HandleExtensions()
   nsresult rv;
   nsAutoCString extensions;
 
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
 
   rv = mHttpChannel->GetResponseHeader(
     NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"), extensions);
   if (NS_SUCCEEDED(rv)) {
+    LOG(("WebSocketChannel::HandleExtensions: received "
+         "Sec-WebSocket-Extensions header: %s\n", extensions.get()));
+
+    extensions.CompressWhitespace();
+
     if (!extensions.IsEmpty()) {
-      if (!extensions.EqualsLiteral("deflate-stream")) {
-        LOG(("WebSocketChannel::OnStartRequest: "
-             "HTTP Sec-WebSocket-Exensions negotiated unknown value %s\n",
+      if (StringBeginsWith(extensions,
+                           NS_LITERAL_CSTRING("permessage-deflate"))) {
+        if (!mAllowPMCE) {
+          LOG(("WebSocketChannel::HandleExtensions: "
+               "Recvd permessage-deflate which wasn't offered\n"));
+          AbortSession(NS_ERROR_ILLEGAL_VALUE);
+          return NS_ERROR_ILLEGAL_VALUE;
+        }
+
+        nsAutoCString param;
+
+        int32_t delimPos = extensions.FindChar(';');
+        if (delimPos != kNotFound) {
+          param = Substring(extensions, delimPos + 1);
+          param.CompressWhitespace(true, false);
+          extensions.Truncate(delimPos);
+          extensions.CompressWhitespace(false, true);
+        }
+ 
+        if (!extensions.EqualsLiteral("permessage-deflate")) {
+          LOG(("WebSocketChannel::HandleExtensions: "
+               "HTTP Sec-WebSocket-Extensions negotiated unknown value %s\n",
+               extensions.get()));
+          AbortSession(NS_ERROR_ILLEGAL_VALUE);
+          return NS_ERROR_ILLEGAL_VALUE;
+        }
+
+        bool noContextTakeover = false;
+        if (!param.IsEmpty()) {
+          if (param.EqualsLiteral("client_no_context_takeover")) {
+            noContextTakeover = true;
+          } else {
+            LOG(("WebSocketChannel::HandleExtensions: "
+                 "HTTP permessage-deflate extension negotiated unknown "
+                 "parameter %s\n", param.get()));
+            AbortSession(NS_ERROR_ILLEGAL_VALUE);
+            return NS_ERROR_ILLEGAL_VALUE;
+          }
+        }
+
+        mPMCECompressor = new PMCECompression(noContextTakeover);
+        if (mPMCECompressor->Active()) {
+          LOG(("WebSocketChannel::HandleExtensions: PMCE negotiated, %susing "
+               "context takeover\n", noContextTakeover ? "NOT " : ""));
+        } else {
+          LOG(("WebSocketChannel::HandleExtensions: Cannot init PMCE "
+               "compression object\n"));
+          mPMCECompressor = nullptr;
+          AbortSession(NS_ERROR_UNEXPECTED);
+          return NS_ERROR_UNEXPECTED;
+        }
+      } else {
+        LOG(("WebSocketChannel::HandleExtensions: "
+             "HTTP Sec-WebSocket-Extensions negotiated unknown value %s\n",
              extensions.get()));
         AbortSession(NS_ERROR_ILLEGAL_VALUE);
         return NS_ERROR_ILLEGAL_VALUE;
       }
 
-      if (!mAllowCompression) {
-        LOG(("WebSocketChannel::HandleExtensions: "
-             "Recvd Compression Extension that wasn't offered\n"));
-        AbortSession(NS_ERROR_ILLEGAL_VALUE);
-        return NS_ERROR_ILLEGAL_VALUE;
-      }
-
-      nsCOMPtr<nsIStreamConverterService> serv =
-        do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
-      if (NS_FAILED(rv)) {
-        LOG(("WebSocketChannel:: Cannot find compression service\n"));
-        AbortSession(NS_ERROR_UNEXPECTED);
-        return NS_ERROR_UNEXPECTED;
-      }
-
-      rv = serv->AsyncConvertData("deflate", "uncompressed", this, nullptr,
-                                  getter_AddRefs(mInflateReader));
-
-      if (NS_FAILED(rv)) {
-        LOG(("WebSocketChannel:: Cannot find inflate listener\n"));
-        AbortSession(NS_ERROR_UNEXPECTED);
-        return NS_ERROR_UNEXPECTED;
-      }
-
-      mInflateStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
-
-      if (NS_FAILED(rv)) {
-        LOG(("WebSocketChannel:: Cannot find inflate stream\n"));
-        AbortSession(NS_ERROR_UNEXPECTED);
-        return NS_ERROR_UNEXPECTED;
-      }
-
-      mCompressor = new nsWSCompression(this, mSocketOut);
-      if (!mCompressor->Active()) {
-        LOG(("WebSocketChannel:: Cannot init deflate object\n"));
-        delete mCompressor;
-        mCompressor = nullptr;
-        AbortSession(NS_ERROR_UNEXPECTED);
-        return NS_ERROR_UNEXPECTED;
-      }
       mNegotiatedExtensions = extensions;
     }
   }
 
   return NS_OK;
 }
 
 nsresult
@@ -2311,19 +2463,19 @@ WebSocketChannel::SetupRequest()
   if (!mOrigin.IsEmpty())
     mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), mOrigin,
                                    false);
 
   if (!mProtocol.IsEmpty())
     mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"),
                                    mProtocol, true);
 
-  if (mAllowCompression)
+  if (mAllowPMCE)
     mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"),
-                                   NS_LITERAL_CSTRING("deflate-stream"),
+                                   NS_LITERAL_CSTRING("permessage-deflate"),
                                    false);
 
   uint8_t      *secKey;
   nsAutoCString secKeyString;
 
   rv = mRandomGenerator->GenerateRandomBytes(16, &secKey);
   NS_ENSURE_SUCCESS(rv, rv);
   char* b64 = PL_Base64Encode((const char *)secKey, 16, nullptr);
@@ -2838,20 +2990,20 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI
     if (NS_SUCCEEDED(rv) && !mClientSetPingInterval) {
       mPingInterval = clamped(intpref, 0, 86400) * 1000;
     }
     rv = prefService->GetIntPref("network.websocket.timeout.ping.response",
                                  &intpref);
     if (NS_SUCCEEDED(rv) && !mClientSetPingTimeout) {
       mPingResponseTimeout = clamped(intpref, 1, 3600) * 1000;
     }
-    rv = prefService->GetBoolPref("network.websocket.extensions.stream-deflate",
+    rv = prefService->GetBoolPref("network.websocket.extensions.permessage-deflate",
                                   &boolpref);
     if (NS_SUCCEEDED(rv)) {
-      mAllowCompression = boolpref ? 1 : 0;
+      mAllowPMCE = boolpref ? 1 : 0;
     }
     rv = prefService->GetBoolPref("network.websocket.auto-follow-http-redirects",
                                   &boolpref);
     if (NS_SUCCEEDED(rv)) {
       mAutoFollowRedirects = boolpref ? 1 : 0;
     }
     rv = prefService->GetIntPref
       ("network.websocket.max-connections", &intpref);
@@ -3288,21 +3440,18 @@ WebSocketChannel::OnStopRequest(nsIReque
 NS_IMETHODIMP
 WebSocketChannel::OnInputStreamReady(nsIAsyncInputStream *aStream)
 {
   LOG(("WebSocketChannel::OnInputStreamReady() %p\n", this));
   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
 
   if (!mSocketIn) // did we we clean up the socket after scheduling InputReady?
     return NS_OK;
-  
-  nsRefPtr<nsIStreamListener>    deleteProtector1(mInflateReader);
-  nsRefPtr<nsIStringInputStream> deleteProtector2(mInflateStream);
-
-  // this is after the  http upgrade - so we are speaking websockets
+
+  // this is after the http upgrade - so we are speaking websockets
   char  buffer[2048];
   uint32_t count;
   nsresult rv;
 
   do {
     rv = mSocketIn->Read((char *)buffer, 2048, &count);
     LOG(("WebSocketChannel::OnInputStreamReady: read %u rv %x\n", count, rv));
 
@@ -3325,24 +3474,17 @@ WebSocketChannel::OnInputStreamReady(nsI
       AbortSession(NS_BASE_STREAM_CLOSED);
       return NS_OK;
     }
 
     if (mStopped) {
       continue;
     }
 
-    if (mInflateReader) {
-      mInflateStream->ShareData(buffer, count);
-      rv = mInflateReader->OnDataAvailable(nullptr, mSocketIn, mInflateStream, 
-                                           0, count);
-    } else {
-      rv = ProcessInput((uint8_t *)buffer, count);
-    }
-
+    rv = ProcessInput((uint8_t *)buffer, count);
     if (NS_FAILED(rv)) {
       AbortSession(rv);
       return rv;
     }
   } while (NS_SUCCEEDED(rv) && mSocketIn);
 
   return NS_OK;
 }
@@ -3407,19 +3549,19 @@ WebSocketChannel::OnOutputStreamReady(ns
       } else {
         mHdrOut += amtSent;
         mHdrOutToSend -= amtSent;
         mSocketOut->AsyncWait(this, 0, 0, mSocketThread);
       }
     } else {
       if (amtSent == toSend) {
         if (!mStopped) {
-          mTargetThread->Dispatch(new CallAcknowledge(this,
-                                                      mCurrentOut->Length()),
-                                  NS_DISPATCH_NORMAL);
+          mTargetThread->Dispatch(
+            new CallAcknowledge(this, mCurrentOut->OrigLength()),
+            NS_DISPATCH_NORMAL);
         }
         DeleteCurrentOutGoingMessage();
         PrimeNewOutgoingMessage();
       } else {
         mCurrentOutSent += amtSent;
         mSocketOut->AsyncWait(this, 0, 0, mSocketThread);
       }
     }
@@ -3437,84 +3579,20 @@ WebSocketChannel::OnDataAvailable(nsIReq
                                     nsISupports *aContext,
                                     nsIInputStream *aInputStream,
                                     uint64_t aOffset,
                                     uint32_t aCount)
 {
   LOG(("WebSocketChannel::OnDataAvailable() %p [%p %p %p %llu %u]\n",
          this, aRequest, aContext, aInputStream, aOffset, aCount));
 
-  if (aContext == mSocketIn) {
-    // This is the deflate decoder
-
-    LOG(("WebSocketChannel::OnDataAvailable: Deflate Data %u\n",
-             aCount));
-
-    uint8_t  buffer[2048];
-    uint32_t maxRead;
-    uint32_t count;
-    nsresult rv = NS_OK;  // aCount always > 0, so this just avoids warning
-
-    while (aCount > 0) {
-      if (mStopped)
-        return NS_BASE_STREAM_CLOSED;
-
-      maxRead = std::min(2048U, aCount);
-      rv = aInputStream->Read((char *)buffer, maxRead, &count);
-      LOG(("WebSocketChannel::OnDataAvailable: InflateRead read %u rv %x\n",
-           count, rv));
-      if (NS_FAILED(rv) || count == 0) {
-        AbortSession(NS_ERROR_UNEXPECTED);
-        break;
-      }
-
-      aCount -= count;
-      rv = ProcessInput(buffer, count);
-      if (NS_FAILED(rv)) {
-        AbortSession(rv);
-        break;
-      }
-    }
-    return rv;
-  }
-
-  if (aContext == mSocketOut) {
-    // This is the deflate encoder
-
-    uint32_t maxRead;
-    uint32_t count;
-    nsresult rv;
-
-    while (aCount > 0) {
-      if (mStopped)
-        return NS_BASE_STREAM_CLOSED;
-
-      maxRead = std::min(2048U, aCount);
-      EnsureHdrOut(mHdrOutToSend + aCount);
-      rv = aInputStream->Read((char *)mHdrOut + mHdrOutToSend, maxRead, &count);
-      LOG(("WebSocketChannel::OnDataAvailable: DeflateWrite read %u rv %x\n", 
-           count, rv));
-      if (NS_FAILED(rv) || count == 0) {
-        AbortSession(rv);
-        break;
-      }
-
-      mHdrOutToSend += count;
-      aCount -= count;
-    }
-    return NS_OK;
-  }
-
-
-  // Otherwise, this is the HTTP OnDataAvailable Method, which means
-  // this is http data in response to the upgrade request and
-  // there should be no http response body if the upgrade succeeded
-
-  // This generally should be caught by a non 101 response code in
-  // OnStartRequest().. so we can ignore the data here
+  // This is the HTTP OnDataAvailable Method, which means this is http data in
+  // response to the upgrade request and there should be no http response body
+  // if the upgrade succeeded. This generally should be caught by a non 101
+  // response code in OnStartRequest().. so we can ignore the data here
 
   LOG(("WebSocketChannel::OnDataAvailable: HTTP data unexpected len>=%u\n",
          aCount));
 
   return NS_OK;
 }
 
 nsresult
--- a/netwerk/protocol/websocket/WebSocketChannel.h
+++ b/netwerk/protocol/websocket/WebSocketChannel.h
@@ -38,17 +38,17 @@ class nsIRandomGenerator;
 class nsISocketTransport;
 class nsIURI;
 
 namespace mozilla { namespace net {
 
 class OutboundMessage;
 class OutboundEnqueuer;
 class nsWSAdmissionManager;
-class nsWSCompression;
+class PMCECompression;
 class CallOnMessageAvailable;
 class CallOnStop;
 class CallOnServerClose;
 class CallAcknowledge;
 
 // Used to enforce "1 connecting websocket per host" rule, and reconnect delays
 enum wsConnectingState {
   NOT_CONNECTING = 0,     // Not yet (or no longer) trying to open connection
@@ -113,16 +113,18 @@ public:
     kClose =        0x8,
     kPing =         0x9,
     kPong =         0xA
   };
 
   const static uint32_t kControlFrameMask   = 0x8;
   const static uint8_t kMaskBit             = 0x80;
   const static uint8_t kFinalFragBit        = 0x80;
+  const static uint8_t kRsvBitsMask         = 0x70;
+  const static uint8_t kRsv1Bit             = 0x40;
 
 protected:
   virtual ~WebSocketChannel();
 
 private:
   friend class OutboundEnqueuer;
   friend class nsWSAdmissionManager;
   friend class FailDelayManager;
@@ -216,24 +218,24 @@ private:
   uint32_t                        mGotUpgradeOK              : 1;
   uint32_t                        mRecvdHttpUpgradeTransport : 1;
   uint32_t                        mRequestedClose            : 1;
   uint32_t                        mClientClosed              : 1;
   uint32_t                        mServerClosed              : 1;
   uint32_t                        mStopped                   : 1;
   uint32_t                        mCalledOnStop              : 1;
   uint32_t                        mPingOutstanding           : 1;
-  uint32_t                        mAllowCompression          : 1;
   uint32_t                        mAutoFollowRedirects       : 1;
   uint32_t                        mReleaseOnTransmit         : 1;
   uint32_t                        mTCPClosed                 : 1;
   uint32_t                        mOpenedHttpChannel         : 1;
   uint32_t                        mDataStarted               : 1;
   uint32_t                        mIncrementedSessionCount   : 1;
   uint32_t                        mDecrementedSessionCount   : 1;
+  uint32_t                        mAllowPMCE                 : 1;
 
   int32_t                         mMaxMessageSize;
   nsresult                        mStopOnClose;
   uint16_t                        mServerCloseCode;
   nsCString                       mServerCloseReason;
   uint16_t                        mScriptCloseCode;
   nsCString                       mScriptCloseReason;
 
@@ -245,31 +247,29 @@ private:
   const static uint32_t kIncomingBufferStableSize = 128 * 1024;
 
   uint8_t                        *mFramePtr;
   uint8_t                        *mBuffer;
   uint8_t                         mFragmentOpcode;
   uint32_t                        mFragmentAccumulator;
   uint32_t                        mBuffered;
   uint32_t                        mBufferSize;
-  nsCOMPtr<nsIStreamListener>     mInflateReader;
-  nsCOMPtr<nsIStringInputStream>  mInflateStream;
 
   // These are for the send buffers
   const static int32_t kCopyBreak = 1000;
 
   OutboundMessage                *mCurrentOut;
   uint32_t                        mCurrentOutSent;
   nsDeque                         mOutgoingMessages;
   nsDeque                         mOutgoingPingMessages;
   nsDeque                         mOutgoingPongMessages;
   uint32_t                        mHdrOutToSend;
   uint8_t                        *mHdrOut;
   uint8_t                         mOutHeader[kCopyBreak + 16];
-  nsWSCompression                *mCompressor;
+  nsAutoPtr<PMCECompression>      mPMCECompressor;
   uint32_t                        mDynamicOutputSize;
   uint8_t                        *mDynamicOutput;
   bool                            mPrivateBrowsing;
 
   nsCOMPtr<nsIDashboardEventNotifier> mConnectionLogService;
   uint32_t mSerial;
   static uint32_t sSerialSeed;
 
--- a/testing/mochitest/moz.build
+++ b/testing/mochitest/moz.build
@@ -82,23 +82,25 @@ TEST_HARNESS_FILES.testing.mochitest.pyw
 TEST_HARNESS_FILES.testing.mochitest.pywebsocket.mod_pywebsocket += [
     'pywebsocket/mod_pywebsocket/__init__.py',
     'pywebsocket/mod_pywebsocket/_stream_base.py',
     'pywebsocket/mod_pywebsocket/_stream_hixie75.py',
     'pywebsocket/mod_pywebsocket/_stream_hybi.py',
     'pywebsocket/mod_pywebsocket/common.py',
     'pywebsocket/mod_pywebsocket/dispatch.py',
     'pywebsocket/mod_pywebsocket/extensions.py',
+    'pywebsocket/mod_pywebsocket/fast_masking.i',
     'pywebsocket/mod_pywebsocket/headerparserhandler.py',
     'pywebsocket/mod_pywebsocket/http_header_util.py',
     'pywebsocket/mod_pywebsocket/memorizingfile.py',
     'pywebsocket/mod_pywebsocket/msgutil.py',
+    'pywebsocket/mod_pywebsocket/mux.py',
     'pywebsocket/mod_pywebsocket/stream.py',
     'pywebsocket/mod_pywebsocket/util.py',
+    'pywebsocket/mod_pywebsocket/xhr_benchmark_handler.py',
 ]
 
 TEST_HARNESS_FILES.testing.mochitest.pywebsocket.mod_pywebsocket.handshake += [
     'pywebsocket/mod_pywebsocket/handshake/__init__.py',
     'pywebsocket/mod_pywebsocket/handshake/_base.py',
-    'pywebsocket/mod_pywebsocket/handshake/draft75.py',
     'pywebsocket/mod_pywebsocket/handshake/hybi.py',
     'pywebsocket/mod_pywebsocket/handshake/hybi00.py',
 ]
--- a/testing/mochitest/pywebsocket/COPYING
+++ b/testing/mochitest/pywebsocket/COPYING
@@ -1,9 +1,9 @@
-Copyright 2009, Google Inc.
+Copyright 2012, 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.
--- a/testing/mochitest/pywebsocket/README
+++ b/testing/mochitest/pywebsocket/README
@@ -1,9 +1,17 @@
-Install this package by:
+INSTALL
+
+To install this package to the system, run this:
 $ python setup.py build
 $ sudo python setup.py install
 
-If you're going to use this package as a normal user, run this instead:
+To install this package as a normal user, run this instead:
+$ python setup.py build
 $ python setup.py install --user
 
-Then read document by:
+LAUNCH
+
+To use pywebsocket as Apache module, run this to read the document:
 $ pydoc mod_pywebsocket
+
+To use pywebsocket as standalone server, run this to read the document:
+$ pydoc mod_pywebsocket.standalone
--- a/testing/mochitest/pywebsocket/README-MOZILLA
+++ b/testing/mochitest/pywebsocket/README-MOZILLA
@@ -1,15 +1,15 @@
 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:  631  (supports RFC 6455)
+  svnversion:  860  (supports RFC 6455, permessage compression extension)
 
 --------------------------------------------------------------------------------
 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
 
@@ -24,18 +24,18 @@ STEPS TO UPDATE MOZILLA TO NEWER PYWEBSO
 
 - Get rid of examples/test directory and some cruft:
 
     rm -rf example test setup.py MANIFEST.in
 
 - 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
+- hg add/rm appropriate files, and add/remove them from
+  testing/mochitest/moz.build
 
 - We need to apply the patch to hybi.py that makes HSTS work: (attached at end
   of this README)
 
 - Test and make sure the code works:
 
     make mochitest-plain TEST_PATH=dom/base/test/test_websocket.html
 
@@ -47,48 +47,50 @@ STEPS TO UPDATE MOZILLA TO NEWER PYWEBSO
 
 --------------------------------------------------------------------------------
 PATCH TO hybi.py for HSTS support:
 
 
 diff --git a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi.py b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi.py
 --- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi.py
 +++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi.py
-@@ -227,16 +227,19 @@ class Handshaker(object):
- 
-     def _check_version(self):
-         unused_value = validate_mandatory_header(
-             self._request, common.SEC_WEBSOCKET_VERSION_HEADER,
-             str(common.VERSION_HYBI_LATEST), fail_status=426)
+@@ -299,16 +299,19 @@ class Handshaker(object):
+                 status=common.HTTP_STATUS_BAD_REQUEST)
+         raise VersionException(
+             'Unsupported version %r for header %s' %
+             (version, common.SEC_WEBSOCKET_VERSION_HEADER),
+             supported_versions=', '.join(map(str, _SUPPORTED_VERSIONS)))
  
      def _set_protocol(self):
          self._request.ws_protocol = None
 +        # MOZILLA
 +        self._request.sts = None
 +        # /MOZILLA
  
          protocol_header = self._request.headers_in.get(
              common.SEC_WEBSOCKET_PROTOCOL_HEADER)
  
-         if not protocol_header:
+         if protocol_header is None:
              self._request.ws_requested_protocols = None
              return
  
-@@ -311,16 +314,21 @@ class Handshaker(object):
+@@ -396,16 +399,21 @@ class Handshaker(object):
              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)))
+                 common.format_extensions(self._request.ws_extensions)))
 +        # MOZILLA: Add HSTS header if requested to
 +        if self._request.sts is not None:
 +            response.append(format_header("Strict-Transport-Security",
 +                                          self._request.sts))
 +        # /MOZILLA
+ 
+         # Headers not specific for WebSocket
+         for name, value in self._request.extra_headers:
+             response.append(format_header(name, value))
+ 
          response.append('\r\n')
  
-         raw_response = ''.join(response)
-         self._logger.debug('Opening handshake response: %r', raw_response)
-         self._request.connection.write(raw_response)
-
+         return ''.join(response)
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/__init__.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/__init__.py
@@ -29,17 +29,18 @@
 
 
 """WebSocket extension for Apache HTTP Server.
 
 mod_pywebsocket is a WebSocket extension for Apache HTTP Server
 intended for testing or experimental purposes. mod_python is required.
 
 
-Installation:
+Installation
+============
 
 0. Prepare an Apache HTTP Server for which mod_python is enabled.
 
 1. Specify the following Apache HTTP Server directives to suit your
    configuration.
 
    If mod_pywebsocket is not in the Python path, specify the following.
    <websock_lib> is the directory where mod_pywebsocket is installed.
@@ -55,21 +56,16 @@ 1. Specify the following Apache HTTP Ser
    To limit the search for WebSocket handlers to a directory <scan_dir>
    under <websock_handlers>, configure as follows:
 
        PythonOption mod_pywebsocket.handler_scan <scan_dir>
 
    <scan_dir> is useful in saving scan time when <websock_handlers>
    contains many non-WebSocket handler files.
 
-   If you want to support old handshake based on
-   draft-hixie-thewebsocketprotocol-75:
-
-       PythonOption mod_pywebsocket.allow_draft75 On
-
    If you want to allow handlers whose canonical path is not under the root
    directory (i.e. symbolic link is in root directory but its target is not),
    configure as follows:
 
        PythonOption mod_pywebsocket.allow_handlers_outside_root_dir On
 
    Example snippet of httpd.conf:
    (mod_pywebsocket is in /websock_lib, WebSocket handlers are in
@@ -84,17 +80,18 @@ 1. Specify the following Apache HTTP Ser
 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.
 
 3. Verify installation. You can use example/console.html to poke the server.
 
 
-Writing WebSocket handlers:
+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.
@@ -113,38 +110,46 @@ headers are successfully parsed and WebS
 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):
 - ws_resource
 - ws_origin
 - ws_version
-- ws_location (Hixie 75 and HyBi 00 only)
-- ws_extensions (Hybi 06 and later)
+- ws_location (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.
+The last two are a bit tricky. See the next subsection.
+
+
+Subprotocol Negotiation
+-----------------------
 
 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.
 
-For Hixie 75 and HyBi 00, when web_socket_do_extra_handshake is called,
+For 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
+Sec-WebSocket-Protocol 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
+Then, Sec-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.
 
+
+Data Transfer
+-------------
+
 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()
@@ -154,31 +159,66 @@ of the incoming frame will be stored int
 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)
 
+
+Closing Connection
+------------------
+
 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
+close_connection will wait
 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
 
+
+Threading
+---------
+
 A WebSocket handler must be thread-safe if the server (Apache or
 standalone.py) is configured to use threads.
+
+
+Configuring WebSocket Extension Processors
+------------------------------------------
+
+See extensions.py for supported WebSocket extensions. Note that they are
+unstable and their APIs are subject to change substantially.
+
+A request object has these extension processing related attributes.
+
+- ws_requested_extensions:
+
+  A list of common.ExtensionParameter instances representing extension
+  parameters received from the client in the client's opening handshake.
+  You shouldn't modify it manually.
+
+- ws_extensions:
+
+  A list of common.ExtensionParameter instances representing extension
+  parameters to send back to the client in the server's opening handshake.
+  You shouldn't touch it directly. Instead, call methods on extension
+  processors.
+
+- ws_extension_processors:
+
+  A list of loaded extension processors. Find the processor for the
+  extension you want to configure from it, and call its methods.
 """
 
 
 # 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
@@ -34,16 +34,18 @@
 
 # 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.
 
 
+import socket
+
 from mod_pywebsocket import util
 
 
 # Exceptions
 
 
 class ConnectionTerminatedException(Exception):
     """This exception will be raised when a connection is terminated
@@ -104,62 +106,76 @@ class StreamBase(object):
     def _read(self, length):
         """Reads length bytes from connection. In case we catch any exception,
         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:
+        try:
+            read_bytes = self._request.connection.read(length)
+            if not read_bytes:
+                raise ConnectionTerminatedException(
+                    'Receiving %d byte failed. Peer (%r) closed connection' %
+                    (length, (self._request.connection.remote_addr,)))
+            return read_bytes
+        except socket.error, e:
+            # Catch a socket.error. Because it's not a child class of the
+            # IOError prior to Python 2.6, we cannot omit this except clause.
+            # Use %s rather than %r for the exception to use human friendly
+            # format.
             raise ConnectionTerminatedException(
-                'Receiving %d byte failed. Peer (%r) closed connection' %
-                (length, (self._request.connection.remote_addr,)))
-        return bytes
+                'Receiving %d byte failed. socket.error (%s) occurred' %
+                (length, e))
+        except IOError, e:
+            # Also catch an IOError because mod_python throws it.
+            raise ConnectionTerminatedException(
+                'Receiving %d byte failed. IOError (%s) occurred' %
+                (length, e))
 
-    def _write(self, bytes):
+    def _write(self, bytes_to_write):
         """Writes given bytes to connection. In case we catch any exception,
         prepends remote address to the exception message and raise again.
         """
 
         try:
-            self._request.connection.write(bytes)
+            self._request.connection.write(bytes_to_write)
         except Exception, e:
             util.prepend_message_to_exception(
                     'Failed to send message to %r: ' %
                             (self._request.connection.remote_addr,),
                     e)
             raise
 
     def receive_bytes(self, length):
         """Receives multiple bytes. Retries read when we couldn't receive the
         specified amount.
 
         Raises:
             ConnectionTerminatedException: when read returns empty string.
         """
 
-        bytes = []
+        read_bytes = []
         while length > 0:
-            new_bytes = self._read(length)
-            bytes.append(new_bytes)
-            length -= len(new_bytes)
-        return ''.join(bytes)
+            new_read_bytes = self._read(length)
+            read_bytes.append(new_read_bytes)
+            length -= len(new_read_bytes)
+        return ''.join(read_bytes)
 
     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.
         """
 
-        bytes = []
+        read_bytes = []
         while True:
             ch = self._read(1)
             if ch == delim_char:
                 break
-            bytes.append(ch)
-        return ''.join(bytes)
+            read_bytes.append(ch)
+        return ''.join(read_bytes)
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hixie75.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hixie75.py
@@ -27,17 +27,18 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 """This file provides a class for parsing/building frames of the WebSocket
 protocol version HyBi 00 and Hixie 75.
 
 Specification:
-http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
+- HyBi 00 http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
+- Hixie 75 http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
 """
 
 
 from mod_pywebsocket import common
 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
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hybi.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/_stream_hybi.py
@@ -32,18 +32,20 @@
 of the WebSocket protocol (RFC 6455).
 
 Specification:
 http://tools.ietf.org/html/rfc6455
 """
 
 
 from collections import deque
+import logging
 import os
 import struct
+import time
 
 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
@@ -156,32 +158,163 @@ 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)
 
 
+def parse_frame(receive_bytes, logger=None,
+                ws_version=common.VERSION_HYBI_LATEST,
+                unmask_receive=True):
+    """Parses a frame. Returns a tuple containing each header field and
+    payload.
+
+    Args:
+        receive_bytes: a function that reads frame data from a stream or
+            something similar. The function takes length of the bytes to be
+            read. The function must raise ConnectionTerminatedException if
+            there is not enough data to be read.
+        logger: a logging object.
+        ws_version: the version of WebSocket protocol.
+        unmask_receive: unmask received frames. When received unmasked
+            frame, raises InvalidFrameException.
+
+    Raises:
+        ConnectionTerminatedException: when receive_bytes raises it.
+        InvalidFrameException: when the frame contains invalid data.
+    """
+
+    if not logger:
+        logger = logging.getLogger()
+
+    logger.log(common.LOGLEVEL_FINE, 'Receive the first 2 octets of a frame')
+
+    received = 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
+
+    logger.log(common.LOGLEVEL_FINE,
+               'FIN=%s, RSV1=%s, RSV2=%s, RSV3=%s, opcode=%s, '
+               'Mask=%s, Payload_length=%s',
+               fin, rsv1, rsv2, rsv3, opcode, mask, payload_length)
+
+    if (mask == 1) != unmask_receive:
+        raise InvalidFrameException(
+            'Mask bit on the received frame did\'nt match masking '
+            'configuration for received frames')
+
+    # The HyBi and later specs disallow putting a value in 0x0-0xFFFF
+    # into the 8-octet extended payload length field (or 0x0-0xFD in
+    # 2-octet field).
+    valid_length_encoding = True
+    length_encoding_bytes = 1
+    if payload_length == 127:
+        logger.log(common.LOGLEVEL_FINE,
+                   'Receive 8-octet extended payload length')
+
+        extended_payload_length = receive_bytes(8)
+        payload_length = struct.unpack(
+            '!Q', extended_payload_length)[0]
+        if payload_length > 0x7FFFFFFFFFFFFFFF:
+            raise InvalidFrameException(
+                'Extended payload length >= 2^63')
+        if ws_version >= 13 and payload_length < 0x10000:
+            valid_length_encoding = False
+            length_encoding_bytes = 8
+
+        logger.log(common.LOGLEVEL_FINE,
+                   'Decoded_payload_length=%s', payload_length)
+    elif payload_length == 126:
+        logger.log(common.LOGLEVEL_FINE,
+                   'Receive 2-octet extended payload length')
+
+        extended_payload_length = receive_bytes(2)
+        payload_length = struct.unpack(
+            '!H', extended_payload_length)[0]
+        if ws_version >= 13 and payload_length < 126:
+            valid_length_encoding = False
+            length_encoding_bytes = 2
+
+        logger.log(common.LOGLEVEL_FINE,
+                   'Decoded_payload_length=%s', payload_length)
+
+    if not valid_length_encoding:
+        logger.warning(
+            'Payload length is not encoded using the minimal number of '
+            'bytes (%d is encoded using %d bytes)',
+            payload_length,
+            length_encoding_bytes)
+
+    if mask == 1:
+        logger.log(common.LOGLEVEL_FINE, 'Receive mask')
+
+        masking_nonce = receive_bytes(4)
+        masker = util.RepeatedXorMasker(masking_nonce)
+
+        logger.log(common.LOGLEVEL_FINE, 'Mask=%r', masking_nonce)
+    else:
+        masker = _NOOP_MASKER
+
+    logger.log(common.LOGLEVEL_FINE, 'Receive payload data')
+    if logger.isEnabledFor(common.LOGLEVEL_FINE):
+        receive_start = time.time()
+
+    raw_payload_bytes = receive_bytes(payload_length)
+
+    if logger.isEnabledFor(common.LOGLEVEL_FINE):
+        logger.log(
+            common.LOGLEVEL_FINE,
+            'Done receiving payload data at %s MB/s',
+            payload_length / (time.time() - receive_start) / 1000 / 1000)
+    logger.log(common.LOGLEVEL_FINE, 'Unmask payload data')
+
+    if logger.isEnabledFor(common.LOGLEVEL_FINE):
+        unmask_start = time.time()
+
+    unmasked_bytes = masker.mask(raw_payload_bytes)
+
+    if logger.isEnabledFor(common.LOGLEVEL_FINE):
+        logger.log(
+            common.LOGLEVEL_FINE,
+            'Done unmasking payload data at %s MB/s',
+            payload_length / (time.time() - unmask_start) / 1000 / 1000)
+
+    return opcode, unmasked_bytes, fin, rsv1, rsv2, rsv3
+
+
 class FragmentedFrameBuilder(object):
     """A stateful class to send a message as fragments."""
 
-    def __init__(self, mask, frame_filters=[]):
+    def __init__(self, mask, frame_filters=[], encode_utf8=True):
         """Constructs an instance."""
 
         self._mask = mask
         self._frame_filters = frame_filters
+        # This is for skipping UTF-8 encoding when building text type frames
+        # from compressed data.
+        self._encode_utf8 = encode_utf8
 
         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):
+    def build(self, payload_data, 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')
@@ -192,56 +325,84 @@ class FragmentedFrameBuilder(object):
 
         if end:
             self._started = False
             fin = 1
         else:
             self._started = True
             fin = 0
 
-        if binary:
+        if binary or not self._encode_utf8:
             return create_binary_frame(
-                message, opcode, fin, self._mask, self._frame_filters)
+                payload_data, opcode, fin, self._mask, self._frame_filters)
         else:
             return create_text_frame(
-                message, opcode, fin, self._mask, self._frame_filters)
+                payload_data, 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)
+    for frame_filter in frame_filters:
+        frame_filter.filter(frame)
+
+    if len(frame.payload) > 125:
+        raise BadOperationException(
+            'Payload data size of control frames must be 125 bytes or less')
+
+    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_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)
 
 
+def create_closing_handshake_body(code, reason):
+    body = ''
+    if code is not None:
+        if (code > common.STATUS_USER_PRIVATE_MAX or
+            code < common.STATUS_NORMAL_CLOSURE):
+            raise BadOperationException('Status code is out of range')
+        if (code == common.STATUS_NO_STATUS_RECEIVED or
+            code == common.STATUS_ABNORMAL_CLOSURE or
+            code == common.STATUS_TLS_HANDSHAKE):
+            raise BadOperationException('Status code is reserved pseudo '
+                'code')
+        encoded_reason = reason.encode('utf-8')
+        body = struct.pack('!H', code) + encoded_reason
+    return body
+
+
 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 = []
 
+        # Filters applied to messages. Control frames are not affected by them.
+        self.outgoing_message_filters = []
+        self.incoming_message_filters = []
+
+        self.encode_text_message_to_utf8 = True
         self.mask_send = False
         self.unmask_receive = True
 
 
 class Stream(StreamBase):
     """A class for parsing/building frames of the WebSocket protocol
     (RFC 6455).
     """
@@ -254,106 +415,79 @@ class Stream(StreamBase):
         """
 
         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._options.mask_send, self._options.outgoing_frame_filters,
+            self._options.encode_text_message_to_utf8)
 
         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')
+        def _receive_bytes(length):
+            return self.receive_bytes(length)
 
-        # The Hybi-13 and later specs disallow putting a value in 0x0-0xFFFF
-        # into the 8-octet extended payload length field (or 0x0-0xFD in
-        # 2-octet field).
-        valid_length_encoding = True
-        length_encoding_bytes = 1
-        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')
-            if self._request.ws_version >= 13 and payload_length < 0x10000:
-                valid_length_encoding = False
-                length_encoding_bytes = 8
-        elif payload_length == 126:
-            extended_payload_length = self.receive_bytes(2)
-            payload_length = struct.unpack(
-                '!H', extended_payload_length)[0]
-            if self._request.ws_version >= 13 and payload_length < 126:
-                valid_length_encoding = False
-                length_encoding_bytes = 2
-
-        if not valid_length_encoding:
-            self._logger.warning(
-                'Payload length is not encoded using the minimal number of '
-                'bytes (%d is encoded using %d bytes)',
-                payload_length,
-                length_encoding_bytes)
-
-        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
+        return parse_frame(receive_bytes=_receive_bytes,
+                           logger=self._logger,
+                           ws_version=self._request.ws_version,
+                           unmask_receive=self._options.unmask_receive)
 
     def _receive_frame_as_frame_object(self):
-        opcode, bytes, fin, rsv1, rsv2, rsv3 = self._receive_frame()
+        opcode, unmasked_bytes, fin, rsv1, rsv2, rsv3 = self._receive_frame()
 
         return Frame(fin=fin, rsv1=rsv1, rsv2=rsv2, rsv3=rsv3,
-                     opcode=opcode, payload=bytes)
+                     opcode=opcode, payload=unmasked_bytes)
+
+    def receive_filtered_frame(self):
+        """Receives a frame and applies frame filters and message filters.
+        The frame to be received must satisfy following conditions:
+        - The frame is not fragmented.
+        - The opcode of the frame is TEXT or BINARY.
+
+        DO NOT USE this method except for testing purpose.
+        """
+
+        frame = self._receive_frame_as_frame_object()
+        if not frame.fin:
+            raise InvalidFrameException(
+                'Segmented frames must not be received via '
+                'receive_filtered_frame()')
+        if (frame.opcode != common.OPCODE_TEXT and
+            frame.opcode != common.OPCODE_BINARY):
+            raise InvalidFrameException(
+                'Control frames must not be received via '
+                'receive_filtered_frame()')
+
+        for frame_filter in self._options.incoming_frame_filters:
+            frame_filter.filter(frame)
+        for message_filter in self._options.incoming_message_filters:
+            frame.payload = message_filter.filter(frame.payload)
+        return frame
 
     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.
 
@@ -366,21 +500,229 @@ class Stream(StreamBase):
         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')
 
+        for message_filter in self._options.outgoing_message_filters:
+            message = message_filter.filter(message, end, binary)
+
         try:
-            self._write(self._writer.build(message, end, binary))
+            # Set this to any positive integer to limit maximum size of data in
+            # payload data of each frame.
+            MAX_PAYLOAD_DATA_SIZE = -1
+
+            if MAX_PAYLOAD_DATA_SIZE <= 0:
+                self._write(self._writer.build(message, end, binary))
+                return
+
+            bytes_written = 0
+            while True:
+                end_for_this_frame = end
+                bytes_to_write = len(message) - bytes_written
+                if (MAX_PAYLOAD_DATA_SIZE > 0 and
+                    bytes_to_write > MAX_PAYLOAD_DATA_SIZE):
+                    end_for_this_frame = False
+                    bytes_to_write = MAX_PAYLOAD_DATA_SIZE
+
+                frame = self._writer.build(
+                    message[bytes_written:bytes_written + bytes_to_write],
+                    end_for_this_frame,
+                    binary)
+                self._write(frame)
+
+                bytes_written += bytes_to_write
+
+                # This if must be placed here (the end of while block) so that
+                # at least one frame is sent.
+                if len(message) <= bytes_written:
+                    break
         except ValueError, e:
             raise BadOperationException(e)
 
+    def _get_message_from_frame(self, frame):
+        """Gets a message from frame. If the message is composed of fragmented
+        frames and the frame is not the last fragmented frame, this method
+        returns None. The whole message will be returned when the last
+        fragmented frame is passed to this method.
+
+        Raises:
+            InvalidFrameException: when the frame doesn't match defragmentation
+                context, or the frame contains invalid data.
+        """
+
+        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 = []
+                return message
+            else:
+                # Intermediate frame
+                self._received_fragments.append(frame.payload)
+                return None
+        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
+
+                self._original_opcode = frame.opcode
+                return 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)
+                return None
+
+    def _process_close_message(self, message):
+        """Processes close message.
+
+        Args:
+            message: close message.
+
+        Raises:
+            InvalidFrameException: when the message is invalid.
+        """
+
+        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) == 0:
+            self._logger.debug('Received close frame (empty body)')
+            self._request.ws_close_code = (
+                common.STATUS_NO_STATUS_RECEIVED)
+        elif 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)
+
+        # As we've received a close frame, no more data is coming over the
+        # socket. We can now safely close the socket without worrying about
+        # RST sending.
+
+        if self._request.server_terminated:
+            self._logger.debug(
+                'Received ack for server-initiated closing handshake')
+            return
+
+        self._logger.debug(
+            'Received client-initiated closing handshake')
+
+        code = common.STATUS_NORMAL_CLOSURE
+        reason = ''
+        if hasattr(self._request, '_dispatcher'):
+            dispatcher = self._request._dispatcher
+            code, reason = dispatcher.passive_closing_handshake(
+                self._request)
+            if code is None and reason is not None and len(reason) > 0:
+                self._logger.warning(
+                    'Handler specified reason despite code being None')
+                reason = ''
+            if reason is None:
+                reason = ''
+        self._send_closing_handshake(code, reason)
+        self._logger.debug(
+            'Acknowledged closing handshake initiated by the peer '
+            '(code=%r, reason=%r)', code, reason)
+
+    def _process_ping_message(self, message):
+        """Processes ping message.
+
+        Args:
+            message: ping message.
+        """
+
+        try:
+            handler = self._request.on_ping_handler
+            if handler:
+                handler(self._request, message)
+                return
+        except AttributeError, e:
+            pass
+        self._send_pong(message)
+
+    def _process_pong_message(self, message):
+        """Processes pong message.
+
+        Args:
+            message: pong message.
+        """
+
+        # 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)
+        except AttributeError, e:
+            pass
+
     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
@@ -403,243 +745,114 @@ class Stream(StreamBase):
                 '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()
 
+            # Check the constraint on the payload size for control frames
+            # before extension processes the frame.
+            # See also http://tools.ietf.org/html/rfc6455#section-5.5
+            if (common.is_control_opcode(frame.opcode) and
+                len(frame.payload) > 125):
+                raise InvalidFrameException(
+                    'Payload data size of control frames must be 125 bytes or '
+                    'less')
+
             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')
+            message = self._get_message_from_frame(frame)
+            if message is None:
+                continue
 
-                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
+            for message_filter in self._options.incoming_message_filters:
+                message = message_filter.filter(message)
 
             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) == 0:
-                    self._logger.debug('Received close frame (empty body)')
-                    self._request.ws_close_code = (
-                        common.STATUS_NO_STATUS_RECEIVED)
-                elif 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_CLOSURE
-                reason = ''
-                if hasattr(self._request, '_dispatcher'):
-                    dispatcher = self._request._dispatcher
-                    code, reason = dispatcher.passive_closing_handshake(
-                        self._request)
-                    if code is None and reason is not None and len(reason) > 0:
-                        self._logger.warning(
-                            'Handler specified reason despite code being None')
-                        reason = ''
-                    if reason is None:
-                        reason = ''
-                self._send_closing_handshake(code, reason)
-                self._logger.debug(
-                    'Sent ack for client-initiated closing handshake '
-                    '(code=%r, reason=%r)', code, reason)
+                self._process_close_message(message)
                 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)
+                self._process_ping_message(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
+                self._process_pong_message(message)
             else:
                 raise UnsupportedFrameException(
                     'Opcode %d is not supported' % self._original_opcode)
 
     def _send_closing_handshake(self, code, reason):
-        body = ''
-        if code is not None:
-            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')
-            body = struct.pack('!H', code) + encoded_reason
-
+        body = create_closing_handshake_body(code, reason)
         frame = create_close_frame(
-            body,
-            self._options.mask_send,
-            self._options.outgoing_frame_filters)
+            body, mask=self._options.mask_send,
+            frame_filters=self._options.outgoing_frame_filters)
 
         self._request.server_terminated = True
 
         self._write(frame)
 
-    def close_connection(self, code=common.STATUS_NORMAL_CLOSURE, reason=''):
-        """Closes a WebSocket connection.
+    def close_connection(self, code=common.STATUS_NORMAL_CLOSURE, reason='',
+                         wait_response=True):
+        """Closes a WebSocket connection. Note that this method blocks until
+        it receives acknowledgement to the closing handshake.
 
         Args:
             code: Status code for close frame. If code is None, a close
                 frame with empty body will be sent.
             reason: string representing close reason.
+            wait_response: True when caller want to wait the response.
         Raises:
             BadOperationException: when reason is specified with code None
             or reason is not an instance of both str and unicode.
         """
 
         if self._request.server_terminated:
             self._logger.debug(
                 'Requested close_connection but server is already terminated')
             return
 
+        # When we receive a close frame, we call _process_close_message().
+        # _process_close_message() immediately acknowledges to the
+        # server-initiated closing handshake and sets server_terminated to
+        # True. So, here we can assume that we haven't received any close
+        # frame. We're initiating a closing handshake.
+
         if code is None:
             if reason is not None and len(reason) > 0:
                 raise BadOperationException(
                     'close reason must not be specified if code is None')
             reason = ''
         else:
             if not isinstance(reason, str) and not isinstance(reason, unicode):
                 raise BadOperationException(
                     'close reason must be an instance of str or unicode')
 
         self._send_closing_handshake(code, reason)
         self._logger.debug(
-            'Sent server-initiated closing handshake (code=%r, reason=%r)',
+            'Initiated closing handshake (code=%r, reason=%r)',
             code, reason)
 
         if (code == common.STATUS_GOING_AWAY or
-            code == common.STATUS_PROTOCOL_ERROR):
+            code == common.STATUS_PROTOCOL_ERROR) or not wait_response:
             # 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.
@@ -649,53 +862,33 @@ class Stream(StreamBase):
         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.
+    def get_last_received_opcode(self):
+        """Returns the opcode of the WebSocket message which the last received
+        frame belongs to. The return value is valid iff immediately after
+        receive_message call.
         """
 
-        # 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()
+        return self._original_opcode
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/common.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/common.py
@@ -23,16 +23,26 @@
 # 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.
 
 
+"""This file must not depend on any module specific to the WebSocket protocol.
+"""
+
+
+from mod_pywebsocket import http_header_util
+
+
+# Additional log level definitions.
+LOGLEVEL_FINE = 9
+
 # Constants indicating WebSocket protocol version.
 VERSION_HIXIE75 = -1
 VERSION_HYBI00 = 0
 VERSION_HYBI01 = 1
 VERSION_HYBI02 = 2
 VERSION_HYBI03 = 2
 VERSION_HYBI04 = 4
 VERSION_HYBI05 = 5
@@ -86,19 +96,20 @@ SEC_WEBSOCKET_VERSION_HEADER = 'Sec-WebS
 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'
+PERMESSAGE_DEFLATE_EXTENSION = 'permessage-deflate'
 X_WEBKIT_DEFLATE_FRAME_EXTENSION = 'x-webkit-deflate-frame'
+MUX_EXTENSION = 'mux_DO_NOT_USE'
 
 # Status codes
 # Code STATUS_NO_STATUS_RECEIVED, STATUS_ABNORMAL_CLOSURE, and
 # STATUS_TLS_HANDSHAKE are pseudo codes to indicate specific error cases.
 # Could not be used for codes in actual closing frames.
 # Application level errors must use codes in the range
 # STATUS_USER_REGISTERED_BASE to STATUS_USER_PRIVATE_MAX. The codes in the
 # range STATUS_USER_REGISTERED_BASE to STATUS_USER_REGISTERED_MAX are managed
@@ -109,17 +120,17 @@ STATUS_GOING_AWAY = 1001
 STATUS_PROTOCOL_ERROR = 1002
 STATUS_UNSUPPORTED_DATA = 1003
 STATUS_NO_STATUS_RECEIVED = 1005
 STATUS_ABNORMAL_CLOSURE = 1006
 STATUS_INVALID_FRAME_PAYLOAD_DATA = 1007
 STATUS_POLICY_VIOLATION = 1008
 STATUS_MESSAGE_TOO_BIG = 1009
 STATUS_MANDATORY_EXTENSION = 1010
-STATUS_INTERNAL_SERVER_ERROR = 1011
+STATUS_INTERNAL_ENDPOINT_ERROR = 1011
 STATUS_TLS_HANDSHAKE = 1015
 STATUS_USER_REGISTERED_BASE = 3000
 STATUS_USER_REGISTERED_MAX = 3999
 STATUS_USER_PRIVATE_BASE = 4000
 STATUS_USER_PRIVATE_MAX = 4999
 # Following definitions are aliases to keep compatibility. Applications must
 # not use these obsoleted definitions anymore.
 STATUS_NORMAL = STATUS_NORMAL_CLOSURE
@@ -171,9 +182,120 @@ class ExtensionParameter(object):
         return False
 
     def get_parameter_value(self, name):
         for param_name, param_value in self._parameters:
             if param_name == name:
                 return param_value
 
 
+class ExtensionParsingException(Exception):
+    def __init__(self, name):
+        super(ExtensionParsingException, self).__init__(name)
+
+
+def _parse_extension_param(state, definition):
+    param_name = http_header_util.consume_token(state)
+
+    if param_name is None:
+        raise ExtensionParsingException('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)
+
+    # TODO(tyoshino): Add code to validate that parsed param_value is token
+    param_value = http_header_util.consume_token_or_quoted_string(state)
+    if param_value is None:
+        raise ExtensionParsingException(
+            '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 = 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 ExtensionParsingException, e:
+            raise ExtensionParsingException(
+                '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
+    ExtensionParameter objects.
+
+    Leading LWSes must be trimmed.
+    """
+
+    state = http_header_util.ParsingState(data)
+
+    extension_list = []
+    while True:
+        extension = _parse_extension(state)
+        if extension is not None:
+            extension_list.append(extension)
+
+        http_header_util.consume_lwses(state)
+
+        if http_header_util.peek(state) is None:
+            break
+
+        if not http_header_util.consume_string(state, ','):
+            raise ExtensionParsingException(
+                '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 ExtensionParsingException(
+            'No valid extension entry found')
+
+    return extension_list
+
+
+def format_extension(extension):
+    """Formats an ExtensionParameter object."""
+
+    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))
+    return '; '.join(formatted_params)
+
+
+def format_extensions(extension_list):
+    """Formats a list of ExtensionParameter objects."""
+
+    formatted_extension_list = []
+    for extension in extension_list:
+        formatted_extension_list.append(format_extension(extension))
+    return ', '.join(formatted_extension_list)
+
+
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/dispatch.py
@@ -34,16 +34,17 @@
 
 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 mux
 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'
@@ -249,16 +250,19 @@ class Dispatcher(object):
 
         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:
+            # Re-raise to tell the caller of this function to finish this
+            # connection without sending any error.
+            self._logger.debug('%s', util.get_stack_trace())
             raise
         except Exception, e:
             util.prepend_message_to_exception(
                     '%s raised exception for %s: ' % (
                             _DO_EXTRA_HANDSHAKE_HANDLER_NAME,
                             request.ws_resource),
                     e)
             raise handshake.HandshakeException(e, common.HTTP_STATUS_FORBIDDEN)
@@ -272,47 +276,55 @@ class Dispatcher(object):
         Args:
             request: mod_python request.
 
         Raises:
             DispatchException: when handler was not found
             AbortedByUserException: when user handler abort connection
         """
 
-        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 mux.use_mux(request):
+                mux.start(request, self)
+            else:
+                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
+                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)
+            self._logger.debug('%s', util.get_stack_trace())
             raise
         except msgutil.BadOperationException, e:
             self._logger.debug('%s', e)
-            request.ws_stream.close_connection(common.STATUS_ABNORMAL_CLOSURE)
+            request.ws_stream.close_connection(
+                common.STATUS_INTERNAL_ENDPOINT_ERROR)
         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_DATA)
         except stream.InvalidUTF8Exception, e:
             self._logger.debug('%s', e)
             request.ws_stream.close_connection(
                 common.STATUS_INVALID_FRAME_PAYLOAD_DATA)
         except msgutil.ConnectionTerminatedException, e:
             self._logger.debug('%s', e)
         except Exception, e:
+            # Any other exceptions are forwarded to the caller of this
+            # function.
             util.prepend_message_to_exception(
                 '%s raised exception for %s: ' % (
                     _TRANSFER_DATA_HANDLER_NAME, request.ws_resource),
                 e)
             raise
 
     def passive_closing_handshake(self, request):
         """Prepare code and reason for responding client initiated closing
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/extensions.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/extensions.py
@@ -1,9 +1,9 @@
-# Copyright 2011, Google Inc.
+# Copyright 2012, 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.
@@ -25,105 +25,185 @@
 # 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
+from mod_pywebsocket.http_header_util import quote_if_necessary
 
 
+# The list of available server side extension processor classes.
 _available_processors = {}
+_compression_extension_names = []
 
 
 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
+        self._active = True
+
+    def request(self):
+        return self._request
+
+    def name(self):
+        return None
+
+    def check_consistency_with_other_processors(self, processors):
+        pass
+
+    def set_active(self, active):
+        self._active = active
+
+    def is_active(self):
+        return self._active
+
+    def _get_extension_response_internal(self):
+        return None
 
     def get_extension_response(self):
-        if len(self._request.get_parameter_names()) != 0:
+        if not self._active:
+            self._logger.debug('Extension %s is deactivated', self.name())
             return None
 
-        self._logger.debug(
-            'Enable %s extension', common.DEFLATE_STREAM_EXTENSION)
+        response = self._get_extension_response_internal()
+        if response is None:
+            self._active = False
+        return response
 
-        return common.ExtensionParameter(common.DEFLATE_STREAM_EXTENSION)
+    def _setup_stream_options_internal(self, stream_options):
+        pass
 
     def setup_stream_options(self, stream_options):
-        stream_options.deflate_stream = True
+        if self._active:
+            self._setup_stream_options_internal(stream_options)
+
+
+def _log_outgoing_compression_ratio(
+        logger, original_bytes, filtered_bytes, average_ratio):
+    # Print inf when ratio is not available.
+    ratio = float('inf')
+    if original_bytes != 0:
+        ratio = float(filtered_bytes) / original_bytes
+
+    logger.debug('Outgoing compression ratio: %f (average: %f)' %
+            (ratio, average_ratio))
+
+
+def _log_incoming_compression_ratio(
+        logger, received_bytes, filtered_bytes, average_ratio):
+    # Print inf when ratio is not available.
+    ratio = float('inf')
+    if filtered_bytes != 0:
+        ratio = float(received_bytes) / filtered_bytes
+
+    logger.debug('Incoming compression ratio: %f (average: %f)' %
+            (ratio, average_ratio))
 
 
-_available_processors[common.DEFLATE_STREAM_EXTENSION] = (
-    DeflateStreamExtensionProcessor)
+def _parse_window_bits(bits):
+    """Return parsed integer value iff the given string conforms to the
+    grammar of the window bits extension parameters.
+    """
+
+    if bits is None:
+        raise ValueError('Value is required')
+
+    # For non integer values such as "10.0", ValueError will be raised.
+    int_bits = int(bits)
+
+    # First condition is to drop leading zero case e.g. "08".
+    if bits != str(int_bits) or int_bits < 8 or int_bits > 15:
+        raise ValueError('Invalid value: %r' % bits)
+
+    return int_bits
+
+
+class _AverageRatioCalculator(object):
+    """Stores total bytes of original and result data, and calculates average
+    result / original ratio.
+    """
+
+    def __init__(self):
+        self._total_original_bytes = 0
+        self._total_result_bytes = 0
+
+    def add_original_bytes(self, value):
+        self._total_original_bytes += value
+
+    def add_result_bytes(self, value):
+        self._total_result_bytes += value
+
+    def get_average_ratio(self):
+        if self._total_original_bytes != 0:
+            return (float(self._total_result_bytes) /
+                    self._total_original_bytes)
+        else:
+            return float('inf')
 
 
 class DeflateFrameExtensionProcessor(ExtensionProcessorInterface):
-    """WebSocket Per-frame DEFLATE extension processor."""
+    """deflate-frame extension processor.
+
+    Specification:
+    http://tools.ietf.org/html/draft-tyoshino-hybi-websocket-perframe-deflate
+    """
 
     _WINDOW_BITS_PARAM = 'max_window_bits'
     _NO_CONTEXT_TAKEOVER_PARAM = 'no_context_takeover'
 
     def __init__(self, request):
+        ExtensionProcessorInterface.__init__(self, request)
         self._logger = util.get_class_logger(self)
 
-        self._request = request
-
         self._response_window_bits = None
         self._response_no_context_takeover = False
+        self._bfinal = False
 
-        # Counters for statistics.
+        # Calculates
+        #     (Total outgoing bytes supplied to this filter) /
+        #     (Total bytes sent to the network after applying this filter)
+        self._outgoing_average_ratio_calculator = _AverageRatioCalculator()
 
-        # Total number of outgoing bytes supplied to this filter.
-        self._total_outgoing_payload_bytes = 0
-        # Total number of bytes sent to the network after applying this filter.
-        self._total_filtered_outgoing_payload_bytes = 0
+        # Calculates
+        #     (Total bytes received from the network) /
+        #     (Total incoming bytes obtained after applying this filter)
+        self._incoming_average_ratio_calculator = _AverageRatioCalculator()
 
-        # Total number of bytes received from the network.
-        self._total_incoming_payload_bytes = 0
-        # Total number of incoming bytes obtained after applying this filter.
-        self._total_filtered_incoming_payload_bytes = 0
+    def name(self):
+        return common.DEFLATE_FRAME_EXTENSION
 
-    def get_extension_response(self):
+    def _get_extension_response_internal(self):
         # Any unknown parameter will be just ignored.
 
-        window_bits = self._request.get_parameter_value(
-            self._WINDOW_BITS_PARAM)
+        window_bits = None
+        if self._request.has_parameter(self._WINDOW_BITS_PARAM):
+            window_bits = self._request.get_parameter_value(
+                self._WINDOW_BITS_PARAM)
+            try:
+                window_bits = _parse_window_bits(window_bits)
+            except ValueError, e:
+                return None
+
         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(
+        self._rfc1979_deflater = util._RFC1979Deflater(
             window_bits, no_context_takeover)
 
-        self._inflater = util._RFC1979Inflater()
+        self._rfc1979_inflater = util._RFC1979Inflater()
 
         self._compress_outgoing = True
 
         response = common.ExtensionParameter(self._request.name())
 
         if self._response_window_bits is not None:
             response.add_parameter(
                 self._WINDOW_BITS_PARAM, str(self._response_window_bits))
@@ -138,17 +218,17 @@ class DeflateFrameExtensionProcessor(Ext
             (self._request.name(),
              window_bits,
              no_context_takeover,
              self._response_window_bits,
              self._response_no_context_takeover))
 
         return response
 
-    def setup_stream_options(self, stream_options):
+    def _setup_stream_options_internal(self, stream_options):
 
         class _OutgoingFilter(object):
 
             def __init__(self, parent):
                 self._parent = parent
 
             def filter(self, frame):
                 self._parent._outgoing_filter(frame)
@@ -167,99 +247,518 @@ class DeflateFrameExtensionProcessor(Ext
             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 set_bfinal(self, value):
+        self._bfinal = 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.
         """
 
         original_payload_size = len(frame.payload)
-        self._total_outgoing_payload_bytes += original_payload_size
+        self._outgoing_average_ratio_calculator.add_original_bytes(
+                original_payload_size)
 
         if (not self._compress_outgoing or
             common.is_control_opcode(frame.opcode)):
-            self._total_filtered_outgoing_payload_bytes += (
-                original_payload_size)
+            self._outgoing_average_ratio_calculator.add_result_bytes(
+                    original_payload_size)
             return
 
-        frame.payload = self._deflater.filter(frame.payload)
+        frame.payload = self._rfc1979_deflater.filter(
+            frame.payload, bfinal=self._bfinal)
         frame.rsv1 = 1
 
         filtered_payload_size = len(frame.payload)
-        self._total_filtered_outgoing_payload_bytes += filtered_payload_size
+        self._outgoing_average_ratio_calculator.add_result_bytes(
+                filtered_payload_size)
 
-        # Print inf when ratio is not available.
-        ratio = float('inf')
-        average_ratio = float('inf')
-        if original_payload_size != 0:
-            ratio = float(filtered_payload_size) / original_payload_size
-        if self._total_outgoing_payload_bytes != 0:
-            average_ratio = (
-                float(self._total_filtered_outgoing_payload_bytes) /
-                self._total_outgoing_payload_bytes)
-        self._logger.debug(
-            'Outgoing compress ratio: %f (average: %f)' %
-            (ratio, average_ratio))
+        _log_outgoing_compression_ratio(
+                self._logger,
+                original_payload_size,
+                filtered_payload_size,
+                self._outgoing_average_ratio_calculator.get_average_ratio())
 
     def _incoming_filter(self, frame):
         """Transform incoming frames. This method is called only by
         an _IncomingFilter instance.
         """
 
         received_payload_size = len(frame.payload)
-        self._total_incoming_payload_bytes += received_payload_size
+        self._incoming_average_ratio_calculator.add_result_bytes(
+                received_payload_size)
 
         if frame.rsv1 != 1 or common.is_control_opcode(frame.opcode):
-            self._total_filtered_incoming_payload_bytes += (
-                received_payload_size)
+            self._incoming_average_ratio_calculator.add_original_bytes(
+                    received_payload_size)
             return
 
-        frame.payload = self._inflater.filter(frame.payload)
+        frame.payload = self._rfc1979_inflater.filter(frame.payload)
         frame.rsv1 = 0
 
         filtered_payload_size = len(frame.payload)
-        self._total_filtered_incoming_payload_bytes += filtered_payload_size
+        self._incoming_average_ratio_calculator.add_original_bytes(
+                filtered_payload_size)
 
-        # Print inf when ratio is not available.
-        ratio = float('inf')
-        average_ratio = float('inf')
-        if received_payload_size != 0:
-            ratio = float(received_payload_size) / filtered_payload_size
-        if self._total_filtered_incoming_payload_bytes != 0:
-            average_ratio = (
-                float(self._total_incoming_payload_bytes) /
-                self._total_filtered_incoming_payload_bytes)
-        self._logger.debug(
-            'Incoming compress ratio: %f (average: %f)' %
-            (ratio, average_ratio))
+        _log_incoming_compression_ratio(
+                self._logger,
+                received_payload_size,
+                filtered_payload_size,
+                self._incoming_average_ratio_calculator.get_average_ratio())
 
 
 _available_processors[common.DEFLATE_FRAME_EXTENSION] = (
     DeflateFrameExtensionProcessor)
+_compression_extension_names.append(common.DEFLATE_FRAME_EXTENSION)
+
+_available_processors[common.X_WEBKIT_DEFLATE_FRAME_EXTENSION] = (
+    DeflateFrameExtensionProcessor)
+_compression_extension_names.append(common.X_WEBKIT_DEFLATE_FRAME_EXTENSION)
+
+
+class PerMessageDeflateExtensionProcessor(ExtensionProcessorInterface):
+    """permessage-deflate extension processor.
+
+    Specification:
+    http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-08
+    """
+
+    _SERVER_MAX_WINDOW_BITS_PARAM = 'server_max_window_bits'
+    _SERVER_NO_CONTEXT_TAKEOVER_PARAM = 'server_no_context_takeover'
+    _CLIENT_MAX_WINDOW_BITS_PARAM = 'client_max_window_bits'
+    _CLIENT_NO_CONTEXT_TAKEOVER_PARAM = 'client_no_context_takeover'
+
+    def __init__(self, request, draft08=True):
+        """Construct PerMessageDeflateExtensionProcessor
+
+        Args:
+            draft08: Follow the constraints on the parameters that were not
+                specified for permessage-compress but are specified for
+                permessage-deflate as on
+                draft-ietf-hybi-permessage-compression-08.
+        """
+
+        ExtensionProcessorInterface.__init__(self, request)
+        self._logger = util.get_class_logger(self)
+
+        self._preferred_client_max_window_bits = None
+        self._client_no_context_takeover = False
+
+        self._draft08 = draft08
+
+    def name(self):
+        # This method returns "deflate" (not "permessage-deflate") for
+        # compatibility.
+        return 'deflate'
+
+    def _get_extension_response_internal(self):
+        if self._draft08:
+            for name in self._request.get_parameter_names():
+                if name not in [self._SERVER_MAX_WINDOW_BITS_PARAM,
+                                self._SERVER_NO_CONTEXT_TAKEOVER_PARAM,
+                                self._CLIENT_MAX_WINDOW_BITS_PARAM]:
+                    self._logger.debug('Unknown parameter: %r', name)
+                    return None
+        else:
+            # Any unknown parameter will be just ignored.
+            pass
+
+        server_max_window_bits = None
+        if self._request.has_parameter(self._SERVER_MAX_WINDOW_BITS_PARAM):
+            server_max_window_bits = self._request.get_parameter_value(
+                    self._SERVER_MAX_WINDOW_BITS_PARAM)
+            try:
+                server_max_window_bits = _parse_window_bits(
+                    server_max_window_bits)
+            except ValueError, e:
+                self._logger.debug('Bad %s parameter: %r',
+                                   self._SERVER_MAX_WINDOW_BITS_PARAM,
+                                   e)
+                return None
+
+        server_no_context_takeover = self._request.has_parameter(
+            self._SERVER_NO_CONTEXT_TAKEOVER_PARAM)
+        if (server_no_context_takeover and
+            self._request.get_parameter_value(
+                self._SERVER_NO_CONTEXT_TAKEOVER_PARAM) is not None):
+            self._logger.debug('%s parameter must not have a value: %r',
+                               self._SERVER_NO_CONTEXT_TAKEOVER_PARAM,
+                               server_no_context_takeover)
+            return None
+
+        # client_max_window_bits from a client indicates whether the client can
+        # accept client_max_window_bits from a server or not.
+        client_client_max_window_bits = self._request.has_parameter(
+            self._CLIENT_MAX_WINDOW_BITS_PARAM)
+        if (self._draft08 and
+            client_client_max_window_bits and
+            self._request.get_parameter_value(
+                self._CLIENT_MAX_WINDOW_BITS_PARAM) is not None):
+            self._logger.debug('%s parameter must not have a value in a '
+                               'client\'s opening handshake: %r',
+                               self._CLIENT_MAX_WINDOW_BITS_PARAM,
+                               client_client_max_window_bits)
+            return None
+
+        self._rfc1979_deflater = util._RFC1979Deflater(
+            server_max_window_bits, server_no_context_takeover)
+
+        # Note that we prepare for incoming messages compressed with window
+        # bits upto 15 regardless of the client_max_window_bits value to be
+        # sent to the client.
+        self._rfc1979_inflater = util._RFC1979Inflater()
+
+        self._framer = _PerMessageDeflateFramer(
+            server_max_window_bits, server_no_context_takeover)
+        self._framer.set_bfinal(False)
+        self._framer.set_compress_outgoing_enabled(True)
+
+        response = common.ExtensionParameter(self._request.name())
+
+        if server_max_window_bits is not None:
+            response.add_parameter(
+                self._SERVER_MAX_WINDOW_BITS_PARAM,
+                str(server_max_window_bits))
+
+        if server_no_context_takeover:
+            response.add_parameter(
+                self._SERVER_NO_CONTEXT_TAKEOVER_PARAM, None)
+
+        if self._preferred_client_max_window_bits is not None:
+            if self._draft08 and not client_client_max_window_bits:
+                self._logger.debug('Processor is configured to use %s but '
+                                   'the client cannot accept it',
+                                   self._CLIENT_MAX_WINDOW_BITS_PARAM)
+                return None
+            response.add_parameter(
+                self._CLIENT_MAX_WINDOW_BITS_PARAM,
+                str(self._preferred_client_max_window_bits))
+
+        if self._client_no_context_takeover:
+            response.add_parameter(
+                self._CLIENT_NO_CONTEXT_TAKEOVER_PARAM, None)
+
+        self._logger.debug(
+            'Enable %s extension ('
+            'request: server_max_window_bits=%s; '
+            'server_no_context_takeover=%r, '
+            'response: client_max_window_bits=%s; '
+            'client_no_context_takeover=%r)' %
+            (self._request.name(),
+             server_max_window_bits,
+             server_no_context_takeover,
+             self._preferred_client_max_window_bits,
+             self._client_no_context_takeover))
+
+        return response
+
+    def _setup_stream_options_internal(self, stream_options):
+        self._framer.setup_stream_options(stream_options)
+
+    def set_client_max_window_bits(self, value):
+        """If this option is specified, this class adds the
+        client_max_window_bits extension parameter to the handshake response,
+        but doesn't reduce the LZ77 sliding window size of its inflater.
+        I.e., you can use this for testing client implementation but cannot
+        reduce memory usage of this class.
+
+        If this method has been called with True and an offer without the
+        client_max_window_bits extension parameter is received,
+        - (When processing the permessage-deflate extension) this processor
+          declines the request.
+        - (When processing the permessage-compress extension) this processor
+          accepts the request.
+        """
+
+        self._preferred_client_max_window_bits = value
+
+    def set_client_no_context_takeover(self, value):
+        """If this option is specified, this class adds the
+        client_no_context_takeover extension parameter to the handshake
+        response, but doesn't reset inflater for each message. I.e., you can
+        use this for testing client implementation but cannot reduce memory
+        usage of this class.
+        """
+
+        self._client_no_context_takeover = value
+
+    def set_bfinal(self, value):
+        self._framer.set_bfinal(value)
+
+    def enable_outgoing_compression(self):
+        self._framer.set_compress_outgoing_enabled(True)
+
+    def disable_outgoing_compression(self):
+        self._framer.set_compress_outgoing_enabled(False)
 
 
-# Adding vendor-prefixed deflate-frame extension.
-# TODO(bashi): Remove this after WebKit stops using vender prefix.
-_available_processors[common.X_WEBKIT_DEFLATE_FRAME_EXTENSION] = (
-    DeflateFrameExtensionProcessor)
+class _PerMessageDeflateFramer(object):
+    """A framer for extensions with per-message DEFLATE feature."""
+
+    def __init__(self, deflate_max_window_bits, deflate_no_context_takeover):
+        self._logger = util.get_class_logger(self)
+
+        self._rfc1979_deflater = util._RFC1979Deflater(
+            deflate_max_window_bits, deflate_no_context_takeover)
+
+        self._rfc1979_inflater = util._RFC1979Inflater()
+
+        self._bfinal = False
+
+        self._compress_outgoing_enabled = False
+
+        # True if a message is fragmented and compression is ongoing.
+        self._compress_ongoing = False
+
+        # Calculates
+        #     (Total outgoing bytes supplied to this filter) /
+        #     (Total bytes sent to the network after applying this filter)
+        self._outgoing_average_ratio_calculator = _AverageRatioCalculator()
+
+        # Calculates
+        #     (Total bytes received from the network) /
+        #     (Total incoming bytes obtained after applying this filter)
+        self._incoming_average_ratio_calculator = _AverageRatioCalculator()
+
+    def set_bfinal(self, value):
+        self._bfinal = value
+
+    def set_compress_outgoing_enabled(self, value):
+        self._compress_outgoing_enabled = value
+
+    def _process_incoming_message(self, message, decompress):
+        if not decompress:
+            return message
+
+        received_payload_size = len(message)
+        self._incoming_average_ratio_calculator.add_result_bytes(
+                received_payload_size)
+
+        message = self._rfc1979_inflater.filter(message)
+
+        filtered_payload_size = len(message)
+        self._incoming_average_ratio_calculator.add_original_bytes(
+                filtered_payload_size)
+
+        _log_incoming_compression_ratio(
+                self._logger,
+                received_payload_size,
+                filtered_payload_size,
+                self._incoming_average_ratio_calculator.get_average_ratio())
+
+        return message
+
+    def _process_outgoing_message(self, message, end, binary):
+        if not binary:
+            message = message.encode('utf-8')
+
+        if not self._compress_outgoing_enabled:
+            return message
+
+        original_payload_size = len(message)
+        self._outgoing_average_ratio_calculator.add_original_bytes(
+            original_payload_size)
+
+        message = self._rfc1979_deflater.filter(
+            message, end=end, bfinal=self._bfinal)
+
+        filtered_payload_size = len(message)
+        self._outgoing_average_ratio_calculator.add_result_bytes(
+            filtered_payload_size)
+
+        _log_outgoing_compression_ratio(
+                self._logger,
+                original_payload_size,
+                filtered_payload_size,
+                self._outgoing_average_ratio_calculator.get_average_ratio())
+
+        if not self._compress_ongoing:
+            self._outgoing_frame_filter.set_compression_bit()
+        self._compress_ongoing = not end
+        return message
+
+    def _process_incoming_frame(self, frame):
+        if frame.rsv1 == 1 and not common.is_control_opcode(frame.opcode):
+            self._incoming_message_filter.decompress_next_message()
+            frame.rsv1 = 0
+
+    def _process_outgoing_frame(self, frame, compression_bit):
+        if (not compression_bit or
+            common.is_control_opcode(frame.opcode)):
+            return
+
+        frame.rsv1 = 1
+
+    def setup_stream_options(self, stream_options):
+        """Creates filters and sets them to the StreamOptions."""
+
+        class _OutgoingMessageFilter(object):
+
+            def __init__(self, parent):
+                self._parent = parent
+
+            def filter(self, message, end=True, binary=False):
+                return self._parent._process_outgoing_message(
+                    message, end, binary)
+
+        class _IncomingMessageFilter(object):
+
+            def __init__(self, parent):
+                self._parent = parent
+                self._decompress_next_message = False
+
+            def decompress_next_message(self):
+                self._decompress_next_message = True
+
+            def filter(self, message):
+                message = self._parent._process_incoming_message(
+                    message, self._decompress_next_message)
+                self._decompress_next_message = False
+                return message
+
+        self._outgoing_message_filter = _OutgoingMessageFilter(self)
+        self._incoming_message_filter = _IncomingMessageFilter(self)
+        stream_options.outgoing_message_filters.append(
+            self._outgoing_message_filter)
+        stream_options.incoming_message_filters.append(
+            self._incoming_message_filter)
+
+        class _OutgoingFrameFilter(object):
+
+            def __init__(self, parent):
+                self._parent = parent
+                self._set_compression_bit = False
+
+            def set_compression_bit(self):
+                self._set_compression_bit = True
+
+            def filter(self, frame):
+                self._parent._process_outgoing_frame(
+                    frame, self._set_compression_bit)
+                self._set_compression_bit = False
+
+        class _IncomingFrameFilter(object):
+
+            def __init__(self, parent):
+                self._parent = parent
+
+            def filter(self, frame):
+                self._parent._process_incoming_frame(frame)
+
+        self._outgoing_frame_filter = _OutgoingFrameFilter(self)
+        self._incoming_frame_filter = _IncomingFrameFilter(self)
+        stream_options.outgoing_frame_filters.append(
+            self._outgoing_frame_filter)
+        stream_options.incoming_frame_filters.append(
+            self._incoming_frame_filter)
+
+        stream_options.encode_text_message_to_utf8 = False
+
+
+_available_processors[common.PERMESSAGE_DEFLATE_EXTENSION] = (
+        PerMessageDeflateExtensionProcessor)
+# TODO(tyoshino): Reorganize class names.
+_compression_extension_names.append('deflate')
+
+
+class MuxExtensionProcessor(ExtensionProcessorInterface):
+    """WebSocket multiplexing extension processor."""
+
+    _QUOTA_PARAM = 'quota'
+
+    def __init__(self, request):
+        ExtensionProcessorInterface.__init__(self, request)
+        self._quota = 0
+        self._extensions = []
+
+    def name(self):
+        return common.MUX_EXTENSION
+
+    def check_consistency_with_other_processors(self, processors):
+        before_mux = True
+        for processor in processors:
+            name = processor.name()
+            if name == self.name():
+                before_mux = False
+                continue
+            if not processor.is_active():
+                continue
+            if before_mux:
+                # Mux extension cannot be used after extensions
+                # that depend on frame boundary, extension data field, or any
+                # reserved bits which are attributed to each frame.
+                if (name == common.DEFLATE_FRAME_EXTENSION or
+                    name == common.X_WEBKIT_DEFLATE_FRAME_EXTENSION):
+                    self.set_active(False)
+                    return
+            else:
+                # Mux extension should not be applied before any history-based
+                # compression extension.
+                if (name == 'deflate' or
+                    name == common.DEFLATE_FRAME_EXTENSION or
+                    name == common.X_WEBKIT_DEFLATE_FRAME_EXTENSION):
+                    self.set_active(False)
+                    return
+
+    def _get_extension_response_internal(self):
+        self._active = False
+        quota = self._request.get_parameter_value(self._QUOTA_PARAM)
+        if quota is not None:
+            try:
+                quota = int(quota)
+            except ValueError, e:
+                return None
+            if quota < 0 or quota >= 2 ** 32:
+                return None
+            self._quota = quota
+
+        self._active = True
+        return common.ExtensionParameter(common.MUX_EXTENSION)
+
+    def _setup_stream_options_internal(self, stream_options):
+        pass
+
+    def set_quota(self, quota):
+        self._quota = quota
+
+    def quota(self):
+        return self._quota
+
+    def set_extensions(self, extensions):
+        self._extensions = extensions
+
+    def extensions(self):
+        return self._extensions
+
+
+_available_processors[common.MUX_EXTENSION] = MuxExtensionProcessor
 
 
 def get_extension_processor(extension_request):
-    global _available_processors
+    """Given an ExtensionParameter representing an extension offer received
+    from a client, configures and returns an instance of the corresponding
+    extension processor class.
+    """
+
     processor_class = _available_processors.get(extension_request.name())
     if processor_class is None:
         return None
     return processor_class(extension_request)
 
 
+def is_compression_extension(extension_name):
+    return extension_name in _compression_extension_names
+
+
 # vi:sts=4 sw=4 et
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/fast_masking.i
@@ -0,0 +1,98 @@
+// Copyright 2013, 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.
+
+
+%module fast_masking
+
+%include "cstring.i"
+
+%{
+#include <cstring>
+
+#ifdef __SSE2__
+#include <emmintrin.h>
+#endif
+%}
+
+%apply (char *STRING, int LENGTH) {
+    (const char* payload, int payload_length),
+    (const char* masking_key, int masking_key_length) };
+%cstring_output_allocate_size(
+    char** result, int* result_length, delete [] *$1);
+
+%inline %{
+
+void mask(
+    const char* payload, int payload_length,
+    const char* masking_key, int masking_key_length,
+    int masking_key_index,
+    char** result, int* result_length) {
+  *result = new char[payload_length];
+  *result_length = payload_length;
+  memcpy(*result, payload, payload_length);
+
+  char* cursor = *result;
+  char* cursor_end = *result + *result_length;
+
+#ifdef __SSE2__
+  while ((cursor < cursor_end) &&
+         (reinterpret_cast<size_t>(cursor) & 0xf)) {
+    *cursor ^= masking_key[masking_key_index];
+    ++cursor;
+    masking_key_index = (masking_key_index + 1) % masking_key_length;
+  }
+  if (cursor == cursor_end) {
+    return;
+  }
+
+  const int kBlockSize = 16;
+  __m128i masking_key_block;
+  for (int i = 0; i < kBlockSize; ++i) {
+    *(reinterpret_cast<char*>(&masking_key_block) + i) =
+        masking_key[masking_key_index];
+    masking_key_index = (masking_key_index + 1) % masking_key_length;
+  }
+
+  while (cursor + kBlockSize <= cursor_end) {
+    __m128i payload_block =
+        _mm_load_si128(reinterpret_cast<__m128i*>(cursor));
+    _mm_stream_si128(reinterpret_cast<__m128i*>(cursor),
+                     _mm_xor_si128(payload_block, masking_key_block));
+    cursor += kBlockSize;
+  }
+#endif
+
+  while (cursor < cursor_end) {
+    *cursor ^= masking_key[masking_key_index];
+    ++cursor;
+    masking_key_index = (masking_key_index + 1) % masking_key_length;
+  }
+}
+
+%}
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/__init__.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/__init__.py
@@ -32,17 +32,16 @@
 opening handshake processors for each protocol version until a connection is
 successfully established.
 """
 
 
 import logging
 
 from mod_pywebsocket import common
-from mod_pywebsocket.handshake import draft75
 from mod_pywebsocket.handshake import hybi00
 from mod_pywebsocket.handshake import hybi
 # Export AbortedByUserException, HandshakeException, and VersionException
 # symbol from this module.
 from mod_pywebsocket.handshake._base import AbortedByUserException
 from mod_pywebsocket.handshake._base import HandshakeException
 from mod_pywebsocket.handshake._base import VersionException
 
@@ -51,20 +50,18 @@ from mod_pywebsocket.handshake._base imp
 
 
 def do_handshake(request, dispatcher, allowDraft75=False, strict=False):
     """Performs WebSocket handshake.
 
     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.
+        allowDraft75: obsolete argument. ignored.
+        strict: obsolete argument. ignored.
 
     Handshaker will add attributes such as ws_resource in performing
     handshake.
     """
 
     _LOGGER.debug('Client\'s 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
@@ -81,19 +78,16 @@ def do_handshake(request, dispatcher, al
     _LOGGER.debug(
         'Client\'s opening handshake headers: %r', dict(request.headers_in))
 
     handshakers = []
     handshakers.append(
         ('RFC 6455', hybi.Handshaker(request, dispatcher)))
     handshakers.append(
         ('HyBi 00', hybi00.Handshaker(request, dispatcher)))
-    if allowDraft75:
-        handshakers.append(
-            ('Hixie 75', draft75.Handshaker(request, dispatcher, strict)))
 
     for name, handshaker in handshakers:
         _LOGGER.debug('Trying protocol version %s', name)
         try:
             handshaker.do_handshake()
             _LOGGER.info('Established (%s protocol)', name)
             return
         except HandshakeException, e:
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/_base.py
@@ -1,9 +1,9 @@
-# Copyright 2011, Google Inc.
+# Copyright 2012, 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.
@@ -79,82 +79,51 @@ class VersionException(Exception):
 
 def get_default_port(is_secure):
     if is_secure:
         return common.DEFAULT_WEB_SOCKET_SECURE_PORT
     else:
         return common.DEFAULT_WEB_SOCKET_PORT
 
 
-def validate_subprotocol(subprotocol, hixie):
-    """Validate a value in subprotocol fields such as WebSocket-Protocol,
-    Sec-WebSocket-Protocol.
+def validate_subprotocol(subprotocol):
+    """Validate a value in the Sec-WebSocket-Protocol field.
 
-    See
-    - RFC 6455: Section 4.1., 4.2.2., and 4.3.
-    - HyBi 00: Section 4.1. Opening handshake
-    - Hixie 75: Section 4.1. Handshake
+    See the Section 4.1., 4.2.2., and 4.3. of RFC 6455.
     """
 
     if not subprotocol:
         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)
+
+    # 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)
+    fields = request.headers_in[common.HOST_HEADER].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 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."""
-    location_parts = []
-    if request.is_https():
-        location_parts.append(common.WEB_SOCKET_SECURE_SCHEME)
-    else:
-        location_parts.append(common.WEB_SOCKET_SCHEME)
-    location_parts.append('://')
-    host, port = parse_host_header(request)
-    connection_port = request.connection.local_addr[1]
-    if 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 HandshakeException('Header %s is not defined' % key)
     return value
 
 
 def validate_mandatory_header(request, key, expected_value, fail_status=None):
@@ -165,27 +134,21 @@ def validate_mandatory_header(request, k
             'Expected %r for header %s but found %r (case-insensitive)' %
             (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 HandshakeException('Method is not GET')
-
-
-def check_header_lines(request, mandatory_headers):
-    check_request_line(request)
+        raise HandshakeException('Method is not GET: %r' % request.method)
 
-    # The expected field names, and the meaning of their corresponding
-    # values, are as follows.
-    #  |Upgrade| and |Connection|
-    for key, expected_value in mandatory_headers:
-        validate_mandatory_header(request, key, expected_value)
+    if request.protocol != 'HTTP/1.1':
+        raise HandshakeException('Version is not HTTP/1.1: %r' %
+                                 request.protocol)
 
 
 def parse_token_list(data):
     """Parses a header value which follows 1#token and returns parsed elements
     as a list of strings.
 
     Leading LWSes must be trimmed.
     """
@@ -211,113 +174,9 @@ def parse_token_list(data):
         http_header_util.consume_lwses(state)
 
     if len(token_list) == 0:
         raise HandshakeException('No valid token found')
 
     return token_list
 
 
-def _parse_extension_param(state, definition, allow_quoted_string):
-    param_name = http_header_util.consume_token(state)
-
-    if param_name is None:
-        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)
-
-    if allow_quoted_string:
-        # TODO(toyoshim): Add code to validate that parsed param_value is token
-        param_value = http_header_util.consume_token_or_quoted_string(state)
-    else:
-        param_value = http_header_util.consume_token(state)
-    if param_value is None:
-        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, allow_quoted_string):
-    extension_token = http_header_util.consume_token(state)
-    if extension_token is None:
-        return None
-
-    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, allow_quoted_string)
-        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, allow_quoted_string=False):
-    """Parses Sec-WebSocket-Extensions header value returns a list of
-    common.ExtensionParameter objects.
-
-    Leading LWSes must be trimmed.
-    """
-
-    state = http_header_util.ParsingState(data)
-
-    extension_list = []
-    while True:
-        extension = _parse_extension(state, allow_quoted_string)
-        if extension is not None:
-            extension_list.append(extension)
-
-        http_header_util.consume_lwses(state)
-
-        if http_header_util.peek(state) is None:
-            break
-
-        if not http_header_util.consume_string(state, ','):
-            raise 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 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_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
deleted file mode 100644
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/draft75.py
+++ /dev/null
@@ -1,190 +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 handshaking defined in draft-hixie-thewebsocketprotocol-75."""
-
-
-# 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 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 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'],
-    ['Host', None],
-    ['Origin', None],
-]
-
-_FIRST_FIVE_LINES = map(re.compile, [
-    r'^GET /[\S]* HTTP/1.1\r\n$',
-    r'^Upgrade: WebSocket\r\n$',
-    r'^Connection: Upgrade\r\n$',
-    r'^Host: [\S]+\r\n$',
-    r'^Origin: [\S]+\r\n$',
-])
-
-_SIXTH_AND_LATER = re.compile(
-    r'^'
-    r'(WebSocket-Protocol: [\x20-\x7e]+\r\n)?'
-    r'(Cookie: [^\r]*\r\n)*'
-    r'(Cookie2: [^\r]*\r\n)?'
-    r'(Cookie: [^\r]*\r\n)*'
-    r'\r\n')
-
-
-class Handshaker(object):
-    """This class performs WebSocket handshake."""
-
-    def __init__(self, request, dispatcher, strict=False):
-        """Construct an instance.
-
-        Args:
-            request: mod_python request.
-            dispatcher: Dispatcher (dispatch.Dispatcher).
-            strict: Strictly check handshake request. Default: False.
-                If True, request.connection must provide get_memorized_lines
-                method.
-
-        Handshaker will add attributes such as ws_resource in performing
-        handshake.
-        """
-
-        self._logger = util.get_class_logger(self)
-
-        self._request = request
-        self._dispatcher = dispatcher
-        self._strict = strict
-
-    def do_handshake(self):
-        """Perform WebSocket Handshake.
-
-        On _request, we set
-            ws_resource, ws_origin, ws_location, ws_protocol
-            ws_challenge_md5: WebSocket handshake information.
-            ws_stream: Frame generation/parsing class.
-            ws_version: Protocol version.
-        """
-
-        self._check_header_lines()
-        self._set_resource()
-        self._set_origin()
-        self._set_location()
-        self._set_subprotocol()
-        self._set_protocol_version()
-
-        self._dispatcher.do_extra_handshake(self._request)
-
-        self._send_handshake()
-
-        self._logger.debug('Sent opening handshake response')
-
-    def _set_resource(self):
-        self._request.ws_resource = self._request.uri
-
-    def _set_origin(self):
-        self._request.ws_origin = self._request.headers_in['Origin']
-
-    def _set_location(self):
-        self._request.ws_location = build_location(self._request)
-
-    def _set_subprotocol(self):
-        subprotocol = self._request.headers_in.get('WebSocket-Protocol')
-        if subprotocol is not None:
-            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):
-        self._request.connection.write(data)
-
-    def _send_handshake(self):
-        self._sendall('HTTP/1.1 101 Web Socket Protocol Handshake\r\n')
-        self._sendall('Upgrade: WebSocket\r\n')
-        self._sendall('Connection: Upgrade\r\n')
-        self._sendall('WebSocket-Origin: %s\r\n' % self._request.ws_origin)
-        self._sendall('WebSocket-Location: %s\r\n' % self._request.ws_location)
-        if self._request.ws_protocol:
-            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 HandshakeException('Header %s is not defined' % key)
-            if expected_value:
-                if actual_value != expected_value:
-                    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 HandshakeException('Too few header lines: %d' % len(lines))
-        for line, regexp in zip(lines, _FIRST_FIVE_LINES):
-            if not regexp.search(line):
-                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 HandshakeException(
-                'Unexpected header: %r doesn\'t match %r'
-                % (sixth_and_later, _SIXTH_AND_LATER.pattern))
-
-
-# vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/handshake/hybi.py
@@ -1,9 +1,9 @@
-# Copyright 2011, Google Inc.
+# Copyright 2012, 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.
@@ -44,44 +44,40 @@ http://tools.ietf.org/html/rfc6455
 
 import base64
 import logging
 import os
 import re
 
 from mod_pywebsocket import common
 from mod_pywebsocket.extensions import get_extension_processor
+from mod_pywebsocket.extensions import is_compression_extension
 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.handshake._base import VersionException
 from mod_pywebsocket.stream import Stream
 from mod_pywebsocket.stream import StreamOptions
 from mod_pywebsocket import util
 
 
 # Used to validate the value in the Sec-WebSocket-Key header strictly. RFC 4648
 # disallows non-zero padding, so the character right before == must be any of
 # A, Q, g and w.
 _SEC_WEBSOCKET_KEY_REGEX = re.compile('^[+/0-9A-Za-z]{21}[AQgw]==$')
 
 # Defining aliases for values used frequently.
-_VERSION_HYBI08 = common.VERSION_HYBI08
-_VERSION_HYBI08_STRING = str(_VERSION_HYBI08)
 _VERSION_LATEST = common.VERSION_HYBI_LATEST
 _VERSION_LATEST_STRING = str(_VERSION_LATEST)
 _SUPPORTED_VERSIONS = [
     _VERSION_LATEST,
-    _VERSION_HYBI08,
 ]
 
 
 def compute_accept(key):
     """Computes value for the Sec-WebSocket-Accept header from value of the
     Sec-WebSocket-Key header.
     """
 
@@ -146,19 +142,16 @@ class Handshaker(object):
         self._validate_connection_header()
 
         self._request.ws_resource = self._request.uri
 
         unused_host = get_mandatory_header(self._request, common.HOST_HEADER)
 
         self._request.ws_version = 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()
@@ -177,53 +170,103 @@ class Handshaker(object):
             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
 
+            # List of extra headers. The extra handshake handler may add header
+            # data as name/value pairs to this list and pywebsocket appends
+            # them to the WebSocket handshake.
+            self._request.extra_headers = []
+
             # Extra handshake handler may modify/remove processors.
             self._dispatcher.do_extra_handshake(self._request)
+            processors = filter(lambda processor: processor is not None,
+                                self._request.ws_extension_processors)
+
+            # Ask each processor if there are extensions on the request which
+            # cannot co-exist. When processor decided other processors cannot
+            # co-exist with it, the processor marks them (or itself) as
+            # "inactive". The first extension processor has the right to
+            # make the final call.
+            for processor in reversed(processors):
+                if processor.is_active():
+                    processor.check_consistency_with_other_processors(
+                        processors)
+            processors = filter(lambda processor: processor.is_active(),
+                                processors)
+
+            accepted_extensions = []
+
+            # We need to take into account of mux extension here.
+            # If mux extension exists:
+            # - Remove processors of extensions for logical channel,
+            #   which are processors located before the mux processor
+            # - Pass extension requests for logical channel to mux processor
+            # - Attach the mux processor to the request. It will be referred
+            #   by dispatcher to see whether the dispatcher should use mux
+            #   handler or not.
+            mux_index = -1
+            for i, processor in enumerate(processors):
+                if processor.name() == common.MUX_EXTENSION:
+                    mux_index = i
+                    break
+            if mux_index >= 0:
+                logical_channel_extensions = []
+                for processor in processors[:mux_index]:
+                    logical_channel_extensions.append(processor.request())
+                    processor.set_active(False)
+                self._request.mux_processor = processors[mux_index]
+                self._request.mux_processor.set_extensions(
+                    logical_channel_extensions)
+                processors = filter(lambda processor: processor.is_active(),
+                                    processors)
 
             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.
+            for index, processor in enumerate(processors):
+                if not processor.is_active():
                     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)
+                accepted_extensions.append(extension_response)
 
                 processor.setup_stream_options(stream_options)
 
-            if self._request.ws_extensions is not None:
+                if not is_compression_extension(processor.name()):
+                    continue
+
+                # Inactivate all of the following compression extensions.
+                for j in xrange(index + 1, len(processors)):
+                    if is_compression_extension(processors[j].name()):
+                        processors[j].set_active(False)
+
+            if len(accepted_extensions) > 0:
+                self._request.ws_extensions = accepted_extensions
                 self._logger.debug(
                     'Extensions accepted: %r',
-                    map(common.ExtensionParameter.name,
-                        self._request.ws_extensions))
+                    map(common.ExtensionParameter.name, accepted_extensions))
+            else:
+                self._request.ws_extensions = None
 
-            self._request.ws_stream = Stream(self._request, stream_options)
+            self._request.ws_stream = self._create_stream(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)
+                validate_subprotocol(self._request.ws_protocol)
 
                 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 '
@@ -232,30 +275,25 @@ class Handshaker(object):
             self._send_handshake(accept)
         except HandshakeException, e:
             if not e.status:
                 # Fallback to 400 bad request by default.
                 e.status = common.HTTP_STATUS_BAD_REQUEST
             raise e
 
     def _get_origin(self):
-        if self._request.ws_version is _VERSION_HYBI08:
-            origin_header = common.SEC_WEBSOCKET_ORIGIN_HEADER
-        else:
-            origin_header = common.ORIGIN_HEADER
+        origin_header = common.ORIGIN_HEADER
         origin = self._request.headers_in.get(origin_header)
         if origin is None:
             self._logger.debug('Client request does not have origin header')
         self._request.ws_origin = origin
 
     def _check_version(self):
         version = get_mandatory_header(self._request,
                                        common.SEC_WEBSOCKET_VERSION_HEADER)
-        if version == _VERSION_HYBI08_STRING:
-            return _VERSION_HYBI08
         if version == _VERSION_LATEST_STRING:
             return _VERSION_LATEST
 
         if version.find(',') >= 0:
             raise HandshakeException(
                 'Multiple versions (%r) are not allowed for header %s' %
                 (version, common.SEC_WEBSOCKET_VERSION_HEADER),
                 status=common.HTTP_STATUS_BAD_REQUEST)
@@ -268,38 +306,38 @@ class Handshaker(object):
         self._request.ws_protocol = None
         # MOZILLA
         self._request.sts = None
         # /MOZILLA
 
         protocol_header = self._request.headers_in.get(
             common.SEC_WEBSOCKET_PROTOCOL_HEADER)
 
-        if not protocol_header:
+        if protocol_header is None:
             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
 
-        if self._request.ws_version is common.VERSION_HYBI08:
-            allow_quoted_string=False
-        else:
-            allow_quoted_string=True
-        self._request.ws_requested_extensions = parse_extensions(
-            extensions_header, allow_quoted_string=allow_quoted_string)
+        try:
+            self._request.ws_requested_extensions = common.parse_extensions(
+                extensions_header)
+        except common.ExtensionParsingException, e:
+            raise HandshakeException(
+                'Failed to parse Sec-WebSocket-Extensions header: %r' % e)
 
         self._logger.debug(
             'Extensions requested: %r',
             map(common.ExtensionParameter.name,
                 self._request.ws_requested_extensions))
 
     def _validate_key(self, key):
         if key.find(',') >= 0:
@@ -337,42 +375,54 @@ class Handshaker(object):
         self._logger.debug(
             '%s: %r (%s)',
             common.SEC_WEBSOCKET_KEY_HEADER,
             key,
             util.hexify(decoded_key))
 
         return key
 
-    def _send_handshake(self, accept):
+    def _create_stream(self, stream_options):
+        return Stream(self._request, stream_options)
+
+    def _create_handshake_response(self, accept):
         response = []
 
         response.append('HTTP/1.1 101 Switching Protocols\r\n')
 
+        # WebSocket headers
         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)))
+                common.format_extensions(self._request.ws_extensions)))
         # MOZILLA: Add HSTS header if requested to
         if self._request.sts is not None:
             response.append(format_header("Strict-Transport-Security",
                                           self._request.sts))
         # /MOZILLA
+
+        # Headers not specific for WebSocket
+        for name, value in self._request.extra_headers:
+            response.append(format_header(name, value))
+
         response.append('\r\n')
 
-        raw_response = ''.join(response)
+        return ''.join(response)
+
+    def _send_handshake(self, accept):
+        raw_response = self._create_handshake_response(accept)
         self._request.connection.write(raw_response)
         self._logger.debug('Sent server\'s opening handshake: %r',
                            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
@@ -46,30 +46,81 @@ http://tools.ietf.org/html/draft-ietf-hy
 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 HandshakeException
-from mod_pywebsocket.handshake._base import build_location
-from mod_pywebsocket.handshake._base import check_header_lines
+from mod_pywebsocket.handshake._base import check_request_line
 from mod_pywebsocket.handshake._base import format_header
+from mod_pywebsocket.handshake._base import get_default_port
 from mod_pywebsocket.handshake._base import get_mandatory_header
-from mod_pywebsocket.handshake._base import validate_subprotocol
+from mod_pywebsocket.handshake._base import parse_host_header
+from mod_pywebsocket.handshake._base import validate_mandatory_header
 
 
 _MANDATORY_HEADERS = [
     # key, expected value or None
     [common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE_HIXIE75],
     [common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE],
 ]
 
 
+def _validate_subprotocol(subprotocol):
+    """Checks if characters in subprotocol are in range between U+0020 and
+    U+007E. A value in the Sec-WebSocket-Protocol field need to satisfy this
+    requirement.
+
+    See the Section 4.1. Opening handshake of the spec.
+    """
+
+    if not subprotocol:
+        raise HandshakeException('Invalid subprotocol name: empty')
+
+    # 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)
+
+
+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|
+    for key, expected_value in mandatory_headers:
+        validate_mandatory_header(request, key, expected_value)
+
+
+def _build_location(request):
+    """Build WebSocket location for request."""
+
+    location_parts = []
+    if request.is_https():
+        location_parts.append(common.WEB_SOCKET_SECURE_SCHEME)
+    else:
+        location_parts.append(common.WEB_SOCKET_SCHEME)
+    location_parts.append('://')
+    host, port = parse_host_header(request)
+    connection_port = request.connection.local_addr[1]
+    if 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.unparsed_uri)
+    return ''.join(location_parts)
+
+
 class Handshaker(object):
     """Opening handshake processor for the WebSocket protocol version HyBi 00.
     """
 
     def __init__(self, request, dispatcher):
         """Construct an instance.
 
         Args:
@@ -96,17 +147,17 @@ class Handshaker(object):
 
         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)
+        _check_header_lines(self._request, _MANDATORY_HEADERS)
         self._set_resource()
         self._set_subprotocol()
         self._set_location()
         self._set_origin()
         self._set_challenge_response()
         self._set_protocol_version()
 
         self._dispatcher.do_extra_handshake(self._request)
@@ -116,24 +167,24 @@ 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, hixie=True)
+            _validate_subprotocol(subprotocol)
         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)
+            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.get(common.ORIGIN_HEADER)
         if origin is not None:
             self._request.ws_origin = origin
 
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/headerparserhandler.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/headerparserhandler.py
@@ -58,18 +58,19 @@ from mod_pywebsocket import util
 # Set this option with value of 'yes' to allow.
 _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT = (
     'mod_pywebsocket.allow_handlers_outside_root_dir')
 # Map from values to their meanings. 'Yes' and 'No' are allowed just for
 # compatibility.
 _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT_DEFINITION = {
     'off': False, 'no': False, 'on': True, 'yes': True}
 
-# PythonOption to specify to allow draft75 handshake.
-# The default is None (Off)
+# (Obsolete option. Ignored.)
+# PythonOption to specify to allow handshake defined in Hixie 75 version
+# protocol. The default is None (Off)
 _PYOPT_ALLOW_DRAFT75 = 'mod_pywebsocket.allow_draft75'
 # Map from values to their meanings.
 _PYOPT_ALLOW_DRAFT75_DEFINITION = {'off': False, 'on': True}
 
 
 class ApacheLogHandler(logging.Handler):
     """Wrapper logging.Handler to emit log message to apache's error.log."""
 
@@ -161,17 +162,19 @@ def _create_dispatcher():
         _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT,
         options.get(_PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT),
         _PYOPT_ALLOW_HANDLERS_OUTSIDE_ROOT_DEFINITION)
 
     dispatcher = dispatch.Dispatcher(
         handler_root, handler_scan, allow_handlers_outside_root)
 
     for warning in dispatcher.source_warnings():
-        apache.log_error('mod_pywebsocket: %s' % warning, apache.APLOG_WARNING)
+        apache.log_error(
+            'mod_pywebsocket: Warning in source loading: %s' % warning,
+            apache.APLOG_WARNING)
 
     return dispatcher
 
 
 # Initialize
 _dispatcher = _create_dispatcher()
 
 
@@ -185,55 +188,63 @@ def headerparserhandler(request):
     name for a PythonHeaderParserHandler.
     """
 
     handshake_is_done = False
     try:
         # Fallback to default http handler for request paths for which
         # we don't have request handlers.
         if not _dispatcher.get_handler_suite(request.uri):
-            request.log_error('No handler for resource: %r' % request.uri,
-                              apache.APLOG_INFO)
-            request.log_error('Fallback to Apache', apache.APLOG_INFO)
+            request.log_error(
+                'mod_pywebsocket: No handler for resource: %r' % request.uri,
+                apache.APLOG_INFO)
+            request.log_error(
+                'mod_pywebsocket: Fallback to Apache', apache.APLOG_INFO)
             return apache.DECLINED
     except dispatch.DispatchException, e:
-        request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO)
+        request.log_error(
+            'mod_pywebsocket: Dispatch failed for error: %s' % e,
+            apache.APLOG_INFO)
         if not handshake_is_done:
             return e.status
 
     try:
         allow_draft75 = _parse_option(
             _PYOPT_ALLOW_DRAFT75,
             apache.main_server.get_options().get(_PYOPT_ALLOW_DRAFT75),
             _PYOPT_ALLOW_DRAFT75_DEFINITION)
 
         try:
             handshake.do_handshake(
                 request, _dispatcher, allowDraft75=allow_draft75)
         except handshake.VersionException, e:
-            request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO)
+            request.log_error(
+                'mod_pywebsocket: Handshake failed for version error: %s' % e,
+                apache.APLOG_INFO)
             request.err_headers_out.add(common.SEC_WEBSOCKET_VERSION_HEADER,
                                         e.supported_versions)
             return apache.HTTP_BAD_REQUEST
         except handshake.HandshakeException, e:
             # Handshake for ws/wss failed.
             # Send http response with error status.
-            request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO)
+            request.log_error(
+                'mod_pywebsocket: Handshake failed for error: %s' % e,
+                apache.APLOG_INFO)
             return e.status
 
         handshake_is_done = True
         request._dispatcher = _dispatcher
         _dispatcher.transfer_data(request)
     except handshake.AbortedByUserException, e:
-        request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_INFO)
+        request.log_error('mod_pywebsocket: Aborted: %s' % e, apache.APLOG_INFO)
     except Exception, e:
         # DispatchException can also be thrown if something is wrong in
         # pywebsocket code. It's caught here, then.
 
-        request.log_error('mod_pywebsocket: %s\n%s' %
+        request.log_error('mod_pywebsocket: Exception occurred: %s\n%s' %
                           (e, util.get_stack_trace()),
                           apache.APLOG_ERR)
         # Unknown exceptions before handshake mean Apache must handle its
         # request with another handler.
         if not handshake_is_done:
             return apache.DECLINED
     # Set assbackwards to suppress response header generation by Apache.
     request.assbackwards = 1
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/msgutil.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/msgutil.py
@@ -54,30 +54,30 @@ def close_connection(request):
     """Close connection.
 
     Args:
         request: mod_python request.
     """
     request.ws_stream.close_connection()
 
 
-def send_message(request, message, end=True, binary=False):
-    """Send message.
+def send_message(request, payload_data, end=True, binary=False):
+    """Send a message (or part of a message).
 
     Args:
         request: mod_python request.
-        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.
+        payload_data: unicode text or str binary to send.
+        end: True to terminate a message.
+             False to send payload_data as part of a message that is to be
+             terminated by next or later send_message call with end=True.
+        binary: send payload_data as binary frame(s).
     Raises:
         BadOperationException: when server already terminated.
     """
-    request.ws_stream.send_message(message, end, binary)
+    request.ws_stream.send_message(payload_data, end, binary)
 
 
 def receive_message(request):
     """Receive a WebSocket frame and return its payload as a text in
     unicode or a binary in str.
 
     Args:
         request: mod_python request.
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/mux.py
@@ -0,0 +1,1889 @@
+# Copyright 2012, 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.
+
+
+"""This file provides classes and helper functions for multiplexing extension.
+
+Specification:
+http://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-06
+"""
+
+
+import collections
+import copy
+import email
+import email.parser
+import logging
+import math
+import struct
+import threading
+import traceback
+
+from mod_pywebsocket import common
+from mod_pywebsocket import handshake
+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_hybi import Frame
+from mod_pywebsocket._stream_hybi import Stream
+from mod_pywebsocket._stream_hybi import StreamOptions
+from mod_pywebsocket._stream_hybi import create_binary_frame
+from mod_pywebsocket._stream_hybi import create_closing_handshake_body
+from mod_pywebsocket._stream_hybi import create_header
+from mod_pywebsocket._stream_hybi import create_length_header
+from mod_pywebsocket._stream_hybi import parse_frame
+from mod_pywebsocket.handshake import hybi
+
+
+_CONTROL_CHANNEL_ID = 0
+_DEFAULT_CHANNEL_ID = 1
+
+_MUX_OPCODE_ADD_CHANNEL_REQUEST = 0
+_MUX_OPCODE_ADD_CHANNEL_RESPONSE = 1
+_MUX_OPCODE_FLOW_CONTROL = 2
+_MUX_OPCODE_DROP_CHANNEL = 3
+_MUX_OPCODE_NEW_CHANNEL_SLOT = 4
+
+_MAX_CHANNEL_ID = 2 ** 29 - 1
+
+_INITIAL_NUMBER_OF_CHANNEL_SLOTS = 64
+_INITIAL_QUOTA_FOR_CLIENT = 8 * 1024
+
+_HANDSHAKE_ENCODING_IDENTITY = 0
+_HANDSHAKE_ENCODING_DELTA = 1
+
+# We need only these status code for now.
+_HTTP_BAD_RESPONSE_MESSAGES = {
+    common.HTTP_STATUS_BAD_REQUEST: 'Bad Request',
+}
+
+# DropChannel reason code
+# TODO(bashi): Define all reason code defined in -05 draft.
+_DROP_CODE_NORMAL_CLOSURE = 1000
+
+_DROP_CODE_INVALID_ENCAPSULATING_MESSAGE = 2001
+_DROP_CODE_CHANNEL_ID_TRUNCATED = 2002
+_DROP_CODE_ENCAPSULATED_FRAME_IS_TRUNCATED = 2003
+_DROP_CODE_UNKNOWN_MUX_OPCODE = 2004
+_DROP_CODE_INVALID_MUX_CONTROL_BLOCK = 2005
+_DROP_CODE_CHANNEL_ALREADY_EXISTS = 2006
+_DROP_CODE_NEW_CHANNEL_SLOT_VIOLATION = 2007
+_DROP_CODE_UNKNOWN_REQUEST_ENCODING = 2010
+
+_DROP_CODE_SEND_QUOTA_VIOLATION = 3005
+_DROP_CODE_SEND_QUOTA_OVERFLOW = 3006
+_DROP_CODE_ACKNOWLEDGED = 3008
+_DROP_CODE_BAD_FRAGMENTATION = 3009
+
+
+class MuxUnexpectedException(Exception):
+    """Exception in handling multiplexing extension."""
+    pass
+
+
+# Temporary
+class MuxNotImplementedException(Exception):
+    """Raised when a flow enters unimplemented code path."""
+    pass
+
+
+class LogicalConnectionClosedException(Exception):
+    """Raised when logical connection is gracefully closed."""
+    pass
+
+
+class PhysicalConnectionError(Exception):
+    """Raised when there is a physical connection error."""
+    def __init__(self, drop_code, message=''):
+        super(PhysicalConnectionError, self).__init__(
+            'code=%d, message=%r' % (drop_code, message))
+        self.drop_code = drop_code
+        self.message = message
+
+
+class LogicalChannelError(Exception):
+    """Raised when there is a logical channel error."""
+    def __init__(self, channel_id, drop_code, message=''):
+        super(LogicalChannelError, self).__init__(
+            'channel_id=%d, code=%d, message=%r' % (
+                channel_id, drop_code, message))
+        self.channel_id = channel_id
+        self.drop_code = drop_code
+        self.message = message
+
+
+def _encode_channel_id(channel_id):
+    if channel_id < 0:
+        raise ValueError('Channel id %d must not be negative' % channel_id)
+
+    if channel_id < 2 ** 7:
+        return chr(channel_id)
+    if channel_id < 2 ** 14:
+        return struct.pack('!H', 0x8000 + channel_id)
+    if channel_id < 2 ** 21:
+        first = chr(0xc0 + (channel_id >> 16))
+        return first + struct.pack('!H', channel_id & 0xffff)
+    if channel_id < 2 ** 29:
+        return struct.pack('!L', 0xe0000000 + channel_id)
+
+    raise ValueError('Channel id %d is too large' % channel_id)
+
+
+def _encode_number(number):
+    return create_length_header(number, False)
+
+
+def _create_add_channel_response(channel_id, encoded_handshake,
+                                 encoding=0, rejected=False):
+    if encoding != 0 and encoding != 1:
+        raise ValueError('Invalid encoding %d' % encoding)
+
+    first_byte = ((_MUX_OPCODE_ADD_CHANNEL_RESPONSE << 5) |
+                  (rejected << 4) | encoding)
+    block = (chr(first_byte) +
+             _encode_channel_id(channel_id) +
+             _encode_number(len(encoded_handshake)) +
+             encoded_handshake)
+    return block
+
+
+def _create_drop_channel(channel_id, code=None, message=''):
+    if len(message) > 0 and code is None:
+        raise ValueError('Code must be specified if message is specified')
+
+    first_byte = _MUX_OPCODE_DROP_CHANNEL << 5
+    block = chr(first_byte) + _encode_channel_id(channel_id)
+    if code is None:
+        block += _encode_number(0) # Reason size
+    else:
+        reason = struct.pack('!H', code) + message
+        reason_size = _encode_number(len(reason))
+        block += reason_size + reason
+
+    return block
+
+
+def _create_flow_control(channel_id, replenished_quota):
+    first_byte = _MUX_OPCODE_FLOW_CONTROL << 5
+    block = (chr(first_byte) +
+             _encode_channel_id(channel_id) +
+             _encode_number(replenished_quota))
+    return block
+
+
+def _create_new_channel_slot(slots, send_quota):
+    if slots < 0 or send_quota < 0:
+        raise ValueError('slots and send_quota must be non-negative.')
+    first_byte = _MUX_OPCODE_NEW_CHANNEL_SLOT << 5
+    block = (chr(first_byte) +
+             _encode_number(slots) +
+             _encode_number(send_quota))
+    return block
+
+
+def _create_fallback_new_channel_slot():
+    first_byte = (_MUX_OPCODE_NEW_CHANNEL_SLOT << 5) | 1 # Set the F flag
+    block = (chr(first_byte) + _encode_number(0) + _encode_number(0))
+    return block
+
+
+def _parse_request_text(request_text):
+    request_line, header_lines = request_text.split('\r\n', 1)
+
+    words = request_line.split(' ')
+    if len(words) != 3:
+        raise ValueError('Bad Request-Line syntax %r' % request_line)
+    [command, path, version] = words
+    if version != 'HTTP/1.1':
+        raise ValueError('Bad request version %r' % version)
+
+    # email.parser.Parser() parses RFC 2822 (RFC 822) style headers.
+    # RFC 6455 refers RFC 2616 for handshake parsing, and RFC 2616 refers
+    # RFC 822.
+    headers = email.parser.Parser().parsestr(header_lines)
+    return command, path, version, headers
+
+
+class _ControlBlock(object):
+    """A structure that holds parsing result of multiplexing control block.
+    Control block specific attributes will be added by _MuxFramePayloadParser.
+    (e.g. encoded_handshake will be added for AddChannelRequest and
+    AddChannelResponse)
+    """
+
+    def __init__(self, opcode):
+        self.opcode = opcode
+
+
+class _MuxFramePayloadParser(object):
+    """A class that parses multiplexed frame payload."""
+
+    def __init__(self, payload):
+        self._data = payload
+        self._read_position = 0
+        self._logger = util.get_class_logger(self)
+
+    def read_channel_id(self):
+        """Reads channel id.
+
+        Raises:
+            ValueError: when the payload doesn't contain
+                valid channel id.
+        """
+
+        remaining_length = len(self._data) - self._read_position
+        pos = self._read_position
+        if remaining_length == 0:
+            raise ValueError('Invalid channel id format')
+
+        channel_id = ord(self._data[pos])
+        channel_id_length = 1
+        if channel_id & 0xe0 == 0xe0:
+            if remaining_length < 4:
+                raise ValueError('Invalid channel id format')
+            channel_id = struct.unpack('!L',
+                                       self._data[pos:pos+4])[0] & 0x1fffffff
+            channel_id_length = 4
+        elif channel_id & 0xc0 == 0xc0:
+            if remaining_length < 3:
+                raise ValueError('Invalid channel id format')
+            channel_id = (((channel_id & 0x1f) << 16) +
+                          struct.unpack('!H', self._data[pos+1:pos+3])[0])
+            channel_id_length = 3
+        elif channel_id & 0x80 == 0x80:
+            if remaining_length < 2:
+                raise ValueError('Invalid channel id format')
+            channel_id = struct.unpack('!H',
+                                       self._data[pos:pos+2])[0] & 0x3fff
+            channel_id_length = 2
+        self._read_position += channel_id_length
+
+        return channel_id
+
+    def read_inner_frame(self):
+        """Reads an inner frame.
+
+        Raises:
+            PhysicalConnectionError: when the inner frame is invalid.
+        """
+
+        if len(self._data) == self._read_position:
+            raise PhysicalConnectionError(
+                _DROP_CODE_ENCAPSULATED_FRAME_IS_TRUNCATED)
+
+        bits = ord(self._data[self._read_position])
+        self._read_position += 1
+        fin = (bits & 0x80) == 0x80
+        rsv1 = (bits & 0x40) == 0x40
+        rsv2 = (bits & 0x20) == 0x20
+        rsv3 = (bits & 0x10) == 0x10
+        opcode = bits & 0xf
+        payload = self.remaining_data()
+        # Consume rest of the message which is payload data of the original
+        # frame.
+        self._read_position = len(self._data)
+        return fin, rsv1, rsv2, rsv3, opcode, payload
+
+    def _read_number(self):
+        if self._read_position + 1 > len(self._data):
+            raise ValueError(
+                'Cannot read the first byte of number field')
+
+        number = ord(self._data[self._read_position])
+        if number & 0x80 == 0x80:
+            raise ValueError(
+                'The most significant bit of the first byte of number should '
+                'be unset')
+        self._read_position += 1
+        pos = self._read_position
+        if number == 127:
+            if pos + 8 > len(self._data):
+                raise ValueError('Invalid number field')
+            self._read_position += 8
+            number = struct.unpack('!Q', self._data[pos:pos+8])[0]
+            if number > 0x7FFFFFFFFFFFFFFF:
+                raise ValueError('Encoded number(%d) >= 2^63' % number)
+            if number <= 0xFFFF:
+                raise ValueError(
+                    '%d should not be encoded by 9 bytes encoding' % number)
+            return number
+        if number == 126:
+            if pos + 2 > len(self._data):
+                raise ValueError('Invalid number field')
+            self._read_position += 2
+            number = struct.unpack('!H', self._data[pos:pos+2])[0]
+            if number <= 125:
+                raise ValueError(
+                    '%d should not be encoded by 3 bytes encoding' % number)
+        return number
+
+    def _read_size_and_contents(self):
+        """Reads data that consists of followings:
+            - the size of the contents encoded the same way as payload length
+              of the WebSocket Protocol with 1 bit padding at the head.
+            - the contents.
+        """
+
+        try:
+            size = self._read_number()
+        except ValueError, e:
+            raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                                          str(e))
+        pos = self._read_position
+        if pos + size > len(self._data):
+            raise PhysicalConnectionError(
+                _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                'Cannot read %d bytes data' % size)
+
+        self._read_position += size
+        return self._data[pos:pos+size]
+
+    def _read_add_channel_request(self, first_byte, control_block):
+        reserved = (first_byte >> 2) & 0x7
+        if reserved != 0:
+            raise PhysicalConnectionError(
+                _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                'Reserved bits must be unset')
+
+        # Invalid encoding will be handled by MuxHandler.
+        encoding = first_byte & 0x3
+        try:
+            control_block.channel_id = self.read_channel_id()
+        except ValueError, e:
+            raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK)
+        control_block.encoding = encoding
+        encoded_handshake = self._read_size_and_contents()
+        control_block.encoded_handshake = encoded_handshake
+        return control_block
+
+    def _read_add_channel_response(self, first_byte, control_block):
+        reserved = (first_byte >> 2) & 0x3
+        if reserved != 0:
+            raise PhysicalConnectionError(
+                _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                'Reserved bits must be unset')
+
+        control_block.accepted = (first_byte >> 4) & 1
+        control_block.encoding = first_byte & 0x3
+        try:
+            control_block.channel_id = self.read_channel_id()
+        except ValueError, e:
+            raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK)
+        control_block.encoded_handshake = self._read_size_and_contents()
+        return control_block
+
+    def _read_flow_control(self, first_byte, control_block):
+        reserved = first_byte & 0x1f
+        if reserved != 0:
+            raise PhysicalConnectionError(
+                _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                'Reserved bits must be unset')
+
+        try:
+            control_block.channel_id = self.read_channel_id()
+            control_block.send_quota = self._read_number()
+        except ValueError, e:
+            raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                                          str(e))
+
+        return control_block
+
+    def _read_drop_channel(self, first_byte, control_block):
+        reserved = first_byte & 0x1f
+        if reserved != 0:
+            raise PhysicalConnectionError(
+                _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                'Reserved bits must be unset')
+
+        try:
+            control_block.channel_id = self.read_channel_id()
+        except ValueError, e:
+            raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK)
+        reason = self._read_size_and_contents()
+        if len(reason) == 0:
+            control_block.drop_code = None
+            control_block.drop_message = ''
+        elif len(reason) >= 2:
+            control_block.drop_code = struct.unpack('!H', reason[:2])[0]
+            control_block.drop_message = reason[2:]
+        else:
+            raise PhysicalConnectionError(
+                _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                'Received DropChannel that conains only 1-byte reason')
+        return control_block
+
+    def _read_new_channel_slot(self, first_byte, control_block):
+        reserved = first_byte & 0x1e
+        if reserved != 0:
+            raise PhysicalConnectionError(
+                _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                'Reserved bits must be unset')
+        control_block.fallback = first_byte & 1
+        try:
+            control_block.slots = self._read_number()
+            control_block.send_quota = self._read_number()
+        except ValueError, e:
+            raise PhysicalConnectionError(_DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                                          str(e))
+        return control_block
+
+    def read_control_blocks(self):
+        """Reads control block(s).
+
+        Raises:
+           PhysicalConnectionError: when the payload contains invalid control
+               block(s).
+           StopIteration: when no control blocks left.
+        """
+
+        while self._read_position < len(self._data):
+            first_byte = ord(self._data[self._read_position])
+            self._read_position += 1
+            opcode = (first_byte >> 5) & 0x7
+            control_block = _ControlBlock(opcode=opcode)
+            if opcode == _MUX_OPCODE_ADD_CHANNEL_REQUEST:
+                yield self._read_add_channel_request(first_byte, control_block)
+            elif opcode == _MUX_OPCODE_ADD_CHANNEL_RESPONSE:
+                yield self._read_add_channel_response(
+                    first_byte, control_block)
+            elif opcode == _MUX_OPCODE_FLOW_CONTROL:
+                yield self._read_flow_control(first_byte, control_block)
+            elif opcode == _MUX_OPCODE_DROP_CHANNEL:
+                yield self._read_drop_channel(first_byte, control_block)
+            elif opcode == _MUX_OPCODE_NEW_CHANNEL_SLOT:
+                yield self._read_new_channel_slot(first_byte, control_block)
+            else:
+                raise PhysicalConnectionError(
+                    _DROP_CODE_UNKNOWN_MUX_OPCODE,
+                    'Invalid opcode %d' % opcode)
+
+        assert self._read_position == len(self._data)
+        raise StopIteration
+
+    def remaining_data(self):
+        """Returns remaining data."""
+
+        return self._data[self._read_position:]
+
+
+class _LogicalRequest(object):
+    """Mimics mod_python request."""
+
+    def __init__(self, channel_id, command, path, protocol, headers,
+                 connection):
+        """Constructs an instance.
+
+        Args:
+            channel_id: the channel id of the logical channel.
+            command: HTTP request command.
+            path: HTTP request path.
+            headers: HTTP headers.
+            connection: _LogicalConnection instance.
+        """
+
+        self.channel_id = channel_id
+        self.method = command
+        self.uri = path
+        self.protocol = protocol
+        self.headers_in = headers
+        self.connection = connection
+        self.server_terminated = False
+        self.client_terminated = False
+
+    def is_https(self):
+        """Mimics request.is_https(). Returns False because this method is
+        used only by old protocols (hixie and hybi00).
+        """
+
+        return False
+
+
+class _LogicalConnection(object):
+    """Mimics mod_python mp_conn."""
+
+    # For details, see the comment of set_read_state().
+    STATE_ACTIVE = 1
+    STATE_GRACEFULLY_CLOSED = 2
+    STATE_TERMINATED = 3
+
+    def __init__(self, mux_handler, channel_id):
+        """Constructs an instance.
+
+        Args:
+            mux_handler: _MuxHandler instance.
+            channel_id: channel id of this connection.
+        """
+
+        self._mux_handler = mux_handler
+        self._channel_id = channel_id
+        self._incoming_data = ''
+
+        # - Protects _waiting_write_completion
+        # - Signals the thread waiting for completion of write by mux handler
+        self._write_condition = threading.Condition()
+        self._waiting_write_completion = False
+
+        self._read_condition = threading.Condition()
+        self._read_state = self.STATE_ACTIVE
+
+    def get_local_addr(self):
+        """Getter to mimic mp_conn.local_addr."""
+
+        return self._mux_handler.physical_connection.get_local_addr()
+    local_addr = property(get_local_addr)
+
+    def get_remote_addr(self):
+        """Getter to mimic mp_conn.remote_addr."""
+
+        return self._mux_handler.physical_connection.get_remote_addr()
+    remote_addr = property(get_remote_addr)
+
+    def get_memorized_lines(self):
+        """Gets memorized lines. Not supported."""
+
+        raise MuxUnexpectedException('_LogicalConnection does not support '
+                                     'get_memorized_lines')
+
+    def write(self, data):
+        """Writes data. mux_handler sends data asynchronously. The caller will
+        be suspended until write done.
+
+        Args:
+            data: data to be written.
+
+        Raises:
+            MuxUnexpectedException: when called before finishing the previous
+                write.
+        """
+
+        try:
+            self._write_condition.acquire()
+            if self._waiting_write_completion:
+                raise MuxUnexpectedException(
+                    'Logical connection %d is already waiting the completion '
+                    'of write' % self._channel_id)
+
+            self._waiting_write_completion = True
+            self._mux_handler.send_data(self._channel_id, data)
+            self._write_condition.wait()
+            # TODO(tyoshino): Raise an exception if woke up by on_writer_done.
+        finally:
+            self._write_condition.release()
+
+    def write_control_data(self, data):
+        """Writes data via the control channel. Don't wait finishing write
+        because this method can be called by mux dispatcher.
+
+        Args:
+            data: data to be written.
+        """
+
+        self._mux_handler.send_control_data(data)
+
+    def on_write_data_done(self):
+        """Called when sending data is completed."""
+
+        try:
+            self._write_condition.acquire()
+            if not self._waiting_write_completion:
+                raise MuxUnexpectedException(
+                    'Invalid call of on_write_data_done for logical '
+                    'connection %d' % self._channel_id)
+            self._waiting_write_completion = False
+            self._write_condition.notify()
+        finally:
+            self._write_condition.release()
+
+    def on_writer_done(self):
+        """Called by the mux handler when the writer thread has finished."""
+
+        try:
+            self._write_condition.acquire()
+            self._waiting_write_completion = False
+            self._write_condition.notify()
+        finally:
+            self._write_condition.release()
+
+
+    def append_frame_data(self, frame_data):
+        """Appends incoming frame data. Called when mux_handler dispatches
+        frame data to the corresponding application.
+
+        Args:
+            frame_data: incoming frame data.
+        """
+
+        self._read_condition.acquire()
+        self._incoming_data += frame_data
+        self._read_condition.notify()
+        self._read_condition.release()
+
+    def read(self, length):
+        """Reads data. Blocks until enough data has arrived via physical
+        connection.
+
+        Args:
+            length: length of data to be read.
+        Raises:
+            LogicalConnectionClosedException: when closing handshake for this
+                logical channel has been received.
+            ConnectionTerminatedException: when the physical connection has
+                closed, or an error is caused on the reader thread.
+        """
+
+        self._read_condition.acquire()
+        while (self._read_state == self.STATE_ACTIVE and
+               len(self._incoming_data) < length):
+            self._read_condition.wait()
+
+        try:
+            if self._read_state == self.STATE_GRACEFULLY_CLOSED:
+                raise LogicalConnectionClosedException(
+                    'Logical channel %d has closed.' % self._channel_id)
+            elif self._read_state == self.STATE_TERMINATED:
+                raise ConnectionTerminatedException(
+                    'Receiving %d byte failed. Logical channel (%d) closed' %
+                    (length, self._channel_id))
+
+            value = self._incoming_data[:length]
+            self._incoming_data = self._incoming_data[length:]
+        finally:
+            self._read_condition.release()
+
+        return value
+
+    def set_read_state(self, new_state):
+        """Sets the state of this connection. Called when an event for this
+        connection has occurred.
+
+        Args:
+            new_state: state to be set. new_state must be one of followings:
+            - STATE_GRACEFULLY_CLOSED: when closing handshake for this
+                connection has been received.
+            - STATE_TERMINATED: when the physical connection has closed or
+                DropChannel of this connection has received.
+        """
+
+        self._read_condition.acquire()
+        self._read_state = new_state
+        self._read_condition.notify()
+        self._read_condition.release()
+
+
+class _InnerMessage(object):
+    """Holds the result of _InnerMessageBuilder.build().
+    """
+
+    def __init__(self, opcode, payload):
+        self.opcode = opcode
+        self.payload = payload
+
+
+class _InnerMessageBuilder(object):
+    """A class that holds the context of inner message fragmentation and
+    builds a message from fragmented inner frame(s).
+    """
+
+    def __init__(self):
+        self._control_opcode = None
+        self._pending_control_fragments = []
+        self._message_opcode = None
+        self._pending_message_fragments = []
+        self._frame_handler = self._handle_first
+
+    def _handle_first(self, frame):
+        if frame.opcode == common.OPCODE_CONTINUATION:
+            raise InvalidFrameException('Sending invalid continuation opcode')
+
+        if common.is_control_opcode(frame.opcode):
+            return self._process_first_fragmented_control(frame)
+        else:
+            return self._process_first_fragmented_message(frame)
+
+    def _process_first_fragmented_control(self, frame):
+        self._control_opcode = frame.opcode
+        self._pending_control_fragments.append(frame.payload)
+        if not frame.fin:
+            self._frame_handler = self._handle_fragmented_control
+            return None
+        return self._reassemble_fragmented_control()
+
+    def _process_first_fragmented_message(self, frame):
+        self._message_opcode = frame.opcode
+        self._pending_message_fragments.append(frame.payload)
+        if not frame.fin:
+            self._frame_handler = self._handle_fragmented_message
+            return None
+        return self._reassemble_fragmented_message()
+
+    def _handle_fragmented_control(self, frame):
+        if frame.opcode != common.OPCODE_CONTINUATION:
+            raise InvalidFrameException(
+                'Sending invalid opcode %d while sending fragmented control '
+                'message' % frame.opcode)
+        self._pending_control_fragments.append(frame.payload)
+        if not frame.fin:
+            return None
+        return self._reassemble_fragmented_control()
+
+    def _reassemble_fragmented_control(self):
+        opcode = self._control_opcode
+        payload = ''.join(self._pending_control_fragments)
+        self._control_opcode = None
+        self._pending_control_fragments = []
+        if self._message_opcode is not None:
+            self._frame_handler = self._handle_fragmented_message
+        else:
+            self._frame_handler = self._handle_first
+        return _InnerMessage(opcode, payload)
+
+    def _handle_fragmented_message(self, frame):
+        # Sender can interleave a control message while sending fragmented
+        # messages.
+        if common.is_control_opcode(frame.opcode):
+            if self._control_opcode is not None:
+                raise MuxUnexpectedException(
+                    'Should not reach here(Bug in builder)')
+            return self._process_first_fragmented_control(frame)
+
+        if frame.opcode != common.OPCODE_CONTINUATION:
+            raise InvalidFrameException(
+                'Sending invalid opcode %d while sending fragmented message' %
+                frame.opcode)
+        self._pending_message_fragments.append(frame.payload)
+        if not frame.fin:
+            return None
+        return self._reassemble_fragmented_message()
+
+    def _reassemble_fragmented_message(self):
+        opcode = self._message_opcode
+        payload = ''.join(self._pending_message_fragments)
+        self._message_opcode = None
+        self._pending_message_fragments = []
+        self._frame_handler = self._handle_first
+        return _InnerMessage(opcode, payload)
+
+    def build(self, frame):
+        """Build an inner message. Returns an _InnerMessage instance when
+        the given frame is the last fragmented frame. Returns None otherwise.
+
+        Args:
+            frame: an inner frame.
+        Raises:
+            InvalidFrameException: when received invalid opcode. (e.g.
+                receiving non continuation data opcode but the fin flag of
+                the previous inner frame was not set.)
+        """
+
+        return self._frame_handler(frame)
+
+
+class _LogicalStream(Stream):
+    """Mimics the Stream class. This class interprets multiplexed WebSocket
+    frames.
+    """
+
+    def __init__(self, request, stream_options, send_quota, receive_quota):
+        """Constructs an instance.
+
+        Args:
+            request: _LogicalRequest instance.
+            stream_options: StreamOptions instance.
+            send_quota: Initial send quota.
+            receive_quota: Initial receive quota.
+        """
+
+        # Physical stream is responsible for masking.
+        stream_options.unmask_receive = False
+        Stream.__init__(self, request, stream_options)
+
+        self._send_closed = False
+        self._send_quota = send_quota
+        # - Protects _send_closed and _send_quota
+        # - Signals the thread waiting for send quota replenished
+        self._send_condition = threading.Condition()
+
+        # The opcode of the first frame in messages.
+        self._message_opcode = common.OPCODE_TEXT
+        # True when the last message was fragmented.
+        self._last_message_was_fragmented = False
+
+        self._receive_quota = receive_quota
+        self._write_inner_frame_semaphore = threading.Semaphore()
+
+        self._inner_message_builder = _InnerMessageBuilder()
+
+    def _create_inner_frame(self, opcode, payload, end=True):
+        frame = Frame(fin=end, opcode=opcode, payload=payload)
+        for frame_filter in self._options.outgoing_frame_filters:
+            frame_filter.filter(frame)
+
+        if len(payload) != len(frame.payload):
+            raise MuxUnexpectedException(
+                'Mux extension must not be used after extensions which change '
+                ' frame boundary')
+
+        first_byte = ((frame.fin << 7) | (frame.rsv1 << 6) |
+                      (frame.rsv2 << 5) | (frame.rsv3 << 4) | frame.opcode)
+        return chr(first_byte) + frame.payload
+
+    def _write_inner_frame(self, opcode, payload, end=True):
+        payload_length = len(payload)
+        write_position = 0
+
+        try:
+            # An inner frame will be fragmented if there is no enough send
+            # quota. This semaphore ensures that fragmented inner frames are
+            # sent in order on the logical channel.
+            # Note that frames that come from other logical channels or
+            # multiplexing control blocks can be inserted between fragmented
+            # inner frames on the physical channel.
+            self._write_inner_frame_semaphore.acquire()
+
+            # Consume an octet quota when this is the first fragmented frame.
+            if opcode != common.OPCODE_CONTINUATION:
+                try:
+                    self._send_condition.acquire()
+                    while (not self._send_closed) and self._send_quota == 0:
+                        self._send_condition.wait()
+
+                    if self._send_closed:
+                        raise BadOperationException(
+                            'Logical connection %d is closed' %
+                            self._request.channel_id)
+
+                    self._send_quota -= 1
+                finally:
+                    self._send_condition.release()
+
+            while write_position < payload_length:
+                try:
+                    self._send_condition.acquire()
+                    while (not self._send_closed) and self._send_quota == 0:
+                        self._logger.debug(
+                            'No quota. Waiting FlowControl message for %d.' %
+                            self._request.channel_id)
+                        self._send_condition.wait()
+
+                    if self._send_closed:
+                        raise BadOperationException(
+                            'Logical connection %d is closed' %
+                            self.request._channel_id)
+
+                    remaining = payload_length - write_position
+                    write_length = min(self._send_quota, remaining)
+                    inner_frame_end = (
+                        end and
+                        (write_position + write_length == payload_length))
+
+                    inner_frame = self._create_inner_frame(
+                        opcode,
+                        payload[write_position:write_position+write_length],
+                        inner_frame_end)
+                    self._send_quota -= write_length
+                    self._logger.debug('Consumed quota=%d, remaining=%d' %
+                                       (write_length, self._send_quota))
+                finally:
+                    self._send_condition.release()
+
+                # Writing data will block the worker so we need to release
+                # _send_condition before writing.
+                self._logger.debug('Sending inner frame: %r' % inner_frame)
+                self._request.connection.write(inner_frame)
+                write_position += write_length
+
+                opcode = common.OPCODE_CONTINUATION
+
+        except ValueError, e:
+            raise BadOperationException(e)
+        finally:
+            self._write_inner_frame_semaphore.release()
+
+    def replenish_send_quota(self, send_quota):
+        """Replenish send quota."""
+
+        try:
+            self._send_condition.acquire()
+            if self._send_quota + send_quota > 0x7FFFFFFFFFFFFFFF:
+                self._send_quota = 0
+                raise LogicalChannelError(
+                    self._request.channel_id, _DROP_CODE_SEND_QUOTA_OVERFLOW)
+            self._send_quota += send_quota
+            self._logger.debug('Replenished send quota for channel id %d: %d' %
+                               (self._request.channel_id, self._send_quota))
+        finally:
+            self._send_condition.notify()
+            self._send_condition.release()
+
+    def consume_receive_quota(self, amount):
+        """Consumes receive quota. Returns False on failure."""
+
+        if self._receive_quota < amount:
+            self._logger.debug('Violate quota on channel id %d: %d < %d' %
+                               (self._request.channel_id,
+                                self._receive_quota, amount))
+            return False
+        self._receive_quota -= amount
+        return True
+
+    def send_message(self, message, end=True, binary=False):
+        """Override Stream.send_message."""
+
+        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')
+
+        if binary:
+            opcode = common.OPCODE_BINARY
+        else:
+            opcode = common.OPCODE_TEXT
+            message = message.encode('utf-8')
+
+        for message_filter in self._options.outgoing_message_filters:
+            message = message_filter.filter(message, end, binary)
+
+        if self._last_message_was_fragmented:
+            if opcode != self._message_opcode:
+                raise BadOperationException('Message types are different in '
+                                            'frames for the same message')
+            opcode = common.OPCODE_CONTINUATION
+        else:
+            self._message_opcode = opcode
+
+        self._write_inner_frame(opcode, message, end)
+        self._last_message_was_fragmented = not end
+
+    def _receive_frame(self):
+        """Overrides Stream._receive_frame.
+
+        In addition to call Stream._receive_frame, this method adds the amount
+        of payload to receiving quota and sends FlowControl to the client.
+        We need to do it here because Stream.receive_message() handles
+        control frames internally.
+        """
+
+        opcode, payload, fin, rsv1, rsv2, rsv3 = Stream._receive_frame(self)
+        amount = len(payload)
+        # Replenish extra one octet when receiving the first fragmented frame.
+        if opcode != common.OPCODE_CONTINUATION:
+            amount += 1
+        self._receive_quota += amount
+        frame_data = _create_flow_control(self._request.channel_id,
+                                          amount)
+        self._logger.debug('Sending flow control for %d, replenished=%d' %
+                           (self._request.channel_id, amount))
+        self._request.connection.write_control_data(frame_data)
+        return opcode, payload, fin, rsv1, rsv2, rsv3
+
+    def _get_message_from_frame(self, frame):
+        """Overrides Stream._get_message_from_frame.
+        """
+
+        try:
+            inner_message = self._inner_message_builder.build(frame)
+        except InvalidFrameException:
+            raise LogicalChannelError(
+                self._request.channel_id, _DROP_CODE_BAD_FRAGMENTATION)
+
+        if inner_message is None:
+            return None
+        self._original_opcode = inner_message.opcode
+        return inner_message.payload
+
+    def receive_message(self):
+        """Overrides Stream.receive_message."""
+
+        # Just call Stream.receive_message(), but catch
+        # LogicalConnectionClosedException, which is raised when the logical
+        # connection has closed gracefully.
+        try:
+            return Stream.receive_message(self)
+        except LogicalConnectionClosedException, e:
+            self._logger.debug('%s', e)
+            return None
+
+    def _send_closing_handshake(self, code, reason):
+        """Overrides Stream._send_closing_handshake."""
+
+        body = create_closing_handshake_body(code, reason)
+        self._logger.debug('Sending closing handshake for %d: (%r, %r)' %
+                           (self._request.channel_id, code, reason))
+        self._write_inner_frame(common.OPCODE_CLOSE, body, end=True)
+
+        self._request.server_terminated = True
+
+    def send_ping(self, body=''):
+        """Overrides Stream.send_ping"""
+
+        self._logger.debug('Sending ping on logical channel %d: %r' %
+                           (self._request.channel_id, body))
+        self._write_inner_frame(common.OPCODE_PING, body, end=True)
+
+        self._ping_queue.append(body)
+
+    def _send_pong(self, body):
+        """Overrides Stream._send_pong"""
+
+        self._logger.debug('Sending pong on logical channel %d: %r' %
+                           (self._request.channel_id, body))
+        self._write_inner_frame(common.OPCODE_PONG, body, end=True)
+
+    def close_connection(self, code=common.STATUS_NORMAL_CLOSURE, reason=''):
+        """Overrides Stream.close_connection."""
+
+        # TODO(bashi): Implement
+        self._logger.debug('Closing logical connection %d' %
+                           self._request.channel_id)
+        self._request.server_terminated = True
+
+    def stop_sending(self):
+        """Stops accepting new send operation (_write_inner_frame)."""
+
+        self._send_condition.acquire()
+        self._send_closed = True
+        self._send_condition.notify()
+        self._send_condition.release()
+
+
+class _OutgoingData(object):
+    """A structure that holds data to be sent via physical connection and
+    origin of the data.
+    """
+
+    def __init__(self, channel_id, data):
+        self.channel_id = channel_id
+        self.data = data
+
+
+class _PhysicalConnectionWriter(threading.Thread):
+    """A thread that is responsible for writing data to physical connection.
+
+    TODO(bashi): Make sure there is no thread-safety problem when the reader
+    thread reads data from the same socket at a time.
+    """
+
+    def __init__(self, mux_handler):
+        """Constructs an instance.
+
+        Args:
+            mux_handler: _MuxHandler instance.
+        """
+
+        threading.Thread.__init__(self)
+        self._logger = util.get_class_logger(self)
+        self._mux_handler = mux_handler
+        self.setDaemon(True)
+
+        # When set, make this thread stop accepting new data, flush pending
+        # data and exit.
+        self._stop_requested = False
+        # The close code of the physical connection.
+        self._close_code = common.STATUS_NORMAL_CLOSURE
+        # Deque for passing write data. It's protected by _deque_condition
+        # until _stop_requested is set.
+        self._deque = collections.deque()
+        # - Protects _deque, _stop_requested and _close_code
+        # - Signals threads waiting for them to be available
+        self._deque_condition = threading.Condition()
+
+    def put_outgoing_data(self, data):
+        """Puts outgoing data.
+
+        Args:
+            data: _OutgoingData instance.
+
+        Raises:
+            BadOperationException: when the thread has been requested to
+                terminate.
+        """
+
+        try:
+            self._deque_condition.acquire()
+            if self._stop_requested:
+                raise BadOperationException('Cannot write data anymore')
+
+            self._deque.append(data)
+            self._deque_condition.notify()
+        finally:
+            self._deque_condition.release()
+
+    def _write_data(self, outgoing_data):
+        message = (_encode_channel_id(outgoing_data.channel_id) +
+                   outgoing_data.data)
+        try:
+            self._mux_handler.physical_stream.send_message(
+                message=message, end=True, binary=True)
+        except Exception, e:
+            util.prepend_message_to_exception(
+                'Failed to send message to %r: ' %
+                (self._mux_handler.physical_connection.remote_addr,), e)
+            raise
+
+        # TODO(bashi): It would be better to block the thread that sends
+        # control data as well.
+        if outgoing_data.channel_id != _CONTROL_CHANNEL_ID:
+            self._mux_handler.notify_write_data_done(outgoing_data.channel_id)
+
+    def run(self):
+        try:
+            self._deque_condition.acquire()
+            while not self._stop_requested:
+                if len(self._deque) == 0:
+                    self._deque_condition.wait()
+                    continue
+
+                outgoing_data = self._deque.popleft()
+
+                self._deque_condition.release()
+                self._write_data(outgoing_data)
+                self._deque_condition.acquire()
+
+            # Flush deque.
+            #
+            # At this point, self._deque_condition is always acquired.
+            try:
+                while len(self._deque) > 0:
+                    outgoing_data = self._deque.popleft()
+                    self._write_data(outgoing_data)
+            finally:
+                self._deque_condition.release()
+
+            # Close physical connection.
+            try:
+                # Don't wait the response here. The response will be read
+                # by the reader thread.
+                self._mux_handler.physical_stream.close_connection(
+                    self._close_code, wait_response=False)
+            except Exception, e:
+                util.prepend_message_to_exception(
+                    'Failed to close the physical connection: %r' % e)
+                raise
+        finally:
+            self._mux_handler.notify_writer_done()
+
+    def stop(self, close_code=common.STATUS_NORMAL_CLOSURE):
+        """Stops the writer thread."""
+
+        self._deque_condition.acquire()
+        self._stop_requested = True
+        self._close_code = close_code
+        self._deque_condition.notify()
+        self._deque_condition.release()
+
+
+class _PhysicalConnectionReader(threading.Thread):
+    """A thread that is responsible for reading data from physical connection.
+    """
+
+    def __init__(self, mux_handler):
+        """Constructs an instance.
+
+        Args:
+            mux_handler: _MuxHandler instance.
+        """
+
+        threading.Thread.__init__(self)
+        self._logger = util.get_class_logger(self)
+        self._mux_handler = mux_handler
+        self.setDaemon(True)
+
+    def run(self):
+        while True:
+            try:
+                physical_stream = self._mux_handler.physical_stream
+                message = physical_stream.receive_message()
+                if message is None:
+                    break
+                # Below happens only when a data message is received.
+                opcode = physical_stream.get_last_received_opcode()
+                if opcode != common.OPCODE_BINARY:
+                    self._mux_handler.fail_physical_connection(
+                        _DROP_CODE_INVALID_ENCAPSULATING_MESSAGE,
+                        'Received a text message on physical connection')
+                    break
+
+            except ConnectionTerminatedException, e:
+                self._logger.debug('%s', e)
+                break
+
+            try:
+                self._mux_handler.dispatch_message(message)
+            except PhysicalConnectionError, e:
+                self._mux_handler.fail_physical_connection(
+                    e.drop_code, e.message)
+                break
+            except LogicalChannelError, e:
+                self._mux_handler.fail_logical_channel(
+                    e.channel_id, e.drop_code, e.message)
+            except Exception, e:
+                self._logger.debug(traceback.format_exc())
+                break
+
+        self._mux_handler.notify_reader_done()
+
+
+class _Worker(threading.Thread):
+    """A thread that is responsible for running the corresponding application
+    handler.
+    """
+
+    def __init__(self, mux_handler, request):
+        """Constructs an instance.
+
+        Args:
+            mux_handler: _MuxHandler instance.
+            request: _LogicalRequest instance.
+        """
+
+        threading.Thread.__init__(self)
+        self._logger = util.get_class_logger(self)
+        self._mux_handler = mux_handler
+        self._request = request
+        self.setDaemon(True)
+
+    def run(self):
+        self._logger.debug('Logical channel worker started. (id=%d)' %
+                           self._request.channel_id)
+        try:
+            # Non-critical exceptions will be handled by dispatcher.
+            self._mux_handler.dispatcher.transfer_data(self._request)
+        except LogicalChannelError, e:
+            self._mux_handler.fail_logical_channel(
+                e.channel_id, e.drop_code, e.message)
+        finally:
+            self._mux_handler.notify_worker_done(self._request.channel_id)
+
+
+class _MuxHandshaker(hybi.Handshaker):
+    """Opening handshake processor for multiplexing."""
+
+    _DUMMY_WEBSOCKET_KEY = 'dGhlIHNhbXBsZSBub25jZQ=='
+
+    def __init__(self, request, dispatcher, send_quota, receive_quota):
+        """Constructs an instance.
+        Args:
+            request: _LogicalRequest instance.
+            dispatcher: Dispatcher instance (dispatch.Dispatcher).
+            send_quota: Initial send quota.
+            receive_quota: Initial receive quota.
+        """
+
+        hybi.Handshaker.__init__(self, request, dispatcher)
+        self._send_quota = send_quota
+        self._receive_quota = receive_quota
+
+        # Append headers which should not be included in handshake field of
+        # AddChannelRequest.
+        # TODO(bashi): Make sure whether we should raise exception when
+        #     these headers are included already.
+        request.headers_in[common.UPGRADE_HEADER] = (
+            common.WEBSOCKET_UPGRADE_TYPE)
+        request.headers_in[common.SEC_WEBSOCKET_VERSION_HEADER] = (
+            str(common.VERSION_HYBI_LATEST))
+        request.headers_in[common.SEC_WEBSOCKET_KEY_HEADER] = (
+            self._DUMMY_WEBSOCKET_KEY)
+
+    def _create_stream(self, stream_options):
+        """Override hybi.Handshaker._create_stream."""
+
+        self._logger.debug('Creating logical stream for %d' %
+                           self._request.channel_id)
+        return _LogicalStream(
+            self._request, stream_options, self._send_quota,
+            self._receive_quota)
+
+    def _create_handshake_response(self, accept):
+        """Override hybi._create_handshake_response."""
+
+        response = []
+
+        response.append('HTTP/1.1 101 Switching Protocols\r\n')
+
+        # Upgrade and Sec-WebSocket-Accept should be excluded.
+        response.append('%s: %s\r\n' % (
+            common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE))
+        if self._request.ws_protocol is not None:
+            response.append('%s: %s\r\n' % (
+                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('%s: %s\r\n' % (
+                common.SEC_WEBSOCKET_EXTENSIONS_HEADER,
+                common.format_extensions(self._request.ws_extensions)))
+        response.append('\r\n')
+
+        return ''.join(response)
+
+    def _send_handshake(self, accept):
+        """Override hybi.Handshaker._send_handshake."""
+
+        # Don't send handshake response for the default channel
+        if self._request.channel_id == _DEFAULT_CHANNEL_ID:
+            return
+
+        handshake_response = self._create_handshake_response(accept)
+        frame_data = _create_add_channel_response(
+                         self._request.channel_id,
+                         handshake_response)
+        self._logger.debug('Sending handshake response for %d: %r' %
+                           (self._request.channel_id, frame_data))
+        self._request.connection.write_control_data(frame_data)
+
+
+class _LogicalChannelData(object):
+    """A structure that holds information about logical channel.
+    """
+
+    def __init__(self, request, worker):
+        self.request = request
+        self.worker = worker
+        self.drop_code = _DROP_CODE_NORMAL_CLOSURE
+        self.drop_message = ''
+
+
+class _HandshakeDeltaBase(object):
+    """A class that holds information for delta-encoded handshake."""
+
+    def __init__(self, headers):
+        self._headers = headers
+
+    def create_headers(self, delta=None):
+        """Creates request headers for an AddChannelRequest that has
+        delta-encoded handshake.
+
+        Args:
+            delta: headers should be overridden.
+        """
+
+        headers = copy.copy(self._headers)
+        if delta:
+            for key, value in delta.items():
+                # The spec requires that a header with an empty value is
+                # removed from the delta base.
+                if len(value) == 0 and headers.has_key(key):
+                    del headers[key]
+                else:
+                    headers[key] = value
+        return headers
+
+
+class _MuxHandler(object):
+    """Multiplexing handler. When a handler starts, it launches three
+    threads; the reader thread, the writer thread, and a worker thread.
+
+    The reader thread reads data from the physical stream, i.e., the
+    ws_stream object of the underlying websocket connection. The reader
+    thread interprets multiplexed frames and dispatches them to logical
+    channels. Methods of this class are mostly called by the reader thread.
+
+    The writer thread sends multiplexed frames which are created by
+    logical channels via the physical connection.
+
+    The worker thread launched at the starting point handles the
+    "Implicitly Opened Connection". If multiplexing handler receives
+    an AddChannelRequest and accepts it, the handler will launch a new worker
+    thread and dispatch the request to it.
+    """
+
+    def __init__(self, request, dispatcher):
+        """Constructs an instance.
+
+        Args:
+            request: mod_python request of the physical connection.
+            dispatcher: Dispatcher instance (dispatch.Dispatcher).
+        """
+
+        self.original_request = request
+        self.dispatcher = dispatcher
+        self.physical_connection = request.connection
+        self.physical_stream = request.ws_stream
+        self._logger = util.get_class_logger(self)
+        self._logical_channels = {}
+        self._logical_channels_condition = threading.Condition()
+        # Holds client's initial quota
+        self._channel_slots = collections.deque()
+        self._handshake_base = None
+        self._worker_done_notify_received = False
+        self._reader = None
+        self._writer = None
+
+    def start(self):
+        """Starts the handler.
+
+        Raises:
+            MuxUnexpectedException: when the handler already started, or when
+                opening handshake of the default channel fails.
+        """
+
+        if self._reader or self._writer:
+            raise MuxUnexpectedException('MuxHandler already started')
+
+        self._reader = _PhysicalConnectionReader(self)
+        self._writer = _PhysicalConnectionWriter(self)
+        self._reader.start()
+        self._writer.start()
+
+        # Create "Implicitly Opened Connection".
+        logical_connection = _LogicalConnection(self, _DEFAULT_CHANNEL_ID)
+        headers = copy.copy(self.original_request.headers_in)
+        # Add extensions for logical channel.
+        headers[common.SEC_WEBSOCKET_EXTENSIONS_HEADER] = (
+            common.format_extensions(
+                self.original_request.mux_processor.extensions()))
+        self._handshake_base = _HandshakeDeltaBase(headers)
+        logical_request = _LogicalRequest(
+            _DEFAULT_CHANNEL_ID,
+            self.original_request.method,
+            self.original_request.uri,
+            self.original_request.protocol,
+            self._handshake_base.create_headers(),
+            logical_connection)
+        # Client's send quota for the implicitly opened connection is zero,
+        # but we will send FlowControl later so set the initial quota to
+        # _INITIAL_QUOTA_FOR_CLIENT.
+        self._channel_slots.append(_INITIAL_QUOTA_FOR_CLIENT)
+        send_quota = self.original_request.mux_processor.quota()
+        if not self._do_handshake_for_logical_request(
+            logical_request, send_quota=send_quota):
+            raise MuxUnexpectedException(
+                'Failed handshake on the default channel id')
+        self._add_logical_channel(logical_request)
+
+        # Send FlowControl for the implicitly opened connection.
+        frame_data = _create_flow_control(_DEFAULT_CHANNEL_ID,
+                                          _INITIAL_QUOTA_FOR_CLIENT)
+        logical_request.connection.write_control_data(frame_data)
+
+    def add_channel_slots(self, slots, send_quota):
+        """Adds channel slots.
+
+        Args:
+            slots: number of slots to be added.
+            send_quota: initial send quota for slots.
+        """
+
+        self._channel_slots.extend([send_quota] * slots)
+        # Send NewChannelSlot to client.
+        frame_data = _create_new_channel_slot(slots, send_quota)
+        self.send_control_data(frame_data)
+
+    def wait_until_done(self, timeout=None):
+        """Waits until all workers are done. Returns False when timeout has
+        occurred. Returns True on success.
+
+        Args:
+            timeout: timeout in sec.
+        """
+
+        self._logical_channels_condition.acquire()
+        try:
+            while len(self._logical_channels) > 0:
+                self._logger.debug('Waiting workers(%d)...' %
+                                   len(self._logical_channels))
+                self._worker_done_notify_received = False
+                self._logical_channels_condition.wait(timeout)
+                if not self._worker_done_notify_received:
+                    self._logger.debug('Waiting worker(s) timed out')
+                    return False
+        finally:
+            self._logical_channels_condition.release()
+
+        # Flush pending outgoing data
+        self._writer.stop()
+        self._writer.join()
+
+        return True
+
+    def notify_write_data_done(self, channel_id):
+        """Called by the writer thread when a write operation has done.
+
+        Args:
+            channel_id: objective channel id.
+        """
+
+        try:
+            self._logical_channels_condition.acquire()
+            if channel_id in self._logical_channels:
+                channel_data = self._logical_channels[channel_id]
+                channel_data.request.connection.on_write_data_done()
+            else:
+                self._logger.debug('Seems that logical channel for %d has gone'
+                                   % channel_id)
+        finally:
+            self._logical_channels_condition.release()
+
+    def send_control_data(self, data):
+        """Sends data via the control channel.
+
+        Args:
+            data: data to be sent.
+        """
+
+        self._writer.put_outgoing_data(_OutgoingData(
+                channel_id=_CONTROL_CHANNEL_ID, data=data))
+
+    def send_data(self, channel_id, data):
+        """Sends data via given logical channel. This method is called by
+        worker threads.
+
+        Args:
+            channel_id: objective channel id.
+            data: data to be sent.
+        """
+
+        self._writer.put_outgoing_data(_OutgoingData(
+                channel_id=channel_id, data=data))
+
+    def _send_drop_channel(self, channel_id, code=None, message=''):
+        frame_data = _create_drop_channel(channel_id, code, message)
+        self._logger.debug(
+            'Sending drop channel for channel id %d' % channel_id)
+        self.send_control_data(frame_data)
+
+    def _send_error_add_channel_response(self, channel_id, status=None):
+        if status is None:
+            status = common.HTTP_STATUS_BAD_REQUEST
+
+        if status in _HTTP_BAD_RESPONSE_MESSAGES:
+            message = _HTTP_BAD_RESPONSE_MESSAGES[status]
+        else:
+            self._logger.debug('Response message for %d is not found' % status)
+            message = '???'
+
+        response = 'HTTP/1.1 %d %s\r\n\r\n' % (status, message)
+        frame_data = _create_add_channel_response(channel_id,
+                                                  encoded_handshake=response,
+                                                  encoding=0, rejected=True)
+        self.send_control_data(frame_data)
+
+    def _create_logical_request(self, block):
+        if block.channel_id == _CONTROL_CHANNEL_ID:
+            # TODO(bashi): Raise PhysicalConnectionError with code 2006
+            # instead of MuxUnexpectedException.
+            raise MuxUnexpectedException(
+                'Received the control channel id (0) as objective channel '
+                'id for AddChannel')
+
+        if block.encoding > _HANDSHAKE_ENCODING_DELTA:
+            raise PhysicalConnectionError(
+                _DROP_CODE_UNKNOWN_REQUEST_ENCODING)
+
+        method, path, version, headers = _parse_request_text(
+            block.encoded_handshake)
+        if block.encoding == _HANDSHAKE_ENCODING_DELTA:
+            headers = self._handshake_base.create_headers(headers)
+
+        connection = _LogicalConnection(self, block.channel_id)
+        request = _LogicalRequest(block.channel_id, method, path, version,
+                                  headers, connection)
+        return request
+
+    def _do_handshake_for_logical_request(self, request, send_quota=0):
+        try:
+            receive_quota = self._channel_slots.popleft()
+        except IndexError:
+            raise LogicalChannelError(
+                request.channel_id, _DROP_CODE_NEW_CHANNEL_SLOT_VIOLATION)
+
+        handshaker = _MuxHandshaker(request, self.dispatcher,
+                                    send_quota, receive_quota)
+        try:
+            handshaker.do_handshake()
+        except handshake.VersionException, e:
+            self._logger.info('%s', e)
+            self._send_error_add_channel_response(
+                request.channel_id, status=common.HTTP_STATUS_BAD_REQUEST)
+            return False
+        except handshake.HandshakeException, e:
+            # TODO(bashi): Should we _Fail the Logical Channel_ with 3001
+            # instead?
+            self._logger.info('%s', e)
+            self._send_error_add_channel_response(request.channel_id,
+                                                  status=e.status)
+            return False
+        except handshake.AbortedByUserException, e:
+            self._logger.info('%s', e)
+            self._send_error_add_channel_response(request.channel_id)
+            return False
+
+        return True
+
+    def _add_logical_channel(self, logical_request):
+        try:
+            self._logical_channels_condition.acquire()
+            if logical_request.channel_id in self._logical_channels:
+                self._logger.debug('Channel id %d already exists' %
+                                   logical_request.channel_id)
+                raise PhysicalConnectionError(
+                    _DROP_CODE_CHANNEL_ALREADY_EXISTS,
+                    'Channel id %d already exists' %
+                    logical_request.channel_id)
+            worker = _Worker(self, logical_request)
+            channel_data = _LogicalChannelData(logical_request, worker)
+            self._logical_channels[logical_request.channel_id] = channel_data
+            worker.start()
+        finally:
+            self._logical_channels_condition.release()
+
+    def _process_add_channel_request(self, block):
+        try:
+            logical_request = self._create_logical_request(block)
+        except ValueError, e:
+            self._logger.debug('Failed to create logical request: %r' % e)
+            self._send_error_add_channel_response(
+                block.channel_id, status=common.HTTP_STATUS_BAD_REQUEST)
+            return
+        if self._do_handshake_for_logical_request(logical_request):
+            if block.encoding == _HANDSHAKE_ENCODING_IDENTITY:
+                # Update handshake base.
+                # TODO(bashi): Make sure this is the right place to update
+                # handshake base.
+                self._handshake_base = _HandshakeDeltaBase(
+                    logical_request.headers_in)
+            self._add_logical_channel(logical_request)
+        else:
+            self._send_error_add_channel_response(
+                block.channel_id, status=common.HTTP_STATUS_BAD_REQUEST)
+
+    def _process_flow_control(self, block):
+        try:
+            self._logical_channels_condition.acquire()
+            if not block.channel_id in self._logical_channels:
+                return
+            channel_data = self._logical_channels[block.channel_id]
+            channel_data.request.ws_stream.replenish_send_quota(
+                block.send_quota)
+        finally:
+            self._logical_channels_condition.release()
+
+    def _process_drop_channel(self, block):
+        self._logger.debug(
+            'DropChannel received for %d: code=%r, reason=%r' %
+            (block.channel_id, block.drop_code, block.drop_message))
+        try:
+            self._logical_channels_condition.acquire()
+            if not block.channel_id in self._logical_channels:
+                return
+            channel_data = self._logical_channels[block.channel_id]
+            channel_data.drop_code = _DROP_CODE_ACKNOWLEDGED
+
+            # Close the logical channel
+            channel_data.request.connection.set_read_state(
+                _LogicalConnection.STATE_TERMINATED)
+            channel_data.request.ws_stream.stop_sending()
+        finally:
+            self._logical_channels_condition.release()
+
+    def _process_control_blocks(self, parser):
+        for control_block in parser.read_control_blocks():
+            opcode = control_block.opcode
+            self._logger.debug('control block received, opcode: %d' % opcode)
+            if opcode == _MUX_OPCODE_ADD_CHANNEL_REQUEST:
+                self._process_add_channel_request(control_block)
+            elif opcode == _MUX_OPCODE_ADD_CHANNEL_RESPONSE:
+                raise PhysicalConnectionError(
+                    _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                    'Received AddChannelResponse')
+            elif opcode == _MUX_OPCODE_FLOW_CONTROL:
+                self._process_flow_control(control_block)
+            elif opcode == _MUX_OPCODE_DROP_CHANNEL:
+                self._process_drop_channel(control_block)
+            elif opcode == _MUX_OPCODE_NEW_CHANNEL_SLOT:
+                raise PhysicalConnectionError(
+                    _DROP_CODE_INVALID_MUX_CONTROL_BLOCK,
+                    'Received NewChannelSlot')
+            else:
+                raise MuxUnexpectedException(
+                    'Unexpected opcode %r' % opcode)
+
+    def _process_logical_frame(self, channel_id, parser):
+        self._logger.debug('Received a frame. channel id=%d' % channel_id)
+        try:
+            self._logical_channels_condition.acquire()
+            if not channel_id in self._logical_channels:
+                # We must ignore the message for an inactive channel.
+                return
+            channel_data = self._logical_channels[channel_id]
+            fin, rsv1, rsv2, rsv3, opcode, payload = parser.read_inner_frame()
+            consuming_byte = len(payload)
+            if opcode != common.OPCODE_CONTINUATION:
+                consuming_byte += 1
+            if not channel_data.request.ws_stream.consume_receive_quota(
+                consuming_byte):
+                # The client violates quota. Close logical channel.
+                raise LogicalChannelError(
+                    channel_id, _DROP_CODE_SEND_QUOTA_VIOLATION)
+            header = create_header(opcode, len(payload), fin, rsv1, rsv2, rsv3,
+                                   mask=False)
+            frame_data = header + payload
+            channel_data.request.connection.append_frame_data(frame_data)
+        finally:
+            self._logical_channels_condition.release()
+
+    def dispatch_message(self, message):
+        """Dispatches message. The reader thread calls this method.
+
+        Args:
+            message: a message that contains encapsulated frame.
+        Raises:
+            PhysicalConnectionError: if the message contains physical
+                connection level errors.
+            LogicalChannelError: if the message contains logical channel
+                level errors.
+        """
+
+        parser = _MuxFramePayloadParser(message)
+        try:
+            channel_id = parser.read_channel_id()
+        except ValueError, e:
+            raise PhysicalConnectionError(_DROP_CODE_CHANNEL_ID_TRUNCATED)
+        if channel_id == _CONTROL_CHANNEL_ID:
+            self._process_control_blocks(parser)
+        else:
+            self._process_logical_frame(channel_id, parser)
+
+    def notify_worker_done(self, channel_id):
+        """Called when a worker has finished.
+
+        Args:
+            channel_id: channel id corresponded with the worker.
+        """
+
+        self._logger.debug('Worker for channel id %d terminated' % channel_id)
+        try:
+            self._logical_channels_condition.acquire()
+            if not channel_id in self._logical_channels:
+                raise MuxUnexpectedException(
+                    'Channel id %d not found' % channel_id)
+            channel_data = self._logical_channels.pop(channel_id)
+        finally:
+            self._worker_done_notify_received = True
+            self._logical_channels_condition.notify()
+            self._logical_channels_condition.release()
+
+        if not channel_data.request.server_terminated:
+            self._send_drop_channel(
+                channel_id, code=channel_data.drop_code,
+                message=channel_data.drop_message)
+
+    def notify_reader_done(self):
+        """This method is called by the reader thread when the reader has
+        finished.
+        """
+
+        self._logger.debug(
+            'Termiating all logical connections waiting for incoming data '
+            '...')
+        self._logical_channels_condition.acquire()
+        for channel_data in self._logical_channels.values():
+            try:
+                channel_data.request.connection.set_read_state(
+                    _LogicalConnection.STATE_TERMINATED)
+            except Exception:
+                self._logger.debug(traceback.format_exc())
+        self._logical_channels_condition.release()
+
+    def notify_writer_done(self):
+        """This method is called by the writer thread when the writer has
+        finished.
+        """
+
+        self._logger.debug(
+            'Termiating all logical connections waiting for write '
+            'completion ...')
+        self._logical_channels_condition.acquire()
+        for channel_data in self._logical_channels.values():
+            try:
+                channel_data.request.connection.on_writer_done()
+            except Exception:
+                self._logger.debug(traceback.format_exc())
+        self._logical_channels_condition.release()
+
+    def fail_physical_connection(self, code, message):
+        """Fail the physical connection.
+
+        Args:
+            code: drop reason code.
+            message: drop message.
+        """
+
+        self._logger.debug('Failing the physical connection...')
+        self._send_drop_channel(_CONTROL_CHANNEL_ID, code, message)
+        self._writer.stop(common.STATUS_INTERNAL_ENDPOINT_ERROR)
+
+    def fail_logical_channel(self, channel_id, code, message):
+        """Fail a logical channel.
+
+        Args:
+            channel_id: channel id.
+            code: drop reason code.
+            message: drop message.
+        """
+
+        self._logger.debug('Failing logical channel %d...' % channel_id)
+        try:
+            self._logical_channels_condition.acquire()
+            if channel_id in self._logical_channels:
+                channel_data = self._logical_channels[channel_id]
+                # Close the logical channel. notify_worker_done() will be
+                # called later and it will send DropChannel.
+                channel_data.drop_code = code
+                channel_data.drop_message = message
+
+                channel_data.request.connection.set_read_state(
+                    _LogicalConnection.STATE_TERMINATED)
+                channel_data.request.ws_stream.stop_sending()
+            else:
+                self._send_drop_channel(channel_id, code, message)
+        finally:
+            self._logical_channels_condition.release()
+
+
+def use_mux(request):
+    return hasattr(request, 'mux_processor') and (
+        request.mux_processor.is_active())
+
+
+def start(request, dispatcher):
+    mux_handler = _MuxHandler(request, dispatcher)
+    mux_handler.start()
+
+    mux_handler.add_channel_slots(_INITIAL_NUMBER_OF_CHANNEL_SLOTS,
+                                  _INITIAL_QUOTA_FOR_CLIENT)
+
+    mux_handler.wait_until_done()
+
+
+# vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/stream.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/stream.py
@@ -46,11 +46,12 @@ from mod_pywebsocket._stream_hybi import
 # their implementations receive broken data in tests.
 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
+from mod_pywebsocket._stream_hybi import create_closing_handshake_body
 
 
 # vi:sts=4 sw=4 et
--- a/testing/mochitest/pywebsocket/mod_pywebsocket/util.py
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/util.py
@@ -51,16 +51,21 @@ except ImportError:
 import StringIO
 import logging
 import os
 import re
 import socket
 import traceback
 import zlib
 
+try:
+    from mod_pywebsocket import fast_masking
+except ImportError:
+    pass
+
 
 def get_stack_trace():
     """Get the current stack trace as string.
 
     This is needed to support Python 2.3.
     TODO: Remove this when we only support Python 2.4 and above.
           Use traceback.format_exc instead.
     """
@@ -164,55 +169,49 @@ class NoopMasker(object):
 
 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.
     """
 
-    def __init__(self, mask):
-        self._mask = map(ord, mask)
-        self._mask_size = len(self._mask)
-        self._count = 0
+    def __init__(self, masking_key):
+        self._masking_key = masking_key
+        self._masking_key_index = 0
 
-    def mask(self, s):
+    def _mask_using_swig(self, s):
+        masked_data = fast_masking.mask(
+                s, self._masking_key, self._masking_key_index)
+        self._masking_key_index = (
+                (self._masking_key_index + len(s)) % len(self._masking_key))
+        return masked_data
+
+    def _mask_using_array(self, s):
         result = array.array('B')
         result.fromstring(s)
+
         # Use temporary local variables to eliminate the cost to access
         # attributes
-        count = self._count
-        mask = self._mask
-        mask_size = self._mask_size
+        masking_key = map(ord, self._masking_key)
+        masking_key_size = len(masking_key)
+        masking_key_index = self._masking_key_index
+
         for i in xrange(len(result)):
-            result[i] ^= mask[count]
-            count = (count + 1) % mask_size
-        self._count = count
+            result[i] ^= masking_key[masking_key_index]
+            masking_key_index = (masking_key_index + 1) % masking_key_size
+
+        self._masking_key_index = masking_key_index
 
         return result.tostring()
 
-
-class DeflateRequest(object):
-    """A wrapper class for request object to intercept send and recv to perform
-    deflate compression and decompression transparently.
-    """
-
-    def __init__(self, request):
-        self._request = request
-        self.connection = DeflateConnection(request.connection)
-
-    def __getattribute__(self, name):
-        if name in ('_request', 'connection'):
-            return object.__getattribute__(self, name)
-        return self._request.__getattribute__(name)
-
-    def __setattr__(self, name, value):
-        if name in ('_request', 'connection'):
-            return object.__setattr__(self, name, value)
-        return self._request.__setattr__(name, value)
+    if 'fast_masking' in globals():
+        mask = _mask_using_swig
+    else:
+        mask = _mask_using_array
 
 
 # By making wbits option negative, we can suppress CMF/FLG (2 octet) and
 # ADLER32 (4 octet) fields of zlib so that we can use zlib module just as
 # deflate library. DICTID won't be added as far as we don't set dictionary.
 # LZ77 window of 32K will be used for both compression and decompression.
 # For decompression, we can just use 32K to cover any windows size. For
 # compression, we use 32K so receivers must use 32K.
@@ -227,28 +226,42 @@ class DeflateRequest(object):
 class _Deflater(object):
 
     def __init__(self, window_bits):
         self._logger = get_class_logger(self)
 
         self._compress = zlib.compressobj(
             zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -window_bits)
 
+    def compress(self, bytes):
+        compressed_bytes = self._compress.compress(bytes)
+        self._logger.debug('Compress input %r', bytes)
+        self._logger.debug('Compress result %r', compressed_bytes)
+        return compressed_bytes
+
     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
 
+    def compress_and_finish(self, bytes):
+        compressed_bytes = self._compress.compress(bytes)
+        compressed_bytes += self._compress.flush(zlib.Z_FINISH)
+        self._logger.debug('Compress input %r', bytes)
+        self._logger.debug('Compress result %r', compressed_bytes)
+        return compressed_bytes
+
 
 class _Inflater(object):
 
-    def __init__(self):
+    def __init__(self, window_bits):
         self._logger = get_class_logger(self)
+        self._window_bits = window_bits
 
         self._unconsumed = ''
 
         self.reset()
 
     def decompress(self, size):
         if not (size == -1 or size > 0):
             raise Exception('size must be -1 or positive')
@@ -295,17 +308,17 @@ class _Inflater(object):
         return data
 
     def append(self, data):
         self._logger.debug('Appended %r', data)
         self._unconsumed += data
 
     def reset(self):
         self._logger.debug('Reset')
-        self._decompress = zlib.decompressobj(-zlib.MAX_WBITS)
+        self._decompress = zlib.decompressobj(-self._window_bits)
 
 
 # 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.
@@ -313,32 +326,46 @@ class _RFC1979Deflater(object):
 
     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:
+    def filter(self, bytes, end=True, bfinal=False):
+        if self._deflater is None:
             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]
+        if bfinal:
+            result = self._deflater.compress_and_finish(bytes)
+            # Add a padding block with BFINAL = 0 and BTYPE = 0.
+            result = result + chr(0)
+            self._deflater = None
+            return result
+
+        result = self._deflater.compress_and_flush(bytes)
+        if end:
+            # Strip last 4 octets which is LEN and NLEN field of a
+            # non-compressed block added for Z_SYNC_FLUSH.
+            result = result[:-4]
+
+        if self._no_context_takeover and end:
+            self._deflater = None
+
+        return result
 
 
 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 __init__(self, window_bits=zlib.MAX_WBITS):
+        self._inflater = _Inflater(window_bits)
 
     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)
 
 
@@ -351,17 +378,17 @@ class DeflateSocket(object):
     _RECV_SIZE = 4096
 
     def __init__(self, socket):
         self._socket = socket
 
         self._logger = get_class_logger(self)
 
         self._deflater = _Deflater(zlib.MAX_WBITS)
-        self._inflater = _Inflater()
+        self._inflater = _Inflater(zlib.MAX_WBITS)
 
     def recv(self, size):
         """Receives data from the socket specified on the construction up
         to the specified size. Once any data is available, returns it even
         if it's smaller than the specified size.
         """
 
         # TODO(tyoshino): Allow call with size=0. It should block until any
@@ -381,116 +408,9 @@ class DeflateSocket(object):
     def sendall(self, bytes):
         self.send(bytes)
 
     def send(self, bytes):
         self._socket.sendall(self._deflater.compress_and_flush(bytes))
         return len(bytes)
 
 
-class DeflateConnection(object):
-    """A wrapper class for request object to intercept write and read to
-    perform deflate compression and decompression transparently.
-    """
-
-    def __init__(self, connection):
-        self._connection = connection
-
-        self._logger = get_class_logger(self)
-
-        self._deflater = _Deflater(zlib.MAX_WBITS)
-        self._inflater = _Inflater()
-
-    def get_remote_addr(self):
-        return self._connection.remote_addr
-    remote_addr = property(get_remote_addr)
-
-    def put_bytes(self, bytes):
-        self.write(bytes)
-
-    def read(self, size=-1):
-        """Reads at most size bytes. Blocks until there's at least one byte
-        available.
-        """
-
-        # TODO(tyoshino): Allow call with size=0.
-        if not (size == -1 or size > 0):
-            raise Exception('size must be -1 or positive')
-
-        data = ''
-        while True:
-            if size == -1:
-                data += self._inflater.decompress(-1)
-            else:
-                data += self._inflater.decompress(size - len(data))
-
-            if size >= 0 and len(data) != 0:
-                break
-
-            # TODO(tyoshino): Make this read efficient by some workaround.
-            #
-            # In 3.0.3 and prior of mod_python, read blocks until length bytes
-            # was read. We don't know the exact size to read while using
-            # deflate, so read byte-by-byte.
-            #
-            # _StandaloneRequest.read that ultimately performs
-            # socket._fileobject.read also blocks until length bytes was read
-            read_data = self._connection.read(1)
-            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 _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:
-                error_number, message = e
-            except:
-                # Failed to parse socket.error.
-                raise e
-
-            if _is_ewouldblock_errno(error_number):
-                break
-            else:
-                raise e
-
-    # Rollback timeout value.
-    raw_socket.settimeout(original_timeout)
-
-    return ''.join(drained_data)
-
-
 # vi:sts=4 sw=4 et
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/pywebsocket/mod_pywebsocket/xhr_benchmark_handler.py
@@ -0,0 +1,109 @@
+# Copyright 2014 Google Inc. All rights reserved.
+#
+# Use of this source code is governed by a BSD-style
+# license that can be found in the COPYING file or at
+# https://developers.google.com/open-source/licenses/bsd
+
+
+from mod_pywebsocket import util
+
+
+class XHRBenchmarkHandler(object):
+    def __init__(self, headers, rfile, wfile):
+        self._logger = util.get_class_logger(self)
+
+        self.headers = headers
+        self.rfile = rfile
+        self.wfile = wfile
+
+    def do_send(self):
+        content_length = int(self.headers.getheader('Content-Length'))
+
+        self._logger.debug('Requested to receive %s bytes', content_length)
+
+        RECEIVE_BLOCK_SIZE = 1024 * 1024
+
+        bytes_to_receive = content_length
+        while bytes_to_receive > 0:
+            bytes_to_receive_in_this_loop = bytes_to_receive
+            if bytes_to_receive_in_this_loop > RECEIVE_BLOCK_SIZE:
+                bytes_to_receive_in_this_loop = RECEIVE_BLOCK_SIZE
+            received_data = self.rfile.read(bytes_to_receive_in_this_loop)
+            if received_data != ('a' * bytes_to_receive_in_this_loop):
+                self._logger.debug('Request body verification failed')
+                return
+            bytes_to_receive -= len(received_data)
+        if bytes_to_receive < 0:
+            self._logger.debug('Received %d more bytes than expected' %
+                               (-bytes_to_receive))
+            return
+
+        # Return the number of received bytes back to the client.
+        response_body = '%d' % content_length
+        self.wfile.write(
+            'HTTP/1.1 200 OK\r\n'
+            'Content-Type: text/html\r\n'
+            'Content-Length: %d\r\n'
+            '\r\n%s' % (len(response_body), response_body))
+        self.wfile.flush()
+
+    def do_receive(self):
+        content_length = int(self.headers.getheader('Content-Length'))
+        request_body = self.rfile.read(content_length)
+
+        request_array = request_body.split(' ')
+        if len(request_array) < 2:
+            self._logger.debug('Malformed request body: %r', request_body)
+            return
+
+        # Parse the size parameter.
+        bytes_to_send = request_array[0]
+        try:
+            bytes_to_send = int(bytes_to_send)
+        except ValueError, e:
+            self._logger.debug('Malformed size parameter: %r', bytes_to_send)
+            return
+        self._logger.debug('Requested to send %s bytes', bytes_to_send)
+
+        # Parse the transfer encoding parameter.
+        chunked_mode = False
+        mode_parameter = request_array[1]
+        if mode_parameter == 'chunked':
+            self._logger.debug('Requested chunked transfer encoding')
+            chunked_mode = True
+        elif mode_parameter != 'none':
+            self._logger.debug('Invalid mode parameter: %r', mode_parameter)
+            return
+
+        # Write a header
+        response_header = (
+            'HTTP/1.1 200 OK\r\n'
+            'Content-Type: application/octet-stream\r\n')
+        if chunked_mode:
+            response_header += 'Transfer-Encoding: chunked\r\n\r\n'
+        else:
+            response_header += (
+                'Content-Length: %d\r\n\r\n' % bytes_to_send)
+        self.wfile.write(response_header)
+        self.wfile.flush()
+
+        # Write a body
+        SEND_BLOCK_SIZE = 1024 * 1024
+
+        while bytes_to_send > 0:
+            bytes_to_send_in_this_loop = bytes_to_send
+            if bytes_to_send_in_this_loop > SEND_BLOCK_SIZE:
+                bytes_to_send_in_this_loop = SEND_BLOCK_SIZE
+
+            if chunked_mode:
+                self.wfile.write('%x\r\n' % bytes_to_send_in_this_loop)
+            self.wfile.write('a' * bytes_to_send_in_this_loop)
+            if chunked_mode:
+                self.wfile.write('\r\n')
+            self.wfile.flush()
+
+            bytes_to_send -= bytes_to_send_in_this_loop
+
+        if chunked_mode:
+            self.wfile.write('0\r\n\r\n')
+            self.wfile.flush()
--- a/testing/mochitest/pywebsocket/standalone.py
+++ b/testing/mochitest/pywebsocket/standalone.py
@@ -1,11 +1,11 @@
 #!/usr/bin/env python
 #
-# Copyright 2011, Google Inc.
+# Copyright 2012, 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.
@@ -27,40 +27,102 @@
 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 """Standalone WebSocket server.
 
-BASIC USAGE
+Use this file to launch pywebsocket without Apache HTTP Server.
+
 
-Use this server to run mod_pywebsocket without Apache HTTP Server.
+BASIC USAGE
+===========
 
-Usage:
-    python standalone.py [-p <ws_port>] [-w <websock_handlers>]
-                         [-s <scan_dir>]
-                         [-d <document_root>]
-                         [-m <websock_handlers_map_file>]
-                         ... for other options, see _main below ...
+Go to the src directory and run
+
+  $ python mod_pywebsocket/standalone.py [-p <ws_port>]
+                                         [-w <websock_handlers>]
+                                         [-d <document_root>]
 
 <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.
+If not specified, <document_root> will be used. See __init__.py (or
+run $ pydoc mod_pywebsocket) for how to write WebSocket handlers.
+
+For more detail and other options, run
+
+  $ python mod_pywebsocket/standalone.py --help
+
+or see _build_option_parser method below.
+
+For trouble shooting, adding "--log_level debug" might help you.
+
+
+TRY DEMO
+========
+
+Go to the src directory and run standalone.py with -d option to set the
+document root to the directory containing example HTMLs and handlers like this:
+
+  $ cd src
+  $ PYTHONPATH=. python mod_pywebsocket/standalone.py -d example
+
+to launch pywebsocket with the sample handler and html on port 80. Open
+http://localhost/console.html, click the connect button, type something into
+the text box next to the send button and click the send button. If everything
+is working, you'll see the message you typed echoed by the server.
+
+
+USING TLS
+=========
+
+To run the standalone server with TLS support, run it with -t, -k, and -c
+options. When TLS is enabled, the standalone server accepts only TLS connection.
 
-<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 that when ssl module is used and the key/cert location is incorrect,
+TLS connection silently fails while pyOpenSSL fails on startup.
+
+Example:
+
+  $ PYTHONPATH=. python mod_pywebsocket/standalone.py \
+        -d example \
+        -p 10443 \
+        -t \
+        -c ../test/cert/cert.pem \
+        -k ../test/cert/key.pem \
+
+Note that when passing a relative path to -c and -k option, it will be resolved
+using the document root directory as the base.
+
+
+USING CLIENT AUTHENTICATION
+===========================
+
+To run the standalone server with TLS client authentication support, run it with
+--tls-client-auth and --tls-client-ca options in addition to ones required for
+TLS support.
+
+Example:
+
+  $ PYTHONPATH=. python mod_pywebsocket/standalone.py -d example -p 10443 -t \
+        -c ../test/cert/cert.pem -k ../test/cert/key.pem \
+        --tls-client-auth \
+        --tls-client-ca=../test/cert/cacert.pem
+
+Note that when passing a relative path to --tls-client-ca option, it will be
+resolved using the document root directory as the base.
 
 
 CONFIGURATION FILE
+==================
 
 You can also write a configuration file and use it by specifying the path to
 the configuration file by --config option. Please write a configuration file
 following the documentation of the Python ConfigParser library. Name of each
 entry must be the long version argument name. E.g. to set log level to debug,
 add the following line:
 
 log_level=debug
@@ -74,73 +136,69 @@ Note that tls will be enabled even if yo
 fake.
 
 When both a command line argument and a configuration file entry are set for
 the same configuration item, the command line value will override one in the
 configuration file.
 
 
 THREADING
+=========
 
 This server is derived from SocketServer.ThreadingMixIn. Hence a thread is
 used for each request.
 
 
 SECURITY WARNING
+================
 
 This uses CGIHTTPServer and CGIHTTPServer is not secure.
 It may execute arbitrary Python code or external programs. It should not be
 used outside a firewall.
 """
 
 import BaseHTTPServer
 import CGIHTTPServer
 import SimpleHTTPServer
 import SocketServer
 import ConfigParser
+import base64
 import httplib
 import logging
 import logging.handlers
 import optparse
 import os
 import re
 import select
 import socket
 import sys
 import threading
 import time
 
-_HAS_SSL = False
-_HAS_OPEN_SSL = False
-try:
-    import ssl
-    _HAS_SSL = True
-except ImportError:
-    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
+from mod_pywebsocket.xhr_benchmark_handler import XHRBenchmarkHandler
 
 
 _DEFAULT_LOG_MAX_BYTES = 1024 * 256
 _DEFAULT_LOG_BACKUP_COUNT = 5
 
 _DEFAULT_REQUEST_QUEUE_SIZE = 128
 
 # 1024 is practically large enough to contain WebSocket handshake lines.
 _MAX_MEMORIZED_LINES = 1024
 
+# Constants for the --tls_module flag.
+_TLS_BY_STANDARD_MODULE = 'ssl'
+_TLS_BY_PYOPENSSL = 'pyopenssl'
+
 
 class _StandaloneConnection(object):
     """Mimic mod_python mp_conn."""
 
     def __init__(self, request_handler):
         """Construct an instance.
 
         Args:
@@ -194,81 +252,168 @@ class _StandaloneRequest(object):
         self._logger = util.get_class_logger(self)
 
         self._request_handler = request_handler
         self.connection = _StandaloneConnection(request_handler)
         self._use_tls = use_tls
         self.headers_in = request_handler.headers
 
     def get_uri(self):
-        """Getter to mimic request.uri."""
+        """Getter to mimic request.uri.
+
+        This method returns the raw data at the Request-URI part of the
+        Request-Line, while the uri method on the request object of mod_python
+        returns the path portion after parsing the raw data. This behavior is
+        kept for compatibility.
+        """
 
         return self._request_handler.path
     uri = property(get_uri)
 
+    def get_unparsed_uri(self):
+        """Getter to mimic request.unparsed_uri."""
+
+        return self._request_handler.path
+    unparsed_uri = property(get_unparsed_uri)
+
     def get_method(self):
         """Getter to mimic request.method."""
 
         return self._request_handler.command
     method = property(get_method)
 
+    def get_protocol(self):
+        """Getter to mimic request.protocol."""
+
+        return self._request_handler.request_version
+    protocol = property(get_protocol)
+
     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.
-        """
+
+def _import_ssl():
+    global ssl
+    try:
+        import ssl
+        return True
+    except ImportError:
+        return False
 
-        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)
+def _import_pyopenssl():
+    global OpenSSL
+    try:
+        import OpenSSL.SSL
+        return True
+    except ImportError:
+        return False
 
 
 class _StandaloneSSLConnection(object):
-    """A wrapper class for OpenSSL.SSL.Connection to provide makefile method
-    which is not supported by the class.
+    """A wrapper class for OpenSSL.SSL.Connection to
+    - provide makefile method which is not supported by the class
+    - tweak shutdown method since OpenSSL.SSL.Connection.shutdown doesn't
+      accept the "how" argument.
+    - convert SysCallError exceptions that its recv method may raise into a
+      return value of '', meaning EOF. We cannot overwrite the recv method on
+      self._connection since it's immutable.
     """
 
+    _OVERRIDDEN_ATTRIBUTES = ['_connection', 'makefile', 'shutdown', 'recv']
+
     def __init__(self, connection):
         self._connection = connection
 
     def __getattribute__(self, name):
-        if name in ('_connection', 'makefile'):
+        if name in _StandaloneSSLConnection._OVERRIDDEN_ATTRIBUTES:
             return object.__getattribute__(self, name)
         return self._connection.__getattribute__(name)
 
     def __setattr__(self, name, value):
-        if name in ('_connection', 'makefile'):
+        if name in _StandaloneSSLConnection._OVERRIDDEN_ATTRIBUTES:
             return object.__setattr__(self, name, value)
         return self._connection.__setattr__(name, value)
 
     def makefile(self, mode='r', bufsize=-1):
-        return socket._fileobject(self._connection, mode, bufsize)
+        return socket._fileobject(self, mode, bufsize)
+
+    def shutdown(self, unused_how):
+        self._connection.shutdown()
+
+    def recv(self, bufsize, flags=0):
+        if flags != 0:
+            raise ValueError('Non-zero flags not allowed')
+
+        try:
+            return self._connection.recv(bufsize)
+        except OpenSSL.SSL.SysCallError, (err, message):
+            if err == -1:
+                # Suppress "unexpected EOF" exception. See the OpenSSL document
+                # for SSL_get_error.
+                return ''
+            raise
+
+
+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.DispatchException, e:
+                logging.error(str(e))
+    finally:
+        fp.close()
 
 
 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, options):
         """Override SocketServer.TCPServer.__init__ to set SSL enabled
         socket object to self.socket before server_bind and server_activate,
         if necessary.
         """
 
+        # Share a Dispatcher among request handlers to save time for
+        # instantiation.  Dispatcher can be shared because it is thread-safe.
+        options.dispatcher = dispatch.Dispatcher(
+            options.websock_handlers,
+            options.scan_dir,
+            options.allow_handlers_outside_root_dir)
+        if options.websock_handlers_map_file:
+            _alias_handlers(options.dispatcher,
+                            options.websock_handlers_map_file)
+        warnings = options.dispatcher.source_warnings()
+        if warnings:
+            for warning in warnings:
+                logging.warning('Warning in source loading: %s' % warning)
+
         self._logger = util.get_class_logger(self)
 
         self.request_queue_size = options.request_queue_size
         self.__ws_is_shut_down = threading.Event()
         self.__ws_serving = False
 
         SocketServer.BaseServer.__init__(
             self, (options.server_host, options.port), WebSocketRequestHandler)
@@ -304,29 +449,35 @@ class WebSocketServer(SocketServer.Threa
         for addrinfo in addrinfo_array:
             self._logger.info('Create socket on: %r', addrinfo)
             family, socktype, proto, canonname, sockaddr = addrinfo
             try:
                 socket_ = socket.socket(family, socktype)
             except Exception, e:
                 self._logger.info('Skip by failure: %r', e)
                 continue
-            if self.websocket_server_options.use_tls:
-                if _HAS_SSL:
+            server_options = self.websocket_server_options
+            if server_options.use_tls:
+                # For the case of _HAS_OPEN_SSL, we do wrapper setup after
+                # accept.
+                if server_options.tls_module == _TLS_BY_STANDARD_MODULE:
+                    if server_options.tls_client_auth:
+                        if server_options.tls_client_cert_optional:
+                            client_cert_ = ssl.CERT_OPTIONAL
+                        else:
+                            client_cert_ = ssl.CERT_REQUIRED
+                    else:
+                        client_cert_ = ssl.CERT_NONE
                     socket_ = ssl.wrap_socket(socket_,
-                        keyfile=self.websocket_server_options.private_key,
-                        certfile=self.websocket_server_options.certificate,
-                        ssl_version=ssl.PROTOCOL_SSLv23)
-                if _HAS_OPEN_SSL:
-                    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_)
+                        keyfile=server_options.private_key,
+                        certfile=server_options.certificate,
+                        ssl_version=ssl.PROTOCOL_SSLv23,
+                        ca_certs=server_options.tls_client_ca,
+                        cert_reqs=client_cert_,
+                        do_handshake_on_connect=False)
             self._sockets.append((socket_, addrinfo))
 
     def server_bind(self):
         """Override SocketServer.TCPServer.server_bind to enable multiple
         sockets bind.
         """
 
         failed_sockets = []
@@ -337,16 +488,25 @@ class WebSocketServer(SocketServer.Threa
             if self.allow_reuse_address:
                 socket_.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
             try:
                 socket_.bind(self.server_address)
             except Exception, e:
                 self._logger.info('Skip by failure: %r', e)
                 socket_.close()
                 failed_sockets.append(socketinfo)
+            if self.server_address[1] == 0:
+                # The operating system assigns the actual port number for port
+                # number 0. This case, the second and later sockets should use
+                # the same port number. Also self.server_port is rewritten
+                # because it is exported, and will be used by external code.
+                self.server_address = (
+                    self.server_name, socket_.getsockname()[1])
+                self.server_port = self.server_address[1]
+                self._logger.info('Port %r is assigned', self.server_port)
 
         for socketinfo in failed_sockets:
             self._sockets.remove(socketinfo)
 
     def server_activate(self):
         """Override SocketServer.TCPServer.server_activate to enable multiple
         sockets listen.
         """
@@ -361,51 +521,111 @@ class WebSocketServer(SocketServer.Threa
             except Exception, e:
                 self._logger.info('Skip by failure: %r', e)
                 socket_.close()
                 failed_sockets.append(socketinfo)
 
         for socketinfo in failed_sockets:
             self._sockets.remove(socketinfo)
 
+        if len(self._sockets) == 0:
+            self._logger.critical(
+                'No sockets activated. Use info log level to see the reason.')
+
     def server_close(self):
         """Override SocketServer.TCPServer.server_close to enable multiple
         sockets close.
         """
 
         for socketinfo in self._sockets:
             socket_, addrinfo = socketinfo
             self._logger.info('Close on: %r', addrinfo)
             socket_.close()
 
     def fileno(self):
         """Override SocketServer.TCPServer.fileno."""
 
         self._logger.critical('Not supported: fileno')
         return self._sockets[0][0].fileno()
 
-    def handle_error(self, rquest, client_address):
+    def handle_error(self, request, client_address):
         """Override SocketServer.handle_error."""
 
         self._logger.error(
             'Exception in processing request from: %r\n%s',
             client_address,
             util.get_stack_trace())
         # Note: client_address is a tuple.
 
     def get_request(self):
         """Override TCPServer.get_request to wrap OpenSSL.SSL.Connection
         object with _StandaloneSSLConnection to provide makefile method. We
         cannot substitute OpenSSL.SSL.Connection.makefile since it's readonly
         attribute.
         """
 
         accepted_socket, client_address = self.socket.accept()
-        if self.websocket_server_options.use_tls and _HAS_OPEN_SSL:
-            accepted_socket = _StandaloneSSLConnection(accepted_socket)
+
+        server_options = self.websocket_server_options
+        if server_options.use_tls:
+            if server_options.tls_module == _TLS_BY_STANDARD_MODULE:
+                try:
+                    accepted_socket.do_handshake()
+                except ssl.SSLError, e:
+                    self._logger.debug('%r', e)
+                    raise
+
+                # Print cipher in use. Handshake is done on accept.
+                self._logger.debug('Cipher: %s', accepted_socket.cipher())
+                self._logger.debug('Client cert: %r',
+                                   accepted_socket.getpeercert())
+            elif server_options.tls_module == _TLS_BY_PYOPENSSL:
+                # We cannot print the cipher in use. pyOpenSSL doesn't provide
+                # any method to fetch that.
+
+                ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
+                ctx.use_privatekey_file(server_options.private_key)
+                ctx.use_certificate_file(server_options.certificate)
+
+                def default_callback(conn, cert, errnum, errdepth, ok):
+                    return ok == 1
+
+                # See the OpenSSL document for SSL_CTX_set_verify.
+                if server_options.tls_client_auth:
+                    verify_mode = OpenSSL.SSL.VERIFY_PEER
+                    if not server_options.tls_client_cert_optional:
+                        verify_mode |= OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT
+                    ctx.set_verify(verify_mode, default_callback)
+                    ctx.load_verify_locations(server_options.tls_client_ca,
+                                              None)
+                else:
+                    ctx.set_verify(OpenSSL.SSL.VERIFY_NONE, default_callback)
+
+                accepted_socket = OpenSSL.SSL.Connection(ctx, accepted_socket)
+                accepted_socket.set_accept_state()
+
+                # Convert SSL related error into socket.error so that
+                # SocketServer ignores them and keeps running.
+                #
+                # TODO(tyoshino): Convert all kinds of errors.
+                try:
+                    accepted_socket.do_handshake()
+                except OpenSSL.SSL.Error, e:
+                    # Set errno part to 1 (SSL_ERROR_SSL) like the ssl module
+                    # does.
+                    self._logger.debug('%r', e)
+                    raise socket.error(1, '%r' % e)
+                cert = accepted_socket.get_peer_certificate()
+                if cert is not None:
+                    self._logger.debug('Client cert subject: %r',
+                                       cert.get_subject().get_components())
+                accepted_socket = _StandaloneSSLConnection(accepted_socket)
+            else:
+                raise ValueError('No TLS support module is available')
+
         return accepted_socket, client_address
 
     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
@@ -488,17 +708,42 @@ class WebSocketRequestHandler(CGIHTTPSer
         # it needs variables set by CGIHTTPRequestHandler.parse_request.
         #
         # Variables set by this method will be also used by WebSocket request
         # handling (self.path, self.command, self.requestline, etc. See also
         # how _StandaloneRequest's members are implemented using these
         # attributes).
         if not CGIHTTPServer.CGIHTTPRequestHandler.parse_request(self):
             return False
+
+        if self._options.use_basic_auth:
+            auth = self.headers.getheader('Authorization')
+            if auth != self._options.basic_auth_credential:
+                self.send_response(401)
+                self.send_header('WWW-Authenticate',
+                                 'Basic realm="Pywebsocket"')
+                self.end_headers()
+                self._logger.info('Request basic authentication')
+                return False
+
         host, port, resource = http_header_util.parse_uri(self.path)
+
+        # Special paths for XMLHttpRequest benchmark
+        xhr_benchmark_helper_prefix = '/073be001e10950692ccbf3a2ad21c245'
+        if resource == (xhr_benchmark_helper_prefix + '_send'):
+            xhr_benchmark_handler = XHRBenchmarkHandler(
+                self.headers, self.rfile, self.wfile)
+            xhr_benchmark_handler.do_send()
+            return False
+        if resource == (xhr_benchmark_helper_prefix + '_receive'):
+            xhr_benchmark_handler = XHRBenchmarkHandler(
+                self.headers, self.rfile, self.wfile)
+            xhr_benchmark_handler.do_receive()
+            return False
+
         if resource is None:
             self._logger.info('Invalid URI: %r', self.path)
             self._logger.info('Fallback to CGIHTTPRequestHandler')
             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:
@@ -523,48 +768,48 @@ class WebSocketRequestHandler(CGIHTTPSer
             # 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):
                 self._logger.info('No handler for resource: %r',
                                   self.path)
                 self._logger.info('Fallback to CGIHTTPRequestHandler')
                 return True
         except dispatch.DispatchException, e:
-            self._logger.info('%s', e)
+            self._logger.info('Dispatch failed for error: %s', e)
             self.send_error(e.status)
             return False
 
         # If any Exceptions without except clause setup (including
         # DispatchException) is raised below this point, it will be caught
         # and logged by WebSocketServer.
 
         try:
             try:
                 handshake.do_handshake(
                     request,
                     self._options.dispatcher,
                     allowDraft75=self._options.allow_draft75,
                     strict=self._options.strict)
             except handshake.VersionException, e:
-                self._logger.info('%s', e)
+                self._logger.info('Handshake failed for version error: %s', e)
                 self.send_response(common.HTTP_STATUS_BAD_REQUEST)
                 self.send_header(common.SEC_WEBSOCKET_VERSION_HEADER,
                                  e.supported_versions)
                 self.end_headers()
                 return False
             except handshake.HandshakeException, e:
                 # Handshake for ws(s) failed.
-                self._logger.info('%s', e)
+                self._logger.info('Handshake failed for error: %s', e)
                 self.send_error(e.status)
                 return False
 
             request._dispatcher = self._options.dispatcher
             self._options.dispatcher.transfer_data(request)
         except handshake.AbortedByUserException, e:
-            self._logger.info('%s', e)
+            self._logger.info('Aborted: %s', e)
         return False
 
     def log_request(self, code='-', size='-'):
         """Override BaseHTTPServer.log_request."""
 
         self._logger.info('"%s" %s %s',
                           self.requestline, str(code), str(size))
 
@@ -596,54 +841,41 @@ class WebSocketRequestHandler(CGIHTTPSer
             if not os.path.isfile(scriptfile):
                 return False
             if not self.is_executable(scriptfile):
                 return False
             return True
         return False
 
 
+def _get_logger_from_class(c):
+    return logging.getLogger('%s.%s' % (c.__module__, c.__name__))
+
+
 def _configure_logging(options):
+    logging.addLevelName(common.LOGLEVEL_FINE, 'FINE')
+
     logger = logging.getLogger()
     logger.setLevel(logging.getLevelName(options.log_level.upper()))
     if options.log_file:
         handler = logging.handlers.RotatingFileHandler(
                 options.log_file, 'a', options.log_max, options.log_count)
     else:
         handler = logging.StreamHandler()
     formatter = logging.Formatter(
             '[%(asctime)s] [%(levelname)s] %(name)s: %(message)s')
     handler.setFormatter(formatter)
     logger.addHandler(handler)
 
-
-def _alias_handlers(dispatcher, websock_handlers_map_file):
-    """Set aliases specified in websock_handler_map_file in dispatcher.
-
-    Args:
-        dispatcher: dispatch.Dispatcher instance
-        websock_handler_map_file: alias map file
-    """
-
-    fp = open(websock_handlers_map_file)
-    try:
-        for line in fp:
-            if line[0] == '#' or line.isspace():
-                continue
-            m = re.match('(\S+)\s+(\S+)', line)
-            if not m:
-                logging.warning('Wrong format in map file:' + line)
-                continue
-            try:
-                dispatcher.add_resource_path_alias(
-                    m.group(1), m.group(2))
-            except dispatch.DispatchException, e:
-                logging.error(str(e))
-    finally:
-        fp.close()
+    deflate_log_level_name = logging.getLevelName(
+        options.deflate_log_level.upper())
+    _get_logger_from_class(util._Deflater).setLevel(
+        deflate_log_level_name)
+    _get_logger_from_class(util._Inflater).setLevel(
+        deflate_log_level_name)
 
 
 def _build_option_parser():
     parser = optparse.OptionParser()
 
     parser.add_option('--config', dest='config_file', type='string',
                       default=None,
                       help=('Path to configuration file. See the file comment '
@@ -662,58 +894,99 @@ def _build_option_parser():
                       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.')
+                      help=('The root directory of WebSocket handler files. '
+                            'If the path is relative, --document-root is used '
+                            'as the base.'))
     parser.add_option('-m', '--websock-handlers-map-file',
                       '--websock_handlers_map_file',
                       dest='websock_handlers_map_file',
                       default=None,
                       help=('WebSocket handlers map file. '
                             'Each line consists of alias_resource_path and '
                             'existing_resource_path, separated by spaces.'))
     parser.add_option('-s', '--scan-dir', '--scan_dir', dest='scan_dir',
                       default=None,
-                      help=('WebSocket handlers scan directory. '
-                            'Must be a directory under websock_handlers.'))
+                      help=('Must be a directory under --websock-handlers. '
+                            'Only handlers under this directory are scanned '
+                            'and registered to the server. '
+                            'Useful for saving scan time when the handler '
+                            'root directory contains lots of files that are '
+                            'not handler file or are handler files but you '
+                            'don\'t want them to be registered. '))
     parser.add_option('--allow-handlers-outside-root-dir',
                       '--allow_handlers_outside_root_dir',
                       dest='allow_handlers_outside_root_dir',
                       action='store_true',
                       default=False,
                       help=('Scans WebSocket handlers even if their canonical '
-                            'path is not under websock_handlers.'))
+                            'path is not under --websock-handlers.'))
     parser.add_option('-d', '--document-root', '--document_root',
                       dest='document_root', default='.',
                       help='Document root directory.')
     parser.add_option('-x', '--cgi-paths', '--cgi_paths', dest='cgi_paths',
                       default=None,
                       help=('CGI paths relative to document_root.'
                             'Comma-separated. (e.g -x /cgi,/htbin) '
                             'Files under document_root/cgi_path are handled '
                             'as CGI programs. Must be executable.'))
     parser.add_option('-t', '--tls', dest='use_tls', action='store_true',
                       default=False, help='use TLS (wss://)')
+    parser.add_option('--tls-module', '--tls_module', dest='tls_module',
+                      type='choice',
+                      choices = [_TLS_BY_STANDARD_MODULE, _TLS_BY_PYOPENSSL],
+                      help='Use ssl module if "%s" is specified. '
+                      'Use pyOpenSSL module if "%s" is specified' %
+                      (_TLS_BY_STANDARD_MODULE, _TLS_BY_PYOPENSSL))
     parser.add_option('-k', '--private-key', '--private_key',
                       dest='private_key',
                       default='', help='TLS private key file.')
     parser.add_option('-c', '--certificate', dest='certificate',
                       default='', help='TLS certificate file.')
+    parser.add_option('--tls-client-auth', dest='tls_client_auth',
+                      action='store_true', default=False,
+                      help='Requests TLS client auth on every connection.')
+    parser.add_option('--tls-client-cert-optional',
+                      dest='tls_client_cert_optional',
+                      action='store_true', default=False,
+                      help=('Makes client certificate optional even though '
+                            'TLS client auth is enabled.'))
+    parser.add_option('--tls-client-ca', dest='tls_client_ca', default='',
+                      help=('Specifies a pem file which contains a set of '
+                            'concatenated CA certificates which are used to '
+                            'validate certificates passed from clients'))
+    parser.add_option('--basic-auth', dest='use_basic_auth',
+                      action='store_true', default=False,
+                      help='Requires Basic authentication.')
+    parser.add_option('--basic-auth-credential',
+                      dest='basic_auth_credential', default='test:test',
+                      help='Specifies the credential of basic authentication '
+                      'by username:password pair (e.g. test:test).')
     parser.add_option('-l', '--log-file', '--log_file', dest='log_file',
                       default='', help='Log file.')
+    # Custom log level:
+    # - FINE: Prints status of each frame processing step
     parser.add_option('--log-level', '--log_level', type='choice',
                       dest='log_level', default='warn',
+                      choices=['fine',
+                               'debug', 'info', 'warning', 'warn', 'error',
+                               'critical'],
+                      help='Log level.')
+    parser.add_option('--deflate-log-level', '--deflate_log_level',
+                      type='choice',
+                      dest='deflate_log_level', default='warn',
                       choices=['debug', 'info', 'warning', 'warn', 'error',
                                'critical'],
-                      help='Log level.')
+                      help='Log level for _Deflater and _Inflater.')
     parser.add_option('--thread-monitor-interval-in-sec',
                       '--thread_monitor_interval_in_sec',
                       dest='thread_monitor_interval_in_sec',
                       type='int', default=-1,
                       help=('If positive integer is specified, run a thread '
                             'monitor to show the status of server threads '
                             'periodically in the specified inteval in '
                             'second. If non-positive integer is specified, '
@@ -721,19 +994,19 @@ def _build_option_parser():
     parser.add_option('--log-max', '--log_max', dest='log_max', type='int',
                       default=_DEFAULT_LOG_MAX_BYTES,
                       help='Log maximum bytes')
     parser.add_option('--log-count', '--log_count', dest='log_count',
                       type='int', default=_DEFAULT_LOG_BACKUP_COUNT,
                       help='Log backup count')
     parser.add_option('--allow-draft75', dest='allow_draft75',
                       action='store_true', default=False,
-                      help='Allow draft 75 handshake')
+                      help='Obsolete option. Ignored.')
     parser.add_option('--strict', dest='strict', action='store_true',
-                      default=False, help='Strictly check handshake request')
+                      default=False, help='Obsolete option. Ignored.')
     parser.add_option('-q', '--queue', dest='request_queue_size', type='int',
                       default=_DEFAULT_REQUEST_QUEUE_SIZE,
                       help='request queue size')
 
     return parser
 
 
 class ThreadMonitor(threading.Thread):
@@ -791,22 +1064,34 @@ def _parse_args_and_config(args):
         else:
             args = args_from_config + args
         return parser.parse_args(args=args)
     else:
         return temporary_options, temporary_args
 
 
 def _main(args=None):
+    """You can call this function from your own program, but please note that
+    this function has some side-effects that might affect your program. For
+    example, util.wrap_popen3_for_win use in this method replaces implementation
+    of os.popen3.
+    """
+
     options, args = _parse_args_and_config(args=args)
 
     os.chdir(options.document_root)
 
     _configure_logging(options)
 
+    if options.allow_draft75:
+        logging.warning('--allow_draft75 option is obsolete.')
+
+    if options.strict:
+        logging.warning('--strict option is obsolete.')
+
     # 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:
         options.cgi_directories = options.cgi_paths.split(',')
         if sys.platform in ('cygwin', 'win32'):
             cygwin_path = None
@@ -819,47 +1104,77 @@ def _main(args=None):
             util.wrap_popen3_for_win(cygwin_path)
 
             def __check_script(scriptpath):
                 return util.get_script_interp(scriptpath, cygwin_path)
 
             options.is_executable_method = __check_script
 
     if options.use_tls:
-        if not (_HAS_SSL or _HAS_OPEN_SSL):
-            logging.critical('TLS support requires ssl or pyOpenSSL.')
+        if options.tls_module is None:
+            if _import_ssl():
+                options.tls_module = _TLS_BY_STANDARD_MODULE
+                logging.debug('Using ssl module')
+            elif _import_pyopenssl():
+                options.tls_module = _TLS_BY_PYOPENSSL
+                logging.debug('Using pyOpenSSL module')
+            else:
+                logging.critical(
+                        'TLS support requires ssl or pyOpenSSL module.')
+                sys.exit(1)
+        elif options.tls_module == _TLS_BY_STANDARD_MODULE:
+            if not _import_ssl():
+                logging.critical('ssl module is not available')
+                sys.exit(1)
+        elif options.tls_module == _TLS_BY_PYOPENSSL:
+            if not _import_pyopenssl():
+                logging.critical('pyOpenSSL module is not available')
+                sys.exit(1)
+        else:
+            logging.critical('Invalid --tls-module option: %r',
+                             options.tls_module)
             sys.exit(1)
+
         if not options.private_key or not options.certificate:
             logging.critical(
                     'To use TLS, specify private_key and certificate.')
             sys.exit(1)
 
+        if (options.tls_client_cert_optional and
+            not options.tls_client_auth):
+            logging.critical('Client authentication must be enabled to '
+                             'specify tls_client_cert_optional')
+            sys.exit(1)
+    else:
+        if options.tls_module is not None:
+            logging.critical('Use --tls-module option only together with '
+                             '--use-tls option.')
+            sys.exit(1)
+
+        if options.tls_client_auth:
+            logging.critical('TLS must be enabled for client authentication.')
+            sys.exit(1)
+
+        if options.tls_client_cert_optional:
+            logging.critical('TLS must be enabled for client authentication.')
+            sys.exit(1)
+
     if not options.scan_dir:
         options.scan_dir = options.websock_handlers
 
+    if options.use_basic_auth:
+        options.basic_auth_credential = 'Basic ' + base64.b64encode(
+            options.basic_auth_credential)
+
     try:
         if options.thread_monitor_interval_in_sec > 0:
             # Run a thread monitor to show the status of server threads for
             # debugging.
             ThreadMonitor(options.thread_monitor_interval_in_sec).start()
 
-        # Share a Dispatcher among request handlers to save time for
-        # instantiation.  Dispatcher can be shared because it is thread-safe.
-        options.dispatcher = dispatch.Dispatcher(
-            options.websock_handlers,
-            options.scan_dir,
-            options.allow_handlers_outside_root_dir)
-        if options.websock_handlers_map_file:
-            _alias_handlers(options.dispatcher,
-                            options.websock_handlers_map_file)
-        warnings = options.dispatcher.source_warnings()
-        if warnings:
-            for warning in warnings:
-                logging.warning('mod_pywebsocket: %s' % warning)
-
         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)