~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Tools/Scripts/webkitpy/thirdparty/mod_pywebsocket/common.py

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2012, 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
"""This file must not depend on any module specific to the WebSocket protocol.
 
32
"""
 
33
 
 
34
 
 
35
from mod_pywebsocket import http_header_util
 
36
 
 
37
 
 
38
# Additional log level definitions.
 
39
LOGLEVEL_FINE = 9
 
40
 
 
41
# Constants indicating WebSocket protocol version.
 
42
VERSION_HIXIE75 = -1
 
43
VERSION_HYBI00 = 0
 
44
VERSION_HYBI01 = 1
 
45
VERSION_HYBI02 = 2
 
46
VERSION_HYBI03 = 2
 
47
VERSION_HYBI04 = 4
 
48
VERSION_HYBI05 = 5
 
49
VERSION_HYBI06 = 6
 
50
VERSION_HYBI07 = 7
 
51
VERSION_HYBI08 = 8
 
52
VERSION_HYBI09 = 8
 
53
VERSION_HYBI10 = 8
 
54
VERSION_HYBI11 = 8
 
55
VERSION_HYBI12 = 8
 
56
VERSION_HYBI13 = 13
 
57
VERSION_HYBI14 = 13
 
58
VERSION_HYBI15 = 13
 
59
VERSION_HYBI16 = 13
 
60
VERSION_HYBI17 = 13
 
61
 
 
62
# Constants indicating WebSocket protocol latest version.
 
63
VERSION_HYBI_LATEST = VERSION_HYBI13
 
64
 
 
65
# Port numbers
 
66
DEFAULT_WEB_SOCKET_PORT = 80
 
67
DEFAULT_WEB_SOCKET_SECURE_PORT = 443
 
68
 
 
69
# Schemes
 
70
WEB_SOCKET_SCHEME = 'ws'
 
71
WEB_SOCKET_SECURE_SCHEME = 'wss'
 
72
 
 
73
# Frame opcodes defined in the spec.
 
74
OPCODE_CONTINUATION = 0x0
 
75
OPCODE_TEXT = 0x1
 
76
OPCODE_BINARY = 0x2
 
77
OPCODE_CLOSE = 0x8
 
78
OPCODE_PING = 0x9
 
79
OPCODE_PONG = 0xa
 
80
 
 
81
# UUIDs used by HyBi 04 and later opening handshake and frame masking.
 
82
WEBSOCKET_ACCEPT_UUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
 
83
 
 
84
# Opening handshake header names and expected values.
 
85
UPGRADE_HEADER = 'Upgrade'
 
86
WEBSOCKET_UPGRADE_TYPE = 'websocket'
 
87
WEBSOCKET_UPGRADE_TYPE_HIXIE75 = 'WebSocket'
 
88
CONNECTION_HEADER = 'Connection'
 
89
UPGRADE_CONNECTION_TYPE = 'Upgrade'
 
90
HOST_HEADER = 'Host'
 
91
ORIGIN_HEADER = 'Origin'
 
92
SEC_WEBSOCKET_ORIGIN_HEADER = 'Sec-WebSocket-Origin'
 
93
SEC_WEBSOCKET_KEY_HEADER = 'Sec-WebSocket-Key'
 
94
SEC_WEBSOCKET_ACCEPT_HEADER = 'Sec-WebSocket-Accept'
 
95
SEC_WEBSOCKET_VERSION_HEADER = 'Sec-WebSocket-Version'
 
96
SEC_WEBSOCKET_PROTOCOL_HEADER = 'Sec-WebSocket-Protocol'
 
97
SEC_WEBSOCKET_EXTENSIONS_HEADER = 'Sec-WebSocket-Extensions'
 
98
SEC_WEBSOCKET_DRAFT_HEADER = 'Sec-WebSocket-Draft'
 
99
SEC_WEBSOCKET_KEY1_HEADER = 'Sec-WebSocket-Key1'
 
100
SEC_WEBSOCKET_KEY2_HEADER = 'Sec-WebSocket-Key2'
 
101
SEC_WEBSOCKET_LOCATION_HEADER = 'Sec-WebSocket-Location'
 
102
 
 
103
# Extensions
 
104
DEFLATE_STREAM_EXTENSION = 'deflate-stream'
 
105
DEFLATE_FRAME_EXTENSION = 'deflate-frame'
 
106
PERFRAME_COMPRESSION_EXTENSION = 'perframe-compress'
 
107
PERMESSAGE_COMPRESSION_EXTENSION = 'permessage-compress'
 
