Diff
Modified: trunk/Tools/ChangeLog (120314 => 120315)
--- trunk/Tools/ChangeLog 2012-06-14 12:18:15 UTC (rev 120314)
+++ trunk/Tools/ChangeLog 2012-06-14 12:40:32 UTC (rev 120315)
@@ -1,3 +1,56 @@
+2012-06-14 Takashi Toyoshima <[email protected]>
+
+ Update pywebsocket to 0.7.6 from 0.7.4
+ https://bugs.webkit.org/show_bug.cgi?id=88975
+
+ Reviewed by Kent Tamura.
+
+ This new version of pywebsocket introduce following features.
+ - Allow handlers to send a close frame with empty body in response of
+ a client initiated closing handshake
+ - Implement perframe compression extension
+ - Support client certificate authentication
+
+ * Scripts/webkitpy/thirdparty/mod_pywebsocket/COPYING:
+ * Scripts/webkitpy/thirdparty/mod_pywebsocket/_stream_hybi.py:
+ (_create_control_frame):
+ (Stream._receive_frame):
+ (Stream.send_message):
+ (Stream.receive_message):
+ (Stream._send_closing_handshake):
+ (Stream.close_connection):
+ * Scripts/webkitpy/thirdparty/mod_pywebsocket/common.py:
+ (ExtensionParsingException):
+ (ExtensionParsingException.__init__):
+ (_parse_extension_param):
+ (_parse_extension):
+ (parse_extensions):
+ (format_extension):
+ (format_extensions):
+ * Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py:
+ (_parse_compression_method):
+ (_create_accepted_method_desc):
+ (PerFrameCompressionExtensionProcessor):
+ (PerFrameCompressionExtensionProcessor.__init__):
+ (PerFrameCompressionExtensionProcessor._lookup_compression_processor):
+ (PerFrameCompressionExtensionProcessor._get_compression_processor_response):
+ (PerFrameCompressionExtensionProcessor.get_extension_response):
+ (PerFrameCompressionExtensionProcessor.setup_stream_options):
+ (PerFrameCompressionExtensionProcessor.get_compression_processor):
+ * Scripts/webkitpy/thirdparty/mod_pywebsocket/handshake/_base.py:
+ (parse_token_list):
+ * Scripts/webkitpy/thirdparty/mod_pywebsocket/handshake/hybi.py:
+ (Handshaker._parse_extensions):
+ (Handshaker._send_handshake):
+ * Scripts/webkitpy/thirdparty/mod_pywebsocket/standalone.py:
+ (WebSocketServer._create_sockets):
+ (_get_logger_from_class):
+ (_configure_logging):
+ (_build_option_parser):
+ (_main.if):
+ * Scripts/webkitpy/thirdparty/mod_pywebsocket/util.py:
+ (RepeatedXorMasker.mask):
+
2012-06-14 Zoltan Horvath <[email protected]>
[Qt] Remove USE(QT_IMAGE_DECODER) macro, since we don't use it anymore
Modified: trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/COPYING (120314 => 120315)
--- trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/COPYING 2012-06-14 12:18:15 UTC (rev 120314)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/COPYING 2012-06-14 12:40:32 UTC (rev 120315)
@@ -1,4 +1,4 @@
-Copyright 2009, Google Inc.
+Copyright 2012, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
Modified: trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/_stream_hybi.py (120314 => 120315)
--- trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/_stream_hybi.py 2012-06-14 12:18:15 UTC (rev 120314)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/_stream_hybi.py 2012-06-14 12:40:32 UTC (rev 120315)
@@ -39,6 +39,7 @@
from collections import deque
import os
import struct
+import time
from mod_pywebsocket import common
from mod_pywebsocket import util
@@ -208,9 +209,19 @@
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)
@@ -286,6 +297,9 @@
InvalidFrameException: when the frame contains invalid data.
"""
+ self._logger.log(common.LOGLEVEL_FINE,
+ 'Receive the first 2 octets of a frame')
+
received = self.receive_bytes(2)
first_byte = ord(received[0])
@@ -299,6 +313,11 @@
mask = (second_byte >> 7) & 1
payload_length = second_byte & 0x7f
+ self._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) != self._options.unmask_receive:
raise InvalidFrameException(
'Mask bit on the received frame did\'nt match masking '
@@ -310,6 +329,9 @@
valid_length_encoding = True
length_encoding_bytes = 1
if payload_length == 127:
+ self._logger.log(common.LOGLEVEL_FINE,
+ 'Receive 8-octet extended payload length')
+
extended_payload_length = self.receive_bytes(8)
payload_length = struct.unpack(
'!Q', extended_payload_length)[0]
@@ -319,7 +341,13 @@
if self._request.ws_version >= 13 and payload_length < 0x10000:
valid_length_encoding = False
length_encoding_bytes = 8
+
+ self._logger.log(common.LOGLEVEL_FINE,
+ 'Decoded_payload_length=%s', payload_length)
elif payload_length == 126:
+ self._logger.log(common.LOGLEVEL_FINE,
+ 'Receive 2-octet extended payload length')
+
extended_payload_length = self.receive_bytes(2)
payload_length = struct.unpack(
'!H', extended_payload_length)[0]
@@ -327,6 +355,9 @@
valid_length_encoding = False
length_encoding_bytes = 2
+ self._logger.log(common.LOGLEVEL_FINE,
+ 'Decoded_payload_length=%s', payload_length)
+
if not valid_length_encoding:
self._logger.warning(
'Payload length is not encoded using the minimal number of '
@@ -335,13 +366,39 @@
length_encoding_bytes)
if mask == 1:
+ self._logger.log(common.LOGLEVEL_FINE, 'Receive mask')
+
masking_nonce = self.receive_bytes(4)
masker = util.RepeatedXorMasker(masking_nonce)
+
+ self._logger.log(common.LOGLEVEL_FINE, 'Mask=%r', masking_nonce)
else:
masker = _NOOP_MASKER
- bytes = masker.mask(self.receive_bytes(payload_length))
+ self._logger.log(common.LOGLEVEL_FINE, 'Receive payload data')
+ if self._logger.isEnabledFor(common.LOGLEVEL_FINE):
+ receive_start = time.time()
+ raw_payload_bytes = self.receive_bytes(payload_length)
+
+ if self._logger.isEnabledFor(common.LOGLEVEL_FINE):
+ self._logger.log(
+ common.LOGLEVEL_FINE,
+ 'Done receiving payload data at %s MB/s',
+ payload_length / (time.time() - receive_start) / 1000 / 1000)
+ self._logger.log(common.LOGLEVEL_FINE, 'Unmask payload data')
+
+ if self._logger.isEnabledFor(common.LOGLEVEL_FINE):
+ unmask_start = time.time()
+
+ bytes = masker.mask(raw_payload_bytes)
+
+ if self._logger.isEnabledFor(common.LOGLEVEL_FINE):
+ self._logger.log(
+ common.LOGLEVEL_FINE,
+ 'Done unmasking payload data at %s MB/s',
+ payload_length / (time.time() - unmask_start) / 1000 / 1000)
+
return opcode, bytes, fin, rsv1, rsv2, rsv3
def _receive_frame_as_frame_object(self):
@@ -359,8 +416,8 @@
Raises:
BadOperationException: when called on a server-terminated
- connection or called with inconsistent message type or binary
- parameter.
+ connection or called with inconsistent message type or
+ binary parameter.
"""
if self._request.server_terminated:
@@ -408,6 +465,15 @@
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)
@@ -450,12 +516,6 @@
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:
@@ -488,7 +548,11 @@
# - no application data: no code no reason
# - 2 octet of application data: has code but no reason
# - 3 or more octet of application data: both code and reason
- if len(message) == 1:
+ 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')
@@ -507,8 +571,7 @@
if self._request.server_terminated:
self._logger.debug(
- 'Received ack for server-initiated closing '
- 'handshake')
+ 'Received ack for server-initiated closing handshake')
return None
self._logger.debug(
@@ -520,9 +583,16 @@
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')
+ 'Sent ack for client-initiated closing handshake '
+ '(code=%r, reason=%r)', code, reason)
return None
elif self._original_opcode == common.OPCODE_PING:
try:
@@ -571,17 +641,21 @@
'Opcode %d is not supported' % self._original_opcode)
def _send_closing_handshake(self, code, reason):
- if code >= (1 << 16) or code < 0:
- raise BadOperationException('Status code is out of range')
+ 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
- encoded_reason = reason.encode('utf-8')
- if len(encoded_reason) + 2 > 125:
- raise BadOperationException(
- 'Application data size of close frames must be 125 bytes or '
- 'less')
-
frame = create_close_frame(
- struct.pack('!H', code) + encoded_reason,
+ body,
self._options.mask_send,
self._options.outgoing_frame_filters)
@@ -590,15 +664,36 @@
self._write(frame)
def close_connection(self, code=common.STATUS_NORMAL_CLOSURE, reason=''):
- """Closes a WebSocket connection."""
+ """Closes a WebSocket connection.
+ 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.
+ 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
+ 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')
+ self._logger.debug(
+ 'Sent server-initiated closing handshake (code=%r, reason=%r)',
+ code, reason)
if (code == common.STATUS_GOING_AWAY or
code == common.STATUS_PROTOCOL_ERROR):
@@ -621,10 +716,6 @@
# 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,
@@ -634,10 +725,6 @@
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,
Modified: trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/common.py (120314 => 120315)
--- trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/common.py 2012-06-14 12:18:15 UTC (rev 120314)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/common.py 2012-06-14 12:40:32 UTC (rev 120315)
@@ -28,6 +28,16 @@
# 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
@@ -93,6 +103,7 @@
# Extensions
DEFLATE_STREAM_EXTENSION = 'deflate-stream'
DEFLATE_FRAME_EXTENSION = 'deflate-frame'
+PERFRAME_COMPRESSION_EXTENSION = 'perframe-compress'
X_WEBKIT_DEFLATE_FRAME_EXTENSION = 'x-webkit-deflate-frame'
# Status codes
@@ -176,4 +187,118 @@
return param_value
+class ExtensionParsingException(Exception):
+ def __init__(self, name):
+ super(ExtensionParsingException, self).__init__(name)
+
+
+def _parse_extension_param(state, definition, allow_quoted_string):
+ 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)
+
+ 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 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, allow_quoted_string):
+ 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, allow_quoted_string)
+ except ExtensionParsingException, e:
+ raise ExtensionParsingException(
+ '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
+ 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 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
Modified: trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py (120314 => 120315)
--- trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py 2012-06-14 12:18:15 UTC (rev 120314)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py 2012-06-14 12:40:32 UTC (rev 120315)
@@ -1,4 +1,4 @@
-# Copyright 2011, Google Inc.
+# Copyright 2012, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -30,6 +30,7 @@
from mod_pywebsocket import common
from mod_pywebsocket import util
+from mod_pywebsocket.http_header_util import quote_if_necessary
_available_processors = {}
@@ -254,6 +255,96 @@
DeflateFrameExtensionProcessor)
+def _parse_compression_method(data):
+ """Parses the value of "method" extension parameter."""
+
+ return common.parse_extensions(data, allow_quoted_string=True)
+
+
+def _create_accepted_method_desc(method_name, method_params):
+ """Creates accepted-method-desc from given method name and parameters"""
+
+ extension = common.ExtensionParameter(method_name)
+ for name, value in method_params:
+ extension.add_parameter(name, value)
+ return common.format_extension(extension)
+
+
+class PerFrameCompressionExtensionProcessor(ExtensionProcessorInterface):
+ """WebSocket Per-frame compression extension processor."""
+
+ _METHOD_PARAM = 'method'
+ _DEFLATE_METHOD = 'deflate'
+
+ def __init__(self, request):
+ self._logger = util.get_class_logger(self)
+ self._request = request
+ self._compression_method_name = None
+ self._compression_processor = None
+
+ def _lookup_compression_processor(self, method_desc):
+ if method_desc.name() == self._DEFLATE_METHOD:
+ return DeflateFrameExtensionProcessor(method_desc)
+ return None
+
+ def _get_compression_processor_response(self):
+ """Looks up the compression processor based on the self._request and
+ returns the compression processor's response.
+ """
+
+ method_list = self._request.get_parameter_value(self._METHOD_PARAM)
+ if method_list is None:
+ return None
+ methods = _parse_compression_method(method_list)
+ if methods is None:
+ return None
+ comression_processor = None
+ # The current implementation tries only the first method that matches
+ # supported algorithm. Following methods aren't tried even if the
+ # first one is rejected.
+ # TODO(bashi): Need to clarify this behavior.
+ for method_desc in methods:
+ compression_processor = self._lookup_compression_processor(
+ method_desc)
+ if compression_processor is not None:
+ self._compression_method_name = method_desc.name()
+ break
+ if compression_processor is None:
+ return None
+ processor_response = compression_processor.get_extension_response()
+ if processor_response is None:
+ return None
+ self._compression_processor = compression_processor
+ return processor_response
+
+ def get_extension_response(self):
+ processor_response = self._get_compression_processor_response()
+ if processor_response is None:
+ return None
+
+ response = common.ExtensionParameter(self._request.name())
+ accepted_method_desc = _create_accepted_method_desc(
+ self._compression_method_name,
+ processor_response.get_parameters())
+ response.add_parameter(self._METHOD_PARAM, accepted_method_desc)
+ self._logger.debug(
+ 'Enable %s extension (method: %s)' %
+ (self._request.name(), self._compression_method_name))
+ return response
+
+ def setup_stream_options(self, stream_options):
+ if self._compression_processor is None:
+ return
+ self._compression_processor.setup_stream_options(stream_options)
+
+ def get_compression_processor(self):
+ return self._compression_processor
+
+
+_available_processors[common.PERFRAME_COMPRESSION_EXTENSION] = (
+ PerFrameCompressionExtensionProcessor)
+
+
def get_extension_processor(extension_request):
global _available_processors
processor_class = _available_processors.get(extension_request.name())
Modified: trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/handshake/_base.py (120314 => 120315)
--- trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/handshake/_base.py 2012-06-14 12:18:15 UTC (rev 120314)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/handshake/_base.py 2012-06-14 12:40:32 UTC (rev 120315)
@@ -1,4 +1,4 @@
-# Copyright 2011, Google Inc.
+# Copyright 2012, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -216,108 +216,4 @@
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
Modified: trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/handshake/hybi.py (120314 => 120315)
--- trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/handshake/hybi.py 2012-06-14 12:18:15 UTC (rev 120314)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/handshake/hybi.py 2012-06-14 12:40:32 UTC (rev 120315)
@@ -1,4 +1,4 @@
-# Copyright 2011, Google Inc.
+# Copyright 2012, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -50,11 +50,9 @@
from mod_pywebsocket import common
from mod_pywebsocket.extensions import get_extension_processor
from mod_pywebsocket.handshake._base import check_request_line
-from mod_pywebsocket.handshake._base import format_extensions
from mod_pywebsocket.handshake._base import format_header
from mod_pywebsocket.handshake._base import get_mandatory_header
from mod_pywebsocket.handshake._base import HandshakeException
-from mod_pywebsocket.handshake._base import parse_extensions
from mod_pywebsocket.handshake._base import parse_token_list
from mod_pywebsocket.handshake._base import validate_mandatory_header
from mod_pywebsocket.handshake._base import validate_subprotocol
@@ -290,8 +288,12 @@
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, allow_quoted_string=allow_quoted_string)
+ except common.ExtensionParsingException, e:
+ raise HandshakeException(
+ 'Failed to parse Sec-WebSocket-Extensions header: %r' % e)
self._logger.debug(
'Extensions requested: %r',
@@ -358,7 +360,7 @@
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)))
response.append('\r\n')
raw_response = ''.join(response)
Modified: trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/standalone.py (120314 => 120315)
--- trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/standalone.py 2012-06-14 12:18:15 UTC (rev 120314)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/standalone.py 2012-06-14 12:40:32 UTC (rev 120315)
@@ -1,6 +1,6 @@
#!/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
@@ -55,6 +55,20 @@
handlers under scan_dir are scanned. This is useful in saving scan time.
+SUPPORTING TLS
+
+To support TLS, run standalone.py with -t, -k, and -c options.
+
+
+SUPPORTING CLIENT AUTHENTICATION
+
+To support client authentication with TLS, run standalone.py with -t, -k, -c,
+and --ca-certificate options.
+
+E.g., $./standalone.py -d ../example -p 10443 -t -c ../test/cert/cert.pem -k
+../test/cert/key.pem --ca-certificate=../test/cert/cacert.pem
+
+
CONFIGURATION FILE
You can also write a configuration file and use it by specifying the path to
@@ -311,10 +325,16 @@
continue
if self.websocket_server_options.use_tls:
if _HAS_SSL:
+ if self.websocket_server_options.ca_certificate:
+ 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)
+ ssl_version=ssl.PROTOCOL_SSLv23,
+ ca_certs=self.websocket_server_options.ca_certificate,
+ cert_reqs=client_cert_)
if _HAS_OPEN_SSL:
ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
ctx.use_privatekey_file(
@@ -601,7 +621,13 @@
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:
@@ -614,7 +640,14 @@
handler.setFormatter(formatter)
logger.addHandler(handler)
+ 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 _alias_handlers(dispatcher, websock_handlers_map_file):
"""Set aliases specified in websock_handler_map_file in dispatcher.
@@ -702,13 +735,25 @@
default='', help='TLS private key file.')
parser.add_option('-c', '--certificate', dest='certificate',
default='', help='TLS certificate file.')
+ parser.add_option('--ca-certificate', dest='ca_certificate', default='',
+ help=('TLS CA certificate file for client '
+ 'authentication.'))
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',
@@ -825,13 +870,20 @@
if options.use_tls:
if not (_HAS_SSL or _HAS_OPEN_SSL):
- logging.critical('TLS support requires ssl or pyOpenSSL.')
+ logging.critical('TLS support requires ssl or pyOpenSSL 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.ca_certificate:
+ if not options.use_tls:
+ logging.critical('TLS must be enabled for client authentication.')
+ sys.exit(1)
+ if not _HAS_SSL:
+ logging.critical('Client authentication requires ssl module.')
+
if not options.scan_dir:
options.scan_dir = options.websock_handlers
Modified: trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/util.py (120314 => 120315)
--- trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/util.py 2012-06-14 12:18:15 UTC (rev 120314)
+++ trunk/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/util.py 2012-06-14 12:40:32 UTC (rev 120315)
@@ -177,9 +177,16 @@
def mask(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
for i in xrange(len(result)):
- result[i] ^= self._mask[self._count]
- self._count = (self._count + 1) % self._mask_size
+ result[i] ^= mask[count]
+ count = (count + 1) % mask_size
+ self._count = count
+
return result.tostring()