diff options
author | Allan Sandfeld Jensen <allan.jensen@digia.com> | 2013-09-13 12:51:20 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-19 20:50:05 +0200 |
commit | d441d6f39bb846989d95bcf5caf387b42414718d (patch) | |
tree | e367e64a75991c554930278175d403c072de6bb8 /Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py | |
parent | 0060b2994c07842f4c59de64b5e3e430525c4b90 (diff) | |
download | qtwebkit-d441d6f39bb846989d95bcf5caf387b42414718d.tar.gz |
Import Qt5x2 branch of QtWebkit for Qt 5.2
Importing a new snapshot of webkit.
Change-Id: I2d01ad12cdc8af8cb015387641120a9d7ea5f10c
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@digia.com>
Diffstat (limited to 'Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py')
-rw-r--r-- | Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py | 635 |
1 files changed, 401 insertions, 234 deletions
diff --git a/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py b/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py index 03dbf9ee1..552d2c072 100644 --- a/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py +++ b/Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/extensions.py @@ -34,83 +34,114 @@ from mod_pywebsocket.http_header_util import quote_if_necessary _available_processors = {} +_compression_extension_names = [] class ExtensionProcessorInterface(object): - def name(self): - return None + def __init__(self, request): + self._request = request + self._active = True - def get_extension_response(self): + def request(self): + return self._request + + def name(self): return None - def setup_stream_options(self, stream_options): + def check_consistency_with_other_processors(self, processors): pass + def set_active(self, active): + self._active = active -class DeflateStreamExtensionProcessor(ExtensionProcessorInterface): - """WebSocket DEFLATE stream extension processor. - - Specification: - Section 9.2.1 in - http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10 - """ - - def __init__(self, request): - self._logger = util.get_class_logger(self) - - self._request = request + def is_active(self): + return self._active - def name(self): - return common.DEFLATE_STREAM_EXTENSION + def _get_extension_response_internal(self): + return None def get_extension_response(self): - if len(self._request.get_parameter_names()) != 0: - return None - - self._logger.debug( - 'Enable %s extension', common.DEFLATE_STREAM_EXTENSION) + if self._active: + response = self._get_extension_response_internal() + if response is None: + self._active = False + return response + return None - 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) -_available_processors[common.DEFLATE_STREAM_EXTENSION] = ( - DeflateStreamExtensionProcessor) - - -def _log_compression_ratio(logger, original_bytes, total_original_bytes, - filtered_bytes, total_filtered_bytes): +def _log_outgoing_compression_ratio( + logger, original_bytes, filtered_bytes, average_ratio): # Print inf when ratio is not available. ratio = float('inf') - average_ratio = float('inf') if original_bytes != 0: ratio = float(filtered_bytes) / original_bytes - if total_original_bytes != 0: - average_ratio = ( - float(total_filtered_bytes) / total_original_bytes) - logger.debug('Outgoing compress ratio: %f (average: %f)' % - (ratio, average_ratio)) + logger.debug('Outgoing compression ratio: %f (average: %f)' % + (ratio, average_ratio)) -def _log_decompression_ratio(logger, received_bytes, total_received_bytes, - filtered_bytes, total_filtered_bytes): + +def _log_incoming_compression_ratio( + logger, received_bytes, filtered_bytes, average_ratio): # Print inf when ratio is not available. ratio = float('inf') - average_ratio = float('inf') - if received_bytes != 0: + if filtered_bytes != 0: ratio = float(received_bytes) / filtered_bytes - if total_filtered_bytes != 0: - average_ratio = ( - float(total_received_bytes) / total_filtered_bytes) - logger.debug('Incoming compress ratio: %f (average: %f)' % - (ratio, average_ratio)) + + logger.debug('Incoming compression ratio: %f (average: %f)' % + (ratio, average_ratio)) + + +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 @@ -120,34 +151,38 @@ class DeflateFrameExtensionProcessor(ExtensionProcessorInterface): _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. - - # 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 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 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 + # Calculates + # (Total bytes received from the network) / + # (Total incoming bytes obtained after applying this filter) + self._incoming_average_ratio_calculator = _AverageRatioCalculator() 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 @@ -155,18 +190,10 @@ class DeflateFrameExtensionProcessor(ExtensionProcessorInterface): 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 @@ -191,7 +218,7 @@ class DeflateFrameExtensionProcessor(ExtensionProcessorInterface): return response - def setup_stream_options(self, stream_options): + def _setup_stream_options_internal(self, stream_options): class _OutgoingFilter(object): @@ -235,25 +262,28 @@ class DeflateFrameExtensionProcessor(ExtensionProcessorInterface): """ 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 = 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) - _log_compression_ratio(self._logger, original_payload_size, - self._total_outgoing_payload_bytes, - filtered_payload_size, - self._total_filtered_outgoing_payload_bytes) + _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 @@ -261,33 +291,35 @@ class DeflateFrameExtensionProcessor(ExtensionProcessorInterface): """ 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) - _log_decompression_ratio(self._logger, received_payload_size, - self._total_incoming_payload_bytes, - filtered_payload_size, - self._total_filtered_incoming_payload_bytes) + _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) - -# Adding vendor-prefixed deflate-frame extension. -# TODO(bashi): Remove this after WebKit stops using vendor prefix. _available_processors[common.X_WEBKIT_DEFLATE_FRAME_EXTENSION] = ( DeflateFrameExtensionProcessor) +_compression_extension_names.append(common.X_WEBKIT_DEFLATE_FRAME_EXTENSION) def _parse_compression_method(data): @@ -306,13 +338,13 @@ def _create_accepted_method_desc(method_name, method_params): class CompressionExtensionProcessorBase(ExtensionProcessorInterface): - """Base class for Per-frame and Per-message compression extension.""" + """Base class for perframe-compress and permessage-compress extension.""" _METHOD_PARAM = 'method' def __init__(self, request): + ExtensionProcessorInterface.__init__(self, request) self._logger = util.get_class_logger(self) - self._request = request self._compression_method_name = None self._compression_processor = None self._compression_processor_hook = None @@ -357,7 +389,7 @@ class CompressionExtensionProcessorBase(ExtensionProcessorInterface): self._compression_processor = compression_processor return processor_response - def get_extension_response(self): + def _get_extension_response_internal(self): processor_response = self._get_compression_processor_response() if processor_response is None: return None @@ -372,7 +404,7 @@ class CompressionExtensionProcessorBase(ExtensionProcessorInterface): (self._request.name(), self._compression_method_name)) return response - def setup_stream_options(self, stream_options): + def _setup_stream_options_internal(self, stream_options): if self._compression_processor is None: return self._compression_processor.setup_stream_options(stream_options) @@ -384,8 +416,8 @@ class CompressionExtensionProcessorBase(ExtensionProcessorInterface): return self._compression_processor -class PerFrameCompressionExtensionProcessor(CompressionExtensionProcessorBase): - """WebSocket Per-frame compression extension processor. +class PerFrameCompressExtensionProcessor(CompressionExtensionProcessorBase): + """perframe-compress processor. Specification: http://tools.ietf.org/html/draft-ietf-hybi-websocket-perframe-compression @@ -406,56 +438,66 @@ class PerFrameCompressionExtensionProcessor(CompressionExtensionProcessorBase): _available_processors[common.PERFRAME_COMPRESSION_EXTENSION] = ( - PerFrameCompressionExtensionProcessor) + PerFrameCompressExtensionProcessor) +_compression_extension_names.append(common.PERFRAME_COMPRESSION_EXTENSION) -class DeflateMessageProcessor(ExtensionProcessorInterface): - """Per-message deflate processor.""" +class PerMessageDeflateExtensionProcessor(ExtensionProcessorInterface): + """permessage-deflate extension processor. It's also used for + permessage-compress extension when the deflate method is chosen. + + Specification: + http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-08 + """ _S2C_MAX_WINDOW_BITS_PARAM = 's2c_max_window_bits' _S2C_NO_CONTEXT_TAKEOVER_PARAM = 's2c_no_context_takeover' _C2S_MAX_WINDOW_BITS_PARAM = 'c2s_max_window_bits' _C2S_NO_CONTEXT_TAKEOVER_PARAM = 'c2s_no_context_takeover' - def __init__(self, request): - self._request = request + 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._c2s_max_window_bits = None self._c2s_no_context_takeover = False - self._bfinal = False - - self._compress_outgoing_enabled = False - # True if a message is fragmented and compression is ongoing. - self._compress_ongoing = False - - # Counters for statistics. - - # 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 - - # 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 + self._draft08 = draft08 def name(self): return 'deflate' - def get_extension_response(self): - # Any unknown parameter will be just ignored. + def _get_extension_response_internal(self): + if self._draft08: + for name in self._request.get_parameter_names(): + if name not in [self._S2C_MAX_WINDOW_BITS_PARAM, + self._S2C_NO_CONTEXT_TAKEOVER_PARAM, + self._C2S_MAX_WINDOW_BITS_PARAM]: + self._logger.debug('Unknown parameter: %r', name) + return None + else: + # Any unknown parameter will be just ignored. + pass - s2c_max_window_bits = self._request.get_parameter_value( - self._S2C_MAX_WINDOW_BITS_PARAM) - if s2c_max_window_bits is not None: + s2c_max_window_bits = None + if self._request.has_parameter(self._S2C_MAX_WINDOW_BITS_PARAM): + s2c_max_window_bits = self._request.get_parameter_value( + self._S2C_MAX_WINDOW_BITS_PARAM) try: - s2c_max_window_bits = int(s2c_max_window_bits) + s2c_max_window_bits = _parse_window_bits(s2c_max_window_bits) except ValueError, e: - return None - if s2c_max_window_bits < 8 or s2c_max_window_bits > 15: + self._logger.debug('Bad %s parameter: %r', + self._S2C_MAX_WINDOW_BITS_PARAM, + e) return None s2c_no_context_takeover = self._request.has_parameter( @@ -463,14 +505,32 @@ class DeflateMessageProcessor(ExtensionProcessorInterface): if (s2c_no_context_takeover and self._request.get_parameter_value( self._S2C_NO_CONTEXT_TAKEOVER_PARAM) is not None): + self._logger.debug('%s parameter must not have a value: %r', + self._S2C_NO_CONTEXT_TAKEOVER_PARAM, + s2c_no_context_takeover) return None - self._deflater = util._RFC1979Deflater( + c2s_max_window_bits = self._request.has_parameter( + self._C2S_MAX_WINDOW_BITS_PARAM) + if (self._draft08 and + c2s_max_window_bits and + self._request.get_parameter_value( + self._C2S_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._C2S_MAX_WINDOW_BITS_PARAM, + c2s_max_window_bits) + return None + + self._rfc1979_deflater = util._RFC1979Deflater( s2c_max_window_bits, s2c_no_context_takeover) - self._inflater = util._RFC1979Inflater() + self._rfc1979_inflater = util._RFC1979Inflater() - self._compress_outgoing_enabled = True + self._framer = _PerMessageDeflateFramer( + s2c_max_window_bits, s2c_no_context_takeover) + self._framer.set_bfinal(False) + self._framer.set_compress_outgoing_enabled(True) response = common.ExtensionParameter(self._request.name()) @@ -483,9 +543,15 @@ class DeflateMessageProcessor(ExtensionProcessorInterface): self._S2C_NO_CONTEXT_TAKEOVER_PARAM, None) if self._c2s_max_window_bits is not None: + if self._draft08 and c2s_max_window_bits: + self._logger.debug('Processor is configured to use %s but ' + 'the client cannot accept it', + self._C2S_MAX_WINDOW_BITS_PARAM) + return None response.add_parameter( self._C2S_MAX_WINDOW_BITS_PARAM, str(self._c2s_max_window_bits)) + if self._c2s_no_context_takeover: response.add_parameter( self._C2S_NO_CONTEXT_TAKEOVER_PARAM, None) @@ -502,100 +568,99 @@ class DeflateMessageProcessor(ExtensionProcessorInterface): return response - def setup_stream_options(self, stream_options): - class _OutgoingMessageFilter(object): - - def __init__(self, parent): - self._parent = parent + def _setup_stream_options_internal(self, stream_options): + self._framer.setup_stream_options(stream_options) - def filter(self, message, end=True, binary=False): - return self._parent._process_outgoing_message( - message, end, binary) + def set_c2s_max_window_bits(self, value): + """If this option is specified, this class adds the c2s_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 + c2s_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. + """ - class _IncomingMessageFilter(object): + self._c2s_max_window_bits = value - def __init__(self, parent): - self._parent = parent - self._decompress_next_message = False + def set_c2s_no_context_takeover(self, value): + """If this option is specified, this class adds the + c2s_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. + """ - def decompress_next_message(self): - self._decompress_next_message = True + self._c2s_no_context_takeover = value - def filter(self, message): - message = self._parent._process_incoming_message( - message, self._decompress_next_message) - self._decompress_next_message = False - return message + def set_bfinal(self, value): + self._framer.set_bfinal(value) - 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) + def enable_outgoing_compression(self): + self._framer.set_compress_outgoing_enabled(True) - class _OutgoingFrameFilter(object): + def disable_outgoing_compression(self): + self._framer.set_compress_outgoing_enabled(False) - def __init__(self, parent): - self._parent = parent - self._set_compression_bit = False - def set_compression_bit(self): - self._set_compression_bit = True +class _PerMessageDeflateFramer(object): + """A framer for extensions with per-message DEFLATE feature.""" - def filter(self, frame): - self._parent._process_outgoing_frame( - frame, self._set_compression_bit) - self._set_compression_bit = False + def __init__(self, deflate_max_window_bits, deflate_no_context_takeover): + self._logger = util.get_class_logger(self) - class _IncomingFrameFilter(object): + self._rfc1979_deflater = util._RFC1979Deflater( + deflate_max_window_bits, deflate_no_context_takeover) - def __init__(self, parent): - self._parent = parent + self._rfc1979_inflater = util._RFC1979Inflater() - def filter(self, frame): - self._parent._process_incoming_frame(frame) + self._bfinal = False - 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) + self._compress_outgoing_enabled = False - stream_options.encode_text_message_to_utf8 = False + # True if a message is fragmented and compression is ongoing. + self._compress_ongoing = False - def set_c2s_max_window_bits(self, value): - self._c2s_max_window_bits = value + # Calculates + # (Total outgoing bytes supplied to this filter) / + # (Total bytes sent to the network after applying this filter) + self._outgoing_average_ratio_calculator = _AverageRatioCalculator() - def set_c2s_no_context_takeover(self, value): - self._c2s_no_context_takeover = value + # 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 enable_outgoing_compression(self): - self._compress_outgoing_enabled = True - - def disable_outgoing_compression(self): - self._compress_outgoing_enabled = False + 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._total_incoming_payload_bytes += received_payload_size + self._incoming_average_ratio_calculator.add_result_bytes( + received_payload_size) - message = self._inflater.filter(message) + message = self._rfc1979_inflater.filter(message) filtered_payload_size = len(message) - self._total_filtered_incoming_payload_bytes += filtered_payload_size + self._incoming_average_ratio_calculator.add_original_bytes( + filtered_payload_size) - _log_decompression_ratio(self._logger, received_payload_size, - self._total_incoming_payload_bytes, - filtered_payload_size, - self._total_filtered_incoming_payload_bytes) + _log_incoming_compression_ratio( + self._logger, + received_payload_size, + filtered_payload_size, + self._incoming_average_ratio_calculator.get_average_ratio()) return message @@ -607,18 +672,21 @@ class DeflateMessageProcessor(ExtensionProcessorInterface): return message original_payload_size = len(message) - self._total_outgoing_payload_bytes += original_payload_size + self._outgoing_average_ratio_calculator.add_original_bytes( + original_payload_size) - message = self._deflater.filter( + message = self._rfc1979_deflater.filter( message, flush=end, bfinal=self._bfinal) filtered_payload_size = len(message) - self._total_filtered_outgoing_payload_bytes += filtered_payload_size + self._outgoing_average_ratio_calculator.add_result_bytes( + filtered_payload_size) - _log_compression_ratio(self._logger, original_payload_size, - self._total_outgoing_payload_bytes, - filtered_payload_size, - self._total_filtered_outgoing_payload_bytes) + _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() @@ -637,10 +705,81 @@ class DeflateMessageProcessor(ExtensionProcessorInterface): 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 + -class PerMessageCompressionExtensionProcessor( +_available_processors[common.PERMESSAGE_DEFLATE_EXTENSION] = ( + PerMessageDeflateExtensionProcessor) +# TODO(tyoshino): Reorganize class names. +_compression_extension_names.append('deflate') + + +class PerMessageCompressExtensionProcessor( CompressionExtensionProcessorBase): - """WebSocket Per-message compression extension processor. + """permessage-compress extension processor. Specification: http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression @@ -656,18 +795,13 @@ class PerMessageCompressionExtensionProcessor( def _lookup_compression_processor(self, method_desc): if method_desc.name() == self._DEFLATE_METHOD: - return DeflateMessageProcessor(method_desc) + return PerMessageDeflateExtensionProcessor(method_desc, False) return None _available_processors[common.PERMESSAGE_COMPRESSION_EXTENSION] = ( - PerMessageCompressionExtensionProcessor) - - -# Adding vendor-prefixed permessage-compress extension. -# TODO(bashi): Remove this after WebKit stops using vendor prefix. -_available_processors[common.X_WEBKIT_PERMESSAGE_COMPRESSION_EXTENSION] = ( - PerMessageCompressionExtensionProcessor) + PerMessageCompressExtensionProcessor) +_compression_extension_names.append(common.PERMESSAGE_COMPRESSION_EXTENSION) class MuxExtensionProcessor(ExtensionProcessorInterface): @@ -676,52 +810,85 @@ class MuxExtensionProcessor(ExtensionProcessorInterface): _QUOTA_PARAM = 'quota' def __init__(self, request): - self._request = request + ExtensionProcessorInterface.__init__(self, request) + self._quota = 0 + self._extensions = [] def name(self): return common.MUX_EXTENSION - def get_extension_response(self, ws_request, - logical_channel_extensions): - # 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. - for extension in logical_channel_extensions: - name = extension.name() - if (name == common.PERFRAME_COMPRESSION_EXTENSION or - name == common.DEFLATE_FRAME_EXTENSION or - name == common.X_WEBKIT_DEFLATE_FRAME_EXTENSION): - return None - + 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.PERFRAME_COMPRESSION_EXTENSION or + 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 == common.PERFRAME_COMPRESSION_EXTENSION or + name == common.DEFLATE_FRAME_EXTENSION or + name == common.X_WEBKIT_DEFLATE_FRAME_EXTENSION or + name == common.PERMESSAGE_COMPRESSION_EXTENSION or + name == common.X_WEBKIT_PERMESSAGE_COMPRESSION_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 None: - ws_request.mux_quota = 0 - else: + if quota is not None: try: quota = int(quota) except ValueError, e: return None if quota < 0 or quota >= 2 ** 32: return None - ws_request.mux_quota = quota + self._quota = quota - ws_request.mux = True - ws_request.mux_extensions = logical_channel_extensions + self._active = True return common.ExtensionParameter(common.MUX_EXTENSION) - def setup_stream_options(self, stream_options): + 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 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 |