108
X_WEBKIT_DEFLATE_FRAME_EXTENSION = 'x-webkit-deflate-frame'
 
109
X_WEBKIT_PERMESSAGE_COMPRESSION_EXTENSION = 'x-webkit-permessage-compress'
 
110
MUX_EXTENSION = 'mux_DO_NOT_USE'
 
111
 
 
112
# Status codes
 
113
# Code STATUS_NO_STATUS_RECEIVED, STATUS_ABNORMAL_CLOSURE, and
 
114
# STATUS_TLS_HANDSHAKE are pseudo codes to indicate specific error cases.
 
115
# Could not be used for codes in actual closing frames.
 
116
# Application level errors must use codes in the range
 
117
# STATUS_USER_REGISTERED_BASE to STATUS_USER_PRIVATE_MAX. The codes in the
 
118
# range STATUS_USER_REGISTERED_BASE to STATUS_USER_REGISTERED_MAX are managed
 
119
# by IANA. Usually application must define user protocol level errors in the
 
120
# range STATUS_USER_PRIVATE_BASE to STATUS_USER_PRIVATE_MAX.
 
121
STATUS_NORMAL_CLOSURE = 1000
 
122
STATUS_GOING_AWAY = 1001
 
123
STATUS_PROTOCOL_ERROR = 1002
 
124
STATUS_UNSUPPORTED_DATA = 1003
 
125
STATUS_NO_STATUS_RECEIVED = 1005
 
126
STATUS_ABNORMAL_CLOSURE = 1006
 
127
STATUS_INVALID_FRAME_PAYLOAD_DATA = 1007
 
128
STATUS_POLICY_VIOLATION = 1008
 
129
STATUS_MESSAGE_TOO_BIG = 1009
 
130
STATUS_MANDATORY_EXTENSION = 1010
 
131
STATUS_INTERNAL_ENDPOINT_ERROR = 1011
 
132
STATUS_TLS_HANDSHAKE = 1015
 
133
STATUS_USER_REGISTERED_BASE = 3000
 
134
STATUS_USER_REGISTERED_MAX = 3999
 
135
STATUS_USER_PRIVATE_BASE = 4000
 
136
STATUS_USER_PRIVATE_MAX = 4999
 
137
# Following definitions are aliases to keep compatibility. Applications must
 
138
# not use these obsoleted definitions anymore.
 
139
STATUS_NORMAL = STATUS_NORMAL_CLOSURE
 
140
STATUS_UNSUPPORTED = STATUS_UNSUPPORTED_DATA
 
141
STATUS_CODE_NOT_AVAILABLE = STATUS_NO_STATUS_RECEIVED
 
142
STATUS_ABNORMAL_CLOSE = STATUS_ABNORMAL_CLOSURE
 
143
STATUS_INVALID_FRAME_PAYLOAD = STATUS_INVALID_FRAME_PAYLOAD_DATA
 
144
STATUS_MANDATORY_EXT = STATUS_MANDATORY_EXTENSION
 
145
 
 
146
# HTTP status codes
 
147
HTTP_STATUS_BAD_REQUEST = 400
 
148
HTTP_STATUS_FORBIDDEN = 403
 
149
HTTP_STATUS_NOT_FOUND = 404
 
150
 
 
151
 
 
152
def is_control_opcode(opcode):
 
153
    return (opcode >> 3) == 1
 
154
 
 
155
 
 
156
class ExtensionParameter(object):
 
157
    """Holds information about an extension which is exchanged on extension
 
158
    negotiation in opening handshake.
 
159
    """
 
160
 
 
161
    def __init__(self, name):
 
162
        self._name = name
 
163
        # TODO(tyoshino): Change the data structure to more efficient one such
 
164
        # as dict when the spec changes to say like
 
165
        # - Parameter names must be unique
 
166
        # - The order of parameters is not significant
 
167
        self._parameters = []
 
168
 
 
169
    def name(self):
 
170
        return self._name
 
171
 
 
172
    def add_parameter(self, name, value):
 
173
        self._parameters.append((name, value))
 
174
 
 
175
    def get_parameters(self):
 
176
        return self._parameters
 
177
 
 
178
    def get_parameter_names(self):
 
179
        return [name for name, unused_value in self._parameters]
 
180
 
 
181
    def has_parameter(self, name):
 
182
        for param_name, param_value in self._parameters:
 
183
            if param_name == name:
 
184
                return True
 
185
        return False
 
186
 
 
187
    def get_parameter_value(self, name):
 
