Merge appache_fix

http://code.google.com/p/pywebsocket/issue/detail?id=51

diff --git a/src/mod_pywebsocket/__init__.py b/src/mod_pywebsocket/__init__.py
index 05e80e8..d947128 100755
--- a/src/mod_pywebsocket/__init__.py
+++ b/src/mod_pywebsocket/__init__.py
@@ -53,12 +53,18 @@
 
    To limit the search for Web Socket 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-Web Socket handler files.
 
+   If you want to support old handshake based on
+   draft-hixie-thewebsocketprotocol-75:
+
+       PythonOption mod_pywebsocket.allow_draft75 On
+
+
    Example snippet of httpd.conf:
    (mod_pywebsocket is in /websock_lib, Web Socket handlers are in
    /websock_handlers, port is 80 for ws, 443 for wss.)
diff --git a/src/mod_pywebsocket/handshake.py b/src/mod_pywebsocket/handshake.py
deleted file mode 100755
index b86278e..0000000
--- a/src/mod_pywebsocket/handshake.py
+++ /dev/null
@@ -1,220 +0,0 @@
-# Copyright 2009, 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.
-
-
-"""Web Socket handshaking.
-
-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 re
-
-import util
-
-
-_DEFAULT_WEB_SOCKET_PORT = 80
-_DEFAULT_WEB_SOCKET_SECURE_PORT = 443
-_WEB_SOCKET_SCHEME = 'ws'
-_WEB_SOCKET_SECURE_SCHEME = 'wss'
-
-_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')
-
-
-def _default_port(is_secure):
-    if is_secure:
-        return _DEFAULT_WEB_SOCKET_SECURE_PORT
-    else:
-        return _DEFAULT_WEB_SOCKET_PORT
-
-
-class HandshakeError(Exception):
-    """Exception in Web Socket Handshake."""
-
-    pass
-
-
-def _validate_protocol(protocol):
-    """Validate WebSocket-Protocol string."""
-
-    if not protocol:
-        raise HandshakeError('Invalid WebSocket-Protocol: empty')
-    for c in protocol:
-        if not 0x20 <= ord(c) <= 0x7e:
-            raise HandshakeError('Illegal character in protocol: %r' % c)
-
-
-class Handshaker(object):
-    """This class performs Web Socket 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._request = request
-        self._dispatcher = dispatcher
-        self._strict = strict
-
-    def do_handshake(self):
-        """Perform Web Socket Handshake."""
-
-        self._check_header_lines()
-        self._set_resource()
-        self._set_origin()
-        self._set_location()
-        self._set_protocol()
-        self._dispatcher.do_extra_handshake(self._request)
-        self._send_handshake()
-
-    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):
-        location_parts = []
-        if self._request.is_https():
-            location_parts.append(_WEB_SOCKET_SECURE_SCHEME)
-        else:
-            location_parts.append(_WEB_SOCKET_SCHEME)
-        location_parts.append('://')
-        host, port = self._parse_host_header()
-        connection_port = self._request.connection.local_addr[1]
-        if port != connection_port:
-            raise HandshakeError('Header/connection port mismatch: %d/%d' %
-                                 (port, connection_port))
-        location_parts.append(host)
-        if (port != _default_port(self._request.is_https())):
-            location_parts.append(':')
-            location_parts.append(str(port))
-        location_parts.append(self._request.uri)
-        self._request.ws_location = ''.join(location_parts)
-
-    def _parse_host_header(self):
-        fields = self._request.headers_in['Host'].split(':', 1)
-        if len(fields) == 1:
-            return fields[0], _default_port(self._request.is_https())
-        try:
-            return fields[0], int(fields[1])
-        except ValueError, e:
-            raise HandshakeError('Invalid port number format: %r' % e)
-
-    def _set_protocol(self):
-        protocol = self._request.headers_in.get('WebSocket-Protocol')
-        if protocol is not None:
-            _validate_protocol(protocol)
-        self._request.ws_protocol = protocol
-
-    def _send_handshake(self):
-        self._request.connection.write(
-                'HTTP/1.1 101 Web Socket Protocol Handshake\r\n')
-        self._request.connection.write('Upgrade: WebSocket\r\n')
-        self._request.connection.write('Connection: Upgrade\r\n')
-        self._request.connection.write('WebSocket-Origin: ')
-        self._request.connection.write(self._request.ws_origin)
-        self._request.connection.write('\r\n')
-        self._request.connection.write('WebSocket-Location: ')
-        self._request.connection.write(self._request.ws_location)
-        self._request.connection.write('\r\n')
-        if self._request.ws_protocol:
-            self._request.connection.write('WebSocket-Protocol: ')
-            self._request.connection.write(self._request.ws_protocol)
-            self._request.connection.write('\r\n')
-        self._request.connection.write('\r\n')
-
-    def _check_header_lines(self):
-        for key, expected_value in _MANDATORY_HEADERS:
-            actual_value = self._request.headers_in.get(key)
-            if not actual_value:
-                raise HandshakeError('Header %s is not defined' % key)
-            if expected_value:
-                if actual_value != expected_value:
-                    raise HandshakeError('Illegal value for header %s: %s' %
-                                         (key, actual_value))
-        if self._strict:
-            try:
-                lines = self._request.connection.get_memorized_lines()
-            except AttributeError, e:
-                util.prepend_message_to_exception(
-                    'Strict handshake is specified but the connection '
-                    'doesn\'t provide get_memorized_lines()', e)
-                raise
-            self._check_first_lines(lines)
-
-    def _check_first_lines(self, lines):
-        if len(lines) < len(_FIRST_FIVE_LINES):
-            raise HandshakeError('Too few header lines: %d' % len(lines))
-        for line, regexp in zip(lines, _FIRST_FIVE_LINES):
-            if not regexp.search(line):
-                raise HandshakeError('Unexpected header: %r doesn\'t match %r'
-                                     % (line, regexp.pattern))
-        sixth_and_later = ''.join(lines[5:])
-        if not _SIXTH_AND_LATER.search(sixth_and_later):
-            raise HandshakeError('Unexpected header: %r doesn\'t match %r'
-                                 % (sixth_and_later,
-                                    _SIXTH_AND_LATER.pattern))
-
-
-# vi:sts=4 sw=4 et
diff --git a/src/mod_pywebsocket/handshake/__init__.py b/src/mod_pywebsocket/handshake/__init__.py
index 0c058d1..4c4e41a 100755
--- a/src/mod_pywebsocket/handshake/__init__.py
+++ b/src/mod_pywebsocket/handshake/__init__.py
@@ -68,6 +68,7 @@
         handshake.
         """
 
+        self._logger = logging.getLogger("mod_pywebsocket.handshake")
         self._request = request
         self._dispatcher = dispatcher
         self._strict = strict
@@ -83,9 +84,9 @@
         try:
             self._handshaker.do_handshake()
         except HandshakeError, e:
-            logging.info('Handshake error: %s' % e)
+            self._logger.error('Handshake error: %s' % e)
             if self._fallbackHandshaker:
-                logging.info('fallback to old protocol')
+                self._logger.warning('fallback to old protocol')
                 self._fallbackHandshaker.do_handshake()
                 return
             raise e
diff --git a/src/mod_pywebsocket/handshake/handshake.py b/src/mod_pywebsocket/handshake/handshake.py
index 3985717..cec80c0 100755
--- a/src/mod_pywebsocket/handshake/handshake.py
+++ b/src/mod_pywebsocket/handshake/handshake.py
@@ -68,6 +68,7 @@
         handshake.
         """
 
