michael@0: # Copyright 2011, Google Inc. michael@0: # All rights reserved. michael@0: # michael@0: # Redistribution and use in source and binary forms, with or without michael@0: # modification, are permitted provided that the following conditions are michael@0: # met: michael@0: # michael@0: # * Redistributions of source code must retain the above copyright michael@0: # notice, this list of conditions and the following disclaimer. michael@0: # * Redistributions in binary form must reproduce the above michael@0: # copyright notice, this list of conditions and the following disclaimer michael@0: # in the documentation and/or other materials provided with the michael@0: # distribution. michael@0: # * Neither the name of Google Inc. nor the names of its michael@0: # contributors may be used to endorse or promote products derived from michael@0: # this software without specific prior written permission. michael@0: # michael@0: # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: michael@0: """WebSocket opening handshake processor. This class try to apply available michael@0: opening handshake processors for each protocol version until a connection is michael@0: successfully established. michael@0: """ michael@0: michael@0: michael@0: import logging michael@0: michael@0: from mod_pywebsocket import common michael@0: from mod_pywebsocket.handshake import draft75 michael@0: from mod_pywebsocket.handshake import hybi00 michael@0: from mod_pywebsocket.handshake import hybi michael@0: # Export AbortedByUserException, HandshakeException, and VersionException michael@0: # symbol from this module. michael@0: from mod_pywebsocket.handshake._base import AbortedByUserException michael@0: from mod_pywebsocket.handshake._base import HandshakeException michael@0: from mod_pywebsocket.handshake._base import VersionException michael@0: michael@0: michael@0: _LOGGER = logging.getLogger(__name__) michael@0: michael@0: michael@0: def do_handshake(request, dispatcher, allowDraft75=False, strict=False): michael@0: """Performs WebSocket handshake. michael@0: michael@0: Args: michael@0: request: mod_python request. michael@0: dispatcher: Dispatcher (dispatch.Dispatcher). michael@0: allowDraft75: allow draft 75 handshake protocol. michael@0: strict: Strictly check handshake request in draft 75. michael@0: Default: False. If True, request.connection must provide michael@0: get_memorized_lines method. michael@0: michael@0: Handshaker will add attributes such as ws_resource in performing michael@0: handshake. michael@0: """ michael@0: michael@0: _LOGGER.debug('Client\'s opening handshake resource: %r', request.uri) michael@0: # To print mimetools.Message as escaped one-line string, we converts michael@0: # headers_in to dict object. Without conversion, if we use %r, it just michael@0: # prints the type and address, and if we use %s, it prints the original michael@0: # header string as multiple lines. michael@0: # michael@0: # Both mimetools.Message and MpTable_Type of mod_python can be michael@0: # converted to dict. michael@0: # michael@0: # mimetools.Message.__str__ returns the original header string. michael@0: # dict(mimetools.Message object) returns the map from header names to michael@0: # header values. While MpTable_Type doesn't have such __str__ but just michael@0: # __repr__ which formats itself as well as dictionary object. michael@0: _LOGGER.debug( michael@0: 'Client\'s opening handshake headers: %r', dict(request.headers_in)) michael@0: michael@0: handshakers = [] michael@0: handshakers.append( michael@0: ('RFC 6455', hybi.Handshaker(request, dispatcher))) michael@0: handshakers.append( michael@0: ('HyBi 00', hybi00.Handshaker(request, dispatcher))) michael@0: if allowDraft75: michael@0: handshakers.append( michael@0: ('Hixie 75', draft75.Handshaker(request, dispatcher, strict))) michael@0: michael@0: for name, handshaker in handshakers: michael@0: _LOGGER.debug('Trying protocol version %s', name) michael@0: try: michael@0: handshaker.do_handshake() michael@0: _LOGGER.info('Established (%s protocol)', name) michael@0: return michael@0: except HandshakeException, e: michael@0: _LOGGER.debug( michael@0: 'Failed to complete opening handshake as %s protocol: %r', michael@0: name, e) michael@0: if e.status: michael@0: raise e michael@0: except AbortedByUserException, e: michael@0: raise michael@0: except VersionException, e: michael@0: raise michael@0: michael@0: # TODO(toyoshim): Add a test to cover the case all handshakers fail. michael@0: raise HandshakeException( michael@0: 'Failed to complete opening handshake for all available protocols', michael@0: status=common.HTTP_STATUS_BAD_REQUEST) michael@0: michael@0: michael@0: # vi:sts=4 sw=4 et