188
        for param_name, param_value in self._parameters:
 
189
            if param_name == name:
 
190
                return param_value
 
191
 
 
192
 
 
193
class ExtensionParsingException(Exception):
 
194
    def __init__(self, name):
 
195
        super(ExtensionParsingException, self).__init__(name)
 
196
 
 
197
 
 
198
def _parse_extension_param(state, definition, allow_quoted_string):
 
199
    param_name = http_header_util.consume_token(state)
 
200
 
 
201
    if param_name is None:
 
202
        raise ExtensionParsingException('No valid parameter name found')
 
203
 
 
204
    http_header_util.consume_lwses(state)
 
205
 
 
206
    if not http_header_util.consume_string(state, '='):
 
207
        definition.add_parameter(param_name, None)
 
208
        return
 
209
 
 
210
    http_header_util.consume_lwses(state)
 
211
 
 
212
    if allow_quoted_string:
 
213
        # TODO(toyoshim): Add code to validate that parsed param_value is token
 
214
        param_value = http_header_util.consume_token_or_quoted_string(state)
 
215
    else:
 
216
        param_value = http_header_util.consume_token(state)
 
217
    if param_value is None:
 
218
        raise ExtensionParsingException(
 
219
            'No valid parameter value found on the right-hand side of '
 
220
            'parameter %r' % param_name)
 
221
 
 
222
    definition.add_parameter(param_name, param_value)
 
223
 
 
224
 
 
225
def _parse_extension(state, allow_quoted_string):
 
226
    extension_token = http_header_util.consume_token(state)
 
227
    if extension_token is None:
 
228
        return None
 
229
 
 
230
    extension = ExtensionParameter(extension_token)
 
231
 
 
232
    while True:
 
233
        http_header_util.consume_lwses(state)
 
234
 
 
235
        if not http_header_util.consume_string(state, ';'):
 
236
            break
 
237
 
 
238
        http_header_util.consume_lwses(state)
 
239
 
 
240
        try:
 
241
            _parse_extension_param(state, extension, allow_quoted_string)
 
242
        except ExtensionParsingException, e:
 
243
            raise ExtensionParsingException(
 
244
                'Failed to parse parameter for %r (%r)' %
 
245
                (extension_token, e))
 
246
 
 
247
    return extension
 
248
 
 
249
 
 
250
def parse_extensions(data, allow_quoted_string=False):
 
251
    """Parses Sec-WebSocket-Extensions header value returns a list of
 
252
    ExtensionParameter objects.
 
253
 
 
254
    Leading LWSes must be trimmed.
 
255
    """
 
256
 
 
257
    state = http_header_util.ParsingState(data)
 
258
 
 
259
    extension_list = []
 
260
    while True:
 
261
        extension = _parse_extension(state, allow_quoted_string)
 
262
        if extension is not None:
 
263
            extension_list.append(extension)
 
264
 
 
265
        http_header_util.consume_lwses(state)
 
266
 
 
267
        if http_header_util.peek(state) is None:
 
268
            break
 
269
 
 
270
        if not http_header_util.consume_string(state, ','):
 
271
            raise ExtensionParsingException(
 
272
                'Failed to parse Sec-WebSocket-Extensions header: '
 
273
                'Expected a comma but found %r' %
 
274
                http_header_util.peek(state))
 
275
 
 
276
        http_header_util.consume_lwses(state)
 
277
 
 
278
    if len(extension_list) == 0:
 
279
        raise ExtensionParsingException(
 
280
            'No valid extension entry found')
 
281
 
 
282
    return extension_list
 
283
 
 
284
 
 
285
def format_extension(extension):
 
286
    """Formats an ExtensionParameter object."""
 
287
 
 
288
    formatted_params = [extension.name()]
 
289
    for param_name, param_value in extension.get_parameters():
 
290
        if param_value is None:
 
291
            formatted_params.append(param_name)
 
292
        else:
 
293
            quoted_value = http_header_util.quote_if_necessary(param_value)
 
294
            formatted_params.append('%s=%s' % (param_name, quoted_value))
 
295
    return '; '.join(formatted_params)
 
296
 
 
297
 
 
298
def format_extensions(extension_list):
 
299
    """Formats a list of ExtensionParameter objects."""
 
300
 
 
301
    formatted_extension_list = []
 
302
    for extension in extension_list:
 
303
        formatted_extension_list.append(format_extension(extension))
 
304
    return ', '.join(formatted_extension_list)
 
305
 
 
306
 
 
307
# vi:sts=4 sw=4 et