+        self._logger = logging.getLogger("mod_pywebsocket.handshake")
         self._request = request
         self._dispatcher = dispatcher
 
@@ -119,8 +120,10 @@
         self._request.ws_challenge = self._get_challenge()
         self._request.ws_challenge_md5 = md5(
             self._request.ws_challenge).digest()
-        logging.debug("challenge: %s" % _hexify(self._request.ws_challenge))
-        logging.debug("response:  %s" % _hexify(self._request.ws_challenge_md5))
+        self._logger.debug("challenge: %s" % _hexify(
+	    self._request.ws_challenge))
+        self._logger.debug("response:  %s" % _hexify(
+	    self._request.ws_challenge_md5))
 
     def _get_key_value(self, key_field):
         key_field = self._request.headers_in.get(key_field)
@@ -131,7 +134,7 @@
             digit = int(re.sub("\\D", "", key_field))
             # number of spaces characters in the value.
             num_spaces = re.subn(" ", "", key_field)[1]
-            logging.debug("%s: %d / %d => %d" % (
+            self._logger.debug("%s: %d / %d => %d" % (
                 key_field, digit, num_spaces, digit / num_spaces))
             return digit / num_spaces
         except:
diff --git a/src/mod_pywebsocket/headerparserhandler.py b/src/mod_pywebsocket/headerparserhandler.py
index 124b9f1..9d40ab6 100755
--- a/src/mod_pywebsocket/headerparserhandler.py
+++ b/src/mod_pywebsocket/headerparserhandler.py
@@ -39,6 +39,7 @@
 
 import dispatch
 import handshake
+import logging
 import util
 
 
@@ -50,6 +51,35 @@
 # The default is the root directory.
 _PYOPT_HANDLER_SCAN = 'mod_pywebsocket.handler_scan'
 
+# PythonOption to specify to allow draft75 handshake.
+# The default is None (Off)
+_PYOPT_ALLOW_DRAFT75 = 'mod_pywebsocket.allow_draft75'
+
+
+class ApacheLogHandler(logging.Handler):
+    """Wrapper logging.Handler to emit log message to apache's error.log"""
+    _LEVELS = {
+        logging.DEBUG: apache.APLOG_DEBUG,
+        logging.INFO: apache.APLOG_INFO,
+        logging.WARNING: apache.APLOG_WARNING,
+        logging.ERROR: apache.APLOG_ERR,
+        logging.CRITICAL: apache.APLOG_CRIT,
+        }
+    def __init__(self, request=None):
+        logging.Handler.__init__(self)
+        self.log_error = apache.log_error
+        if request is not None:
+             self.log_error = request.log_error
+
+    def emit(self, record):
+        apache_level = apache.APLOG_DEBUG
+        if record.levelno in ApacheLogHandler._LEVELS:
+            apache_level = ApacheLogHandler._LEVELS[record.levelno]
+        self.log_error(record.getMessage(), apache_level)
+
+
+logging.getLogger("mod_pywebsocket").addHandler(ApacheLogHandler())
+
 
 def _create_dispatcher():
     _HANDLER_ROOT = apache.main_server.get_options().get(
@@ -80,11 +110,20 @@
     """
 
     try:
-        handshaker = handshake.Handshaker(request, _dispatcher)
+        allowDraft75 = apache.main_server.get_options().get(
+		_PYOPT_ALLOW_DRAFT75, None)
+        handshaker = handshake.Handshaker(request, _dispatcher,
+                                          allowDraft75=allowDraft75)
         handshaker.do_handshake()
         request.log_error('mod_pywebsocket: resource: %r' % request.ws_resource,
                           apache.APLOG_DEBUG)
-        _dispatcher.transfer_data(request)
+        try:
+            _dispatcher.transfer_data(request)
+        except Exception, e:
+            # Catch exception in transfer_data.
+            # In this case, handshake has been successful, so just log the
+            # exception and return apache.DONE
+            request.log_error('mod_pywebsocket: %s' % e, apache.APLOG_WARNING)
     except handshake.HandshakeError, e:
         # Handshake for ws/wss failed.
         # But the request can be valid http/https request.
diff --git a/src/setup.py b/src/setup.py
index a34a83b..80e6895 100755
--- a/src/setup.py
+++ b/src/setup.py
@@ -54,7 +54,7 @@
               'See mod_pywebsocket/__init__.py for more detail.'),
       license='See COPYING',
       name=_PACKAGE_NAME,
-      packages=[_PACKAGE_NAME],
+      packages=[_PACKAGE_NAME, _PACKAGE_NAME + '.handshake'],
       url='http://code.google.com/p/pywebsocket/',
       version='0.4.9.2',
       )