don't use autoinstall to import pywebsocket but check it in WebKit directly.
[WebKit-https.git] / Tools / Scripts / webkitpy / thirdparty / mod_pywebsocket / extensions.py
1 # Copyright 2011, Google Inc.
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 #     * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 #     * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 #     * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31 from mod_pywebsocket import common
32 from mod_pywebsocket import util
33
34
35 _available_processors = {}
36
37
38 class ExtensionProcessorInterface(object):
39
40     def get_extension_response(self):
41         return None
42
43     def setup_stream_options(self, stream_options):
44         pass
45
46
47 class DeflateStreamExtensionProcessor(ExtensionProcessorInterface):
48     """WebSocket DEFLATE stream extension processor."""
49
50     def __init__(self, request):
51         self._logger = util.get_class_logger(self)
52
53         self._request = request
54
55     def get_extension_response(self):
56         if len(self._request.get_parameter_names()) != 0:
57             return None
58
59         self._logger.debug(
60             'Enable %s extension', common.DEFLATE_STREAM_EXTENSION)
61
62         return common.ExtensionParameter(common.DEFLATE_STREAM_EXTENSION)
63
64     def setup_stream_options(self, stream_options):
65         stream_options.deflate_stream = True
66
67
68 _available_processors[common.DEFLATE_STREAM_EXTENSION] = (
69     DeflateStreamExtensionProcessor)
70
71
72 class DeflateFrameExtensionProcessor(ExtensionProcessorInterface):
73     """WebSocket Per-frame DEFLATE extension processor."""
74
75     _WINDOW_BITS_PARAM = 'max_window_bits'
76     _NO_CONTEXT_TAKEOVER_PARAM = 'no_context_takeover'
77
78     def __init__(self, request):
79         self._logger = util.get_class_logger(self)
80
81         self._request = request
82
83         self._response_window_bits = None
84         self._response_no_context_takeover = False
85
86         # Counters for statistics.
87
88         # Total number of outgoing bytes supplied to this filter.
89         self._total_outgoing_payload_bytes = 0
90         # Total number of bytes sent to the network after applying this filter.
91         self._total_filtered_outgoing_payload_bytes = 0
92
93         # Total number of bytes received from the network.
94         self._total_incoming_payload_bytes = 0
95         # Total number of incoming bytes obtained after applying this filter.
96         self._total_filtered_incoming_payload_bytes = 0
97
98     def get_extension_response(self):
99         # Any unknown parameter will be just ignored.
100
101         window_bits = self._request.get_parameter_value(
102             self._WINDOW_BITS_PARAM)
103         no_context_takeover = self._request.has_parameter(
104             self._NO_CONTEXT_TAKEOVER_PARAM)
105         if (no_context_takeover and
106             self._request.get_parameter_value(
107                 self._NO_CONTEXT_TAKEOVER_PARAM) is not None):
108             return None
109
110         if window_bits is not None:
111             try:
112                 window_bits = int(window_bits)
113             except ValueError, e:
114                 return None
115             if window_bits < 8 or window_bits > 15:
116                 return None
117
118         self._deflater = util._RFC1979Deflater(
119             window_bits, no_context_takeover)
120
121         self._inflater = util._RFC1979Inflater()
122
123         self._compress_outgoing = True
124
125         response = common.ExtensionParameter(self._request.name())
126
127         if self._response_window_bits is not None:
128             response.add_parameter(
129                 self._WINDOW_BITS_PARAM, str(self._response_window_bits))
130         if self._response_no_context_takeover:
131             response.add_parameter(
132                 self._NO_CONTEXT_TAKEOVER_PARAM, None)
133
134         self._logger.debug(
135             'Enable %s extension ('
136             'request: window_bits=%s; no_context_takeover=%r, '
137             'response: window_wbits=%s; no_context_takeover=%r)' %
138             (self._request.name(),
139              window_bits,
140              no_context_takeover,
141              self._response_window_bits,
142              self._response_no_context_takeover))
143
144         return response
145
146     def setup_stream_options(self, stream_options):
147
148         class _OutgoingFilter(object):
149
150             def __init__(self, parent):
151                 self._parent = parent
152
153             def filter(self, frame):
154                 self._parent._outgoing_filter(frame)
155
156         class _IncomingFilter(object):
157
158             def __init__(self, parent):
159                 self._parent = parent
160
161             def filter(self, frame):
162                 self._parent._incoming_filter(frame)
163
164         stream_options.outgoing_frame_filters.append(
165             _OutgoingFilter(self))
166         stream_options.incoming_frame_filters.insert(
167             0, _IncomingFilter(self))
168
169     def set_response_window_bits(self, value):
170         self._response_window_bits = value
171
172     def set_response_no_context_takeover(self, value):
173         self._response_no_context_takeover = value
174
175     def enable_outgoing_compression(self):
176         self._compress_outgoing = True
177
178     def disable_outgoing_compression(self):
179         self._compress_outgoing = False
180
181     def _outgoing_filter(self, frame):
182         """Transform outgoing frames. This method is called only by
183         an _OutgoingFilter instance.
184         """
185
186         original_payload_size = len(frame.payload)
187         self._total_outgoing_payload_bytes += original_payload_size
188
189         if (not self._compress_outgoing or
190             common.is_control_opcode(frame.opcode)):
191             self._total_filtered_outgoing_payload_bytes += (
192                 original_payload_size)
193             return
194
195         frame.payload = self._deflater.filter(frame.payload)
196         frame.rsv1 = 1
197
198         filtered_payload_size = len(frame.payload)
199         self._total_filtered_outgoing_payload_bytes += filtered_payload_size
200
201         # Print inf when ratio is not available.
202         ratio = float('inf')
203         average_ratio = float('inf')
204         if original_payload_size != 0:
205             ratio = float(filtered_payload_size) / original_payload_size
206         if self._total_outgoing_payload_bytes != 0:
207             average_ratio = (
208                 float(self._total_filtered_outgoing_payload_bytes) /
209                 self._total_outgoing_payload_bytes)
210         self._logger.debug(
211             'Outgoing compress ratio: %f (average: %f)' %
212             (ratio, average_ratio))
213
214     def _incoming_filter(self, frame):
215         """Transform incoming frames. This method is called only by
216         an _IncomingFilter instance.
217         """
218
219         received_payload_size = len(frame.payload)
220         self._total_incoming_payload_bytes += received_payload_size
221
222         if frame.rsv1 != 1 or common.is_control_opcode(frame.opcode):
223             self._total_filtered_incoming_payload_bytes += (
224                 received_payload_size)
225             return
226
227         frame.payload = self._inflater.filter(frame.payload)
228         frame.rsv1 = 0
229
230         filtered_payload_size = len(frame.payload)
231         self._total_filtered_incoming_payload_bytes += filtered_payload_size
232
233         # Print inf when ratio is not available.
234         ratio = float('inf')
235         average_ratio = float('inf')
236         if received_payload_size != 0:
237             ratio = float(received_payload_size) / filtered_payload_size
238         if self._total_filtered_incoming_payload_bytes != 0:
239             average_ratio = (
240                 float(self._total_incoming_payload_bytes) /
241                 self._total_filtered_incoming_payload_bytes)
242         self._logger.debug(
243             'Incoming compress ratio: %f (average: %f)' %
244             (ratio, average_ratio))
245
246
247 _available_processors[common.DEFLATE_FRAME_EXTENSION] = (
248     DeflateFrameExtensionProcessor)
249
250
251 # Adding vendor-prefixed deflate-frame extension.
252 # TODO(bashi): Remove this after WebKit stops using vender prefix.
253 _available_processors[common.X_WEBKIT_DEFLATE_FRAME_EXTENSION] = (
254     DeflateFrameExtensionProcessor)
255
256
257 def get_extension_processor(extension_request):
258     global _available_processors
259     processor_class = _available_processors.get(extension_request.name())
260     if processor_class is None:
261         return None
262     return processor_class(extension_request)
263
264
265 # vi:sts=4 sw=4 et