Home | History | Annotate | Download | only in test
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2011, Google Inc.
      4 # All rights reserved.
      5 #
      6 # Redistribution and use in source and binary forms, with or without
      7 # modification, are permitted provided that the following conditions are
      8 # met:
      9 #
     10 #     * Redistributions of source code must retain the above copyright
     11 # notice, this list of conditions and the following disclaimer.
     12 #     * Redistributions in binary form must reproduce the above
     13 # copyright notice, this list of conditions and the following disclaimer
     14 # in the documentation and/or other materials provided with the
     15 # distribution.
     16 #     * Neither the name of Google Inc. nor the names of its
     17 # contributors may be used to endorse or promote products derived from
     18 # this software without specific prior written permission.
     19 #
     20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31 
     32 
     33 """Tests for handshake.hybi00 module."""
     34 
     35 
     36 import unittest
     37 
     38 import set_sys_path  # Update sys.path to locate mod_pywebsocket module.
     39 
     40 from mod_pywebsocket.handshake._base import HandshakeException
     41 from mod_pywebsocket.handshake import hybi00 as handshake
     42 from test import mock
     43 
     44 
     45 _GOOD_REQUEST = (
     46     80,
     47     'GET',
     48     '/demo',
     49     {
     50         'Host': 'example.com',
     51         'Connection': 'Upgrade',
     52         'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
     53         'Sec-WebSocket-Protocol': 'sample',
     54         'Upgrade': 'WebSocket',
     55         'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
     56         'Origin': 'http://example.com',
     57     },
     58     '^n:ds[4U')
     59 
     60 _GOOD_REQUEST_CAPITALIZED_HEADER_VALUES = (
     61     80,
     62     'GET',
     63     '/demo',
     64     {
     65         'Host': 'example.com',
     66         'Connection': 'UPGRADE',
     67         'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
     68         'Sec-WebSocket-Protocol': 'sample',
     69         'Upgrade': 'WEBSOCKET',
     70         'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
     71         'Origin': 'http://example.com',
     72     },
     73     '^n:ds[4U')
     74 
     75 _GOOD_RESPONSE_DEFAULT_PORT = (
     76     'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
     77     'Upgrade: WebSocket\r\n'
     78     'Connection: Upgrade\r\n'
     79     'Sec-WebSocket-Location: ws://example.com/demo\r\n'
     80     'Sec-WebSocket-Origin: http://example.com\r\n'
     81     'Sec-WebSocket-Protocol: sample\r\n'
     82     '\r\n'
     83     '8jKS\'y:G*Co,Wxa-')
     84 
     85 _GOOD_RESPONSE_SECURE = (
     86     'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
     87     'Upgrade: WebSocket\r\n'
     88     'Connection: Upgrade\r\n'
     89     'Sec-WebSocket-Location: wss://example.com/demo\r\n'
     90     'Sec-WebSocket-Origin: http://example.com\r\n'
     91     'Sec-WebSocket-Protocol: sample\r\n'
     92     '\r\n'
     93     '8jKS\'y:G*Co,Wxa-')
     94 
     95 _GOOD_REQUEST_NONDEFAULT_PORT = (
     96     8081,
     97     'GET',
     98     '/demo',
     99     {
    100         'Host': 'example.com:8081',
    101         'Connection': 'Upgrade',
    102         'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
    103         'Sec-WebSocket-Protocol': 'sample',
    104         'Upgrade': 'WebSocket',
    105         'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
    106         'Origin': 'http://example.com',
    107     },
    108     '^n:ds[4U')
    109 
    110 _GOOD_RESPONSE_NONDEFAULT_PORT = (
    111     'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
    112     'Upgrade: WebSocket\r\n'
    113     'Connection: Upgrade\r\n'
    114     'Sec-WebSocket-Location: ws://example.com:8081/demo\r\n'
    115     'Sec-WebSocket-Origin: http://example.com\r\n'
    116     'Sec-WebSocket-Protocol: sample\r\n'
    117     '\r\n'
    118     '8jKS\'y:G*Co,Wxa-')
    119 
    120 _GOOD_RESPONSE_SECURE_NONDEF = (
    121     'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
    122     'Upgrade: WebSocket\r\n'
    123     'Connection: Upgrade\r\n'
    124     'Sec-WebSocket-Location: wss://example.com:8081/demo\r\n'
    125     'Sec-WebSocket-Origin: http://example.com\r\n'
    126     'Sec-WebSocket-Protocol: sample\r\n'
    127     '\r\n'
    128     '8jKS\'y:G*Co,Wxa-')
    129 
    130 _GOOD_REQUEST_NO_PROTOCOL = (
    131     80,
    132     'GET',
    133     '/demo',
    134     {
    135         'Host': 'example.com',
    136         'Connection': 'Upgrade',
    137         'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
    138         'Upgrade': 'WebSocket',
    139         'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
    140         'Origin': 'http://example.com',
    141     },
    142     '^n:ds[4U')
    143 
    144 _GOOD_RESPONSE_NO_PROTOCOL = (
    145     'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
    146     'Upgrade: WebSocket\r\n'
    147     'Connection: Upgrade\r\n'
    148     'Sec-WebSocket-Location: ws://example.com/demo\r\n'
    149     'Sec-WebSocket-Origin: http://example.com\r\n'
    150     '\r\n'
    151     '8jKS\'y:G*Co,Wxa-')
    152 
    153 _GOOD_REQUEST_WITH_OPTIONAL_HEADERS = (
    154     80,
    155     'GET',
    156     '/demo',
    157     {
    158         'Host': 'example.com',
    159         'Connection': 'Upgrade',
    160         'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
    161         'EmptyValue': '',
    162         'Sec-WebSocket-Protocol': 'sample',
    163         'AKey': 'AValue',
    164         'Upgrade': 'WebSocket',
    165         'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
    166         'Origin': 'http://example.com',
    167     },
    168     '^n:ds[4U')
    169 
    170 # TODO(tyoshino): Include \r \n in key3, challenge response.
    171 
    172 _GOOD_REQUEST_WITH_NONPRINTABLE_KEY = (
    173     80,
    174     'GET',
    175     '/demo',
    176     {
    177         'Host': 'example.com',
    178         'Connection': 'Upgrade',
    179         'Sec-WebSocket-Key2': 'y  R2 48 Q1O4  e|BV3 i5 1  u- 65',
    180         'Sec-WebSocket-Protocol': 'sample',
    181         'Upgrade': 'WebSocket',
    182         'Sec-WebSocket-Key1': '36 7   74 i  92 2\'m 9 0G',
    183         'Origin': 'http://example.com',
    184     },
    185     ''.join(map(chr, [0x01, 0xd1, 0xdd, 0x3b, 0xd1, 0x56, 0x63, 0xff])))
    186 
    187 _GOOD_RESPONSE_WITH_NONPRINTABLE_KEY = (
    188     'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
    189     'Upgrade: WebSocket\r\n'
    190     'Connection: Upgrade\r\n'
    191     'Sec-WebSocket-Location: ws://example.com/demo\r\n'
    192     'Sec-WebSocket-Origin: http://example.com\r\n'
    193     'Sec-WebSocket-Protocol: sample\r\n'
    194     '\r\n' +
    195     ''.join(map(chr, [0x0b, 0x99, 0xfa, 0x55, 0xbd, 0x01, 0x23, 0x7b,
    196                       0x45, 0xa2, 0xf1, 0xd0, 0x87, 0x8a, 0xee, 0xeb])))
    197 
    198 _BAD_REQUESTS = (
    199     (  # HTTP request
    200         80,
    201         'GET',
    202         '/demo',
    203         {
    204             'Host': 'www.google.com',
    205             'User-Agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5;'
    206                           ' en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3'
    207                           ' GTB6 GTBA',
    208             'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,'
    209                       '*/*;q=0.8',
    210             'Accept-Language': 'en-us,en;q=0.5',
    211             'Accept-Encoding': 'gzip,deflate',
    212             'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
    213             'Keep-Alive': '300',
    214             'Connection': 'keep-alive',
    215         }),
    216     (  # Wrong method
    217         80,
    218         'POST',
    219         '/demo',
    220         {
    221             'Host': 'example.com',
    222             'Connection': 'Upgrade',
    223             'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
    224             'Sec-WebSocket-Protocol': 'sample',
    225             'Upgrade': 'WebSocket',
    226             'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
    227             'Origin': 'http://example.com',
    228         },
    229         '^n:ds[4U'),
    230     (  # Missing Upgrade
    231         80,
    232         'GET',
    233         '/demo',
    234         {
    235             'Host': 'example.com',
    236             'Connection': 'Upgrade',
    237             'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
    238             'Sec-WebSocket-Protocol': 'sample',
    239             'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
    240             'Origin': 'http://example.com',
    241         },
    242         '^n:ds[4U'),
    243     (  # Wrong Upgrade
    244         80,
    245         'GET',
    246         '/demo',
    247         {
    248             'Host': 'example.com',
    249             'Connection': 'Upgrade',
    250             'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
    251             'Sec-WebSocket-Protocol': 'sample',
    252             'Upgrade': 'NonWebSocket',
    253             'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
    254             'Origin': 'http://example.com',
    255         },
    256         '^n:ds[4U'),
    257     (  # Empty WebSocket-Protocol
    258         80,
    259         'GET',
    260         '/demo',
    261         {
    262             'Host': 'example.com',
    263             'Connection': 'Upgrade',
    264             'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
    265             'Sec-WebSocket-Protocol': '',
    266             'Upgrade': 'WebSocket',
    267             'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
    268             'Origin': 'http://example.com',
    269         },
    270         '^n:ds[4U'),
    271     (  # Wrong port number format
    272         80,
    273         'GET',
    274         '/demo',
    275         {
    276             'Host': 'example.com:0x50',
    277             'Connection': 'Upgrade',
    278             'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
    279             'Sec-WebSocket-Protocol': 'sample',
    280             'Upgrade': 'WebSocket',
    281             'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
    282             'Origin': 'http://example.com',
    283         },
    284         '^n:ds[4U'),
    285     (  # Header/connection port mismatch
    286         8080,
    287         'GET',
    288         '/demo',
    289         {
    290             'Host': 'example.com',
    291             'Connection': 'Upgrade',
    292             'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
    293             'Sec-WebSocket-Protocol': 'sample',
    294             'Upgrade': 'WebSocket',
    295             'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
    296             'Origin': 'http://example.com',
    297         },
    298         '^n:ds[4U'),
    299     (  # Illegal WebSocket-Protocol
    300         80,
    301         'GET',
    302         '/demo',
    303         {
    304             'Host': 'example.com',
    305             'Connection': 'Upgrade',
    306             'Sec-WebSocket-Key2': '12998 5 Y3 1  .P00',
    307             'Sec-WebSocket-Protocol': 'illegal\x09protocol',
    308             'Upgrade': 'WebSocket',
    309             'Sec-WebSocket-Key1': '4 @1  46546xW%0l 1 5',
    310             'Origin': 'http://example.com',
    311         },
    312         '^n:ds[4U'),
    313 )
    314 
    315 
    316 def _create_request(request_def):
    317     data = ''
    318     if len(request_def) > 4:
    319         data = request_def[4]
    320     conn = mock.MockConn(data)
    321     conn.local_addr = ('0.0.0.0', request_def[0])
    322     return mock.MockRequest(
    323         method=request_def[1],
    324         uri=request_def[2],
    325         headers_in=request_def[3],
    326         connection=conn)
    327 
    328 
    329 def _create_get_memorized_lines(lines):
    330     """Creates a function that returns the given string."""
    331 
    332     def get_memorized_lines():
    333         return lines
    334     return get_memorized_lines
    335 
    336 
    337 def _create_requests_with_lines(request_lines_set):
    338     requests = []
    339     for lines in request_lines_set:
    340         request = _create_request(_GOOD_REQUEST)
    341         request.connection.get_memorized_lines = _create_get_memorized_lines(
    342                 lines)
    343         requests.append(request)
    344     return requests
    345 
    346 
    347 class Hybi00HandshakerTest(unittest.TestCase):
    348 
    349     def test_good_request_default_port(self):
    350         request = _create_request(_GOOD_REQUEST)
    351         handshaker = handshake.Handshaker(request,
    352                                           mock.MockDispatcher())
    353         handshaker.do_handshake()
    354         self.assertEqual(_GOOD_RESPONSE_DEFAULT_PORT,
    355                          request.connection.written_data())
    356         self.assertEqual('/demo', request.ws_resource)
    357         self.assertEqual('http://example.com', request.ws_origin)
    358         self.assertEqual('ws://example.com/demo', request.ws_location)
    359         self.assertEqual('sample', request.ws_protocol)
    360 
    361     def test_good_request_capitalized_header_values(self):
    362         request = _create_request(_GOOD_REQUEST_CAPITALIZED_HEADER_VALUES)
    363         handshaker = handshake.Handshaker(request,
    364                                           mock.MockDispatcher())
    365         handshaker.do_handshake()
    366         self.assertEqual(_GOOD_RESPONSE_DEFAULT_PORT,
    367                          request.connection.written_data())
    368 
    369     def test_good_request_secure_default_port(self):
    370         request = _create_request(_GOOD_REQUEST)
    371         request.connection.local_addr = ('0.0.0.0', 443)
    372         request.is_https_ = True
    373         handshaker = handshake.Handshaker(request,
    374                                           mock.MockDispatcher())
    375         handshaker.do_handshake()
    376         self.assertEqual(_GOOD_RESPONSE_SECURE,
    377                          request.connection.written_data())
    378         self.assertEqual('sample', request.ws_protocol)
    379 
    380     def test_good_request_nondefault_port(self):
    381         request = _create_request(_GOOD_REQUEST_NONDEFAULT_PORT)
    382         handshaker = handshake.Handshaker(request,
    383                                           mock.MockDispatcher())
    384         handshaker.do_handshake()
    385         self.assertEqual(_GOOD_RESPONSE_NONDEFAULT_PORT,
    386                          request.connection.written_data())
    387         self.assertEqual('sample', request.ws_protocol)
    388 
    389     def test_good_request_secure_non_default_port(self):
    390         request = _create_request(_GOOD_REQUEST_NONDEFAULT_PORT)
    391         request.is_https_ = True
    392         handshaker = handshake.Handshaker(request,
    393                                           mock.MockDispatcher())
    394         handshaker.do_handshake()
    395         self.assertEqual(_GOOD_RESPONSE_SECURE_NONDEF,
    396                          request.connection.written_data())
    397         self.assertEqual('sample', request.ws_protocol)
    398 
    399     def test_good_request_default_no_protocol(self):
    400         request = _create_request(_GOOD_REQUEST_NO_PROTOCOL)
    401         handshaker = handshake.Handshaker(request,
    402                                           mock.MockDispatcher())
    403         handshaker.do_handshake()
    404         self.assertEqual(_GOOD_RESPONSE_NO_PROTOCOL,
    405                          request.connection.written_data())
    406         self.assertEqual(None, request.ws_protocol)
    407 
    408     def test_good_request_optional_headers(self):
    409         request = _create_request(_GOOD_REQUEST_WITH_OPTIONAL_HEADERS)
    410         handshaker = handshake.Handshaker(request,
    411                                           mock.MockDispatcher())
    412         handshaker.do_handshake()
    413         self.assertEqual('AValue',
    414                          request.headers_in['AKey'])
    415         self.assertEqual('',
    416                          request.headers_in['EmptyValue'])
    417 
    418     def test_good_request_with_nonprintable_key(self):
    419         request = _create_request(_GOOD_REQUEST_WITH_NONPRINTABLE_KEY)
    420         handshaker = handshake.Handshaker(request,
    421                                           mock.MockDispatcher())
    422         handshaker.do_handshake()
    423         self.assertEqual(_GOOD_RESPONSE_WITH_NONPRINTABLE_KEY,
    424                          request.connection.written_data())
    425         self.assertEqual('sample', request.ws_protocol)
    426 
    427     def test_bad_requests(self):
    428         for request in map(_create_request, _BAD_REQUESTS):
    429             handshaker = handshake.Handshaker(request,
    430                                               mock.MockDispatcher())
    431             self.assertRaises(HandshakeException, handshaker.do_handshake)
    432 
    433 
    434 if __name__ == '__main__':
    435     unittest.main()
    436 
    437 
    438 # vi:sts=4 sw=4 et
    439