testing/mochitest/pywebsocket/mod_pywebsocket/http_header_util.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/testing/mochitest/pywebsocket/mod_pywebsocket/http_header_util.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,263 @@
     1.4 +# Copyright 2011, Google Inc.
     1.5 +# All rights reserved.
     1.6 +#
     1.7 +# Redistribution and use in source and binary forms, with or without
     1.8 +# modification, are permitted provided that the following conditions are
     1.9 +# met:
    1.10 +#
    1.11 +#     * Redistributions of source code must retain the above copyright
    1.12 +# notice, this list of conditions and the following disclaimer.
    1.13 +#     * Redistributions in binary form must reproduce the above
    1.14 +# copyright notice, this list of conditions and the following disclaimer
    1.15 +# in the documentation and/or other materials provided with the
    1.16 +# distribution.
    1.17 +#     * Neither the name of Google Inc. nor the names of its
    1.18 +# contributors may be used to endorse or promote products derived from
    1.19 +# this software without specific prior written permission.
    1.20 +#
    1.21 +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    1.22 +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    1.23 +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.24 +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    1.25 +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.26 +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    1.27 +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.28 +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    1.29 +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    1.30 +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    1.31 +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.32 +
    1.33 +
    1.34 +"""Utilities for parsing and formatting headers that follow the grammar defined
    1.35 +in HTTP RFC http://www.ietf.org/rfc/rfc2616.txt.
    1.36 +"""
    1.37 +
    1.38 +
    1.39 +import urlparse
    1.40 +
    1.41 +
    1.42 +_SEPARATORS = '()<>@,;:\\"/[]?={} \t'
    1.43 +
    1.44 +
    1.45 +def _is_char(c):
    1.46 +    """Returns true iff c is in CHAR as specified in HTTP RFC."""
    1.47 +
    1.48 +    return ord(c) <= 127
    1.49 +
    1.50 +
    1.51 +def _is_ctl(c):
    1.52 +    """Returns true iff c is in CTL as specified in HTTP RFC."""
    1.53 +
    1.54 +    return ord(c) <= 31 or ord(c) == 127
    1.55 +
    1.56 +
    1.57 +class ParsingState(object):
    1.58 +
    1.59 +    def __init__(self, data):
    1.60 +        self.data = data
    1.61 +        self.head = 0
    1.62 +
    1.63 +
    1.64 +def peek(state, pos=0):
    1.65 +    """Peeks the character at pos from the head of data."""
    1.66 +
    1.67 +    if state.head + pos >= len(state.data):
    1.68 +        return None
    1.69 +
    1.70 +    return state.data[state.head + pos]
    1.71 +
    1.72 +
    1.73 +def consume(state, amount=1):
    1.74 +    """Consumes specified amount of bytes from the head and returns the
    1.75 +    consumed bytes. If there's not enough bytes to consume, returns None.
    1.76 +    """
    1.77 +
    1.78 +    if state.head + amount > len(state.data):
    1.79 +        return None
    1.80 +
    1.81 +    result = state.data[state.head:state.head + amount]
    1.82 +    state.head = state.head + amount
    1.83 +    return result
    1.84 +
    1.85 +
    1.86 +def consume_string(state, expected):
    1.87 +    """Given a parsing state and a expected string, consumes the string from
    1.88 +    the head. Returns True if consumed successfully. Otherwise, returns
    1.89 +    False.
    1.90 +    """
    1.91 +
    1.92 +    pos = 0
    1.93 +
    1.94 +    for c in expected:
    1.95 +        if c != peek(state, pos):
    1.96 +            return False
    1.97 +        pos += 1
    1.98 +
    1.99 +    consume(state, pos)
   1.100 +    return True
   1.101 +
   1.102 +
   1.103 +def consume_lws(state):
   1.104 +    """Consumes a LWS from the head. Returns True if any LWS is consumed.
   1.105 +    Otherwise, returns False.
   1.106 +
   1.107 +    LWS = [CRLF] 1*( SP | HT )
   1.108 +    """
   1.109 +
   1.110 +    original_head = state.head
   1.111 +
   1.112 +    consume_string(state, '\r\n')
   1.113 +
   1.114 +    pos = 0
   1.115 +
   1.116 +    while True:
   1.117 +        c = peek(state, pos)
   1.118 +        if c == ' ' or c == '\t':
   1.119 +            pos += 1
   1.120 +        else:
   1.121 +            if pos == 0:
   1.122 +                state.head = original_head
   1.123 +                return False
   1.124 +            else:
   1.125 +                consume(state, pos)
   1.126 +                return True
   1.127 +
   1.128 +
   1.129 +def consume_lwses(state):
   1.130 +    """Consumes *LWS from the head."""
   1.131 +
   1.132 +    while consume_lws(state):
   1.133 +        pass
   1.134 +
   1.135 +
   1.136 +def consume_token(state):
   1.137 +    """Consumes a token from the head. Returns the token or None if no token
   1.138 +    was found.
   1.139 +    """
   1.140 +
   1.141 +    pos = 0
   1.142 +
   1.143 +    while True:
   1.144 +        c = peek(state, pos)
   1.145 +        if c is None or c in _SEPARATORS or _is_ctl(c) or not _is_char(c):
   1.146 +            if pos == 0:
   1.147 +                return None
   1.148 +
   1.149 +            return consume(state, pos)
   1.150 +        else:
   1.151 +            pos += 1
   1.152 +
   1.153 +
   1.154 +def consume_token_or_quoted_string(state):
   1.155 +    """Consumes a token or a quoted-string, and returns the token or unquoted
   1.156 +    string. If no token or quoted-string was found, returns None.
   1.157 +    """
   1.158 +
   1.159 +    original_head = state.head
   1.160 +
   1.161 +    if not consume_string(state, '"'):
   1.162 +        return consume_token(state)
   1.163 +
   1.164 +    result = []
   1.165 +
   1.166 +    expect_quoted_pair = False
   1.167 +
   1.168 +    while True:
   1.169 +        if not expect_quoted_pair and consume_lws(state):
   1.170 +            result.append(' ')
   1.171 +            continue
   1.172 +
   1.173 +        c = consume(state)
   1.174 +        if c is None:
   1.175 +            # quoted-string is not enclosed with double quotation
   1.176 +            state.head = original_head
   1.177 +            return None
   1.178 +        elif expect_quoted_pair:
   1.179 +            expect_quoted_pair = False
   1.180 +            if _is_char(c):
   1.181 +                result.append(c)
   1.182 +            else:
   1.183 +                # Non CHAR character found in quoted-pair
   1.184 +                state.head = original_head
   1.185 +                return None
   1.186 +        elif c == '\\':
   1.187 +            expect_quoted_pair = True
   1.188 +        elif c == '"':
   1.189 +            return ''.join(result)
   1.190 +        elif _is_ctl(c):
   1.191 +            # Invalid character %r found in qdtext
   1.192 +            state.head = original_head
   1.193 +            return None
   1.194 +        else:
   1.195 +            result.append(c)
   1.196 +
   1.197 +
   1.198 +def quote_if_necessary(s):
   1.199 +    """Quotes arbitrary string into quoted-string."""
   1.200 +
   1.201 +    quote = False
   1.202 +    if s == '':
   1.203 +        return '""'
   1.204 +
   1.205 +    result = []
   1.206 +    for c in s:
   1.207 +        if c == '"' or c in _SEPARATORS or _is_ctl(c) or not _is_char(c):
   1.208 +            quote = True
   1.209 +
   1.210 +        if c == '"' or _is_ctl(c):
   1.211 +            result.append('\\' + c)
   1.212 +        else:
   1.213 +            result.append(c)
   1.214 +
   1.215 +    if quote:
   1.216 +        return '"' + ''.join(result) + '"'
   1.217 +    else:
   1.218 +        return ''.join(result)
   1.219 +
   1.220 +
   1.221 +def parse_uri(uri):
   1.222 +    """Parse absolute URI then return host, port and resource."""
   1.223 +
   1.224 +    parsed = urlparse.urlsplit(uri)
   1.225 +    if parsed.scheme != 'wss' and parsed.scheme != 'ws':
   1.226 +        # |uri| must be a relative URI.
   1.227 +        # TODO(toyoshim): Should validate |uri|.
   1.228 +        return None, None, uri
   1.229 +
   1.230 +    if parsed.hostname is None:
   1.231 +        return None, None, None
   1.232 +
   1.233 +    port = None
   1.234 +    try:
   1.235 +        port = parsed.port
   1.236 +    except ValueError, e:
   1.237 +        # port property cause ValueError on invalid null port description like
   1.238 +        # 'ws://host:/path'.
   1.239 +        return None, None, None
   1.240 +
   1.241 +    if port is None:
   1.242 +        if parsed.scheme == 'ws':
   1.243 +            port = 80
   1.244 +        else:
   1.245 +            port = 443
   1.246 +
   1.247 +    path = parsed.path
   1.248 +    if not path:
   1.249 +        path += '/'
   1.250 +    if parsed.query:
   1.251 +        path += '?' + parsed.query
   1.252 +    if parsed.fragment:
   1.253 +        path += '#' + parsed.fragment
   1.254 +
   1.255 +    return parsed.hostname, port, path
   1.256 +
   1.257 +
   1.258 +try:
   1.259 +    urlparse.uses_netloc.index('ws')
   1.260 +except ValueError, e:
   1.261 +    # urlparse in Python2.5.1 doesn't have 'ws' and 'wss' entries.
   1.262 +    urlparse.uses_netloc.append('ws')
   1.263 +    urlparse.uses_netloc.append('wss')
   1.264 +
   1.265 +
   1.266 +# vi:sts=4 sw=4 et

mercurial