mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 15:28:39 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
|
|
@ -0,0 +1,17 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIICvDCCAiWgAwIBAgIJAKqVghkGF1rSMA0GCSqGSIb3DQEBBQUAMEkxCzAJBgNV
|
||||
BAYTAkpQMQ4wDAYDVQQIEwVUb2t5bzEUMBIGA1UEChMLcHl3ZWJzb2NrZXQxFDAS
|
||||
BgNVBAMTC3B5d2Vic29ja2V0MB4XDTEyMDYwNjA3MjQzM1oXDTM5MTAyMzA3MjQz
|
||||
M1owSTELMAkGA1UEBhMCSlAxDjAMBgNVBAgTBVRva3lvMRQwEgYDVQQKEwtweXdl
|
||||
YnNvY2tldDEUMBIGA1UEAxMLcHl3ZWJzb2NrZXQwgZ8wDQYJKoZIhvcNAQEBBQAD
|
||||
gY0AMIGJAoGBAKoSEW2biQxVrMMKdn/8PJzDYiSXDPR9WQbLRRQ1Gm5jkCYiahXW
|
||||
u2CbTThfPPfi2NHA3I+HlT7gO9yR7RVUvN6ISUzGwXDEq4f4UNqtQOhQaqqK+CZ9
|
||||
LO/BhO/YYfNrbSPlYzHUKaT9ese7xO9VzVKLW+qUf2Mjh4/+SzxBDNP7AgMBAAGj
|
||||
gaswgagwHQYDVR0OBBYEFOsWdxCSuyhwaZeab6BoTho3++bzMHkGA1UdIwRyMHCA
|
||||
FOsWdxCSuyhwaZeab6BoTho3++bzoU2kSzBJMQswCQYDVQQGEwJKUDEOMAwGA1UE
|
||||
CBMFVG9reW8xFDASBgNVBAoTC3B5d2Vic29ja2V0MRQwEgYDVQQDEwtweXdlYnNv
|
||||
Y2tldIIJAKqVghkGF1rSMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA
|
||||
gsMI1WEYqNw/jhUIdrTBcCxJ0X6hJvA9ziKANVm1Rs+4P3YDArkQ8bCr6xY+Kw7s
|
||||
Zp0yE7dM8GMdi+DU6hL3t3E5eMkTS1yZr9WCK4f2RLo+et98selZydpHemF3DJJ3
|
||||
gAj8Sx4LBaG8Cb/WnEMPv3MxG3fBE5favF6V4jU07hQ=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 1 (0x1)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=JP, ST=Tokyo, O=pywebsocket, CN=pywebsocket
|
||||
Validity
|
||||
Not Before: Jun 6 07:25:08 2012 GMT
|
||||
Not After : Oct 23 07:25:08 2039 GMT
|
||||
Subject: C=JP, ST=Tokyo, O=pywebsocket, CN=pywebsocket
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
RSA Public Key: (1024 bit)
|
||||
Modulus (1024 bit):
|
||||
00:de:10:ce:3a:5a:04:a4:1c:29:93:5c:23:82:1a:
|
||||
f2:06:01:e6:2b:a4:0f:dd:77:49:76:89:03:a2:21:
|
||||
de:04:75:c6:e2:dd:fb:35:27:3a:a2:92:8e:12:62:
|
||||
2b:3e:1f:f4:78:df:b6:94:cb:27:d6:cb:d6:37:d7:
|
||||
5c:08:f0:09:3e:c9:ce:24:2d:00:c9:df:4a:e0:99:
|
||||
e5:fb:23:a9:e2:d6:c9:3d:96:fa:01:88:de:5a:89:
|
||||
b0:cf:03:67:6f:04:86:1d:ef:62:1c:55:a9:07:9a:
|
||||
2e:66:2a:73:5b:4c:62:03:f9:82:83:db:68:bf:b8:
|
||||
4b:0b:8b:93:11:b8:54:73:7b
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Cert Type:
|
||||
SSL Server
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
82:A1:73:8B:16:0C:7C:E4:D3:46:95:13:95:1A:32:C1:84:E9:06:00
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:EB:16:77:10:92:BB:28:70:69:97:9A:6F:A0:68:4E:1A:37:FB:E6:F3
|
||||
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
6b:b3:46:29:02:df:b0:c8:8e:c4:d7:7f:a0:1e:0d:1a:eb:2f:
|
||||
df:d1:48:57:36:5f:95:8c:1b:f0:51:d6:52:e7:8d:84:3b:9f:
|
||||
d8:ed:22:9c:aa:bd:ee:9b:90:1d:84:a3:4c:0b:cb:eb:64:73:
|
||||
ba:f7:15:ce:da:5f:db:8b:15:07:a6:28:7f:b9:8c:11:9b:64:
|
||||
d3:f1:be:52:4f:c3:d8:58:fe:de:56:63:63:3b:51:ed:a7:81:
|
||||
f9:05:51:70:63:32:09:0e:94:7e:05:fe:a1:56:18:34:98:d5:
|
||||
99:1e:4e:27:38:89:90:6a:e5:ce:60:35:01:f5:de:34:60:b1:
|
||||
cb:ae
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICmDCCAgGgAwIBAgIBATANBgkqhkiG9w0BAQUFADBJMQswCQYDVQQGEwJKUDEO
|
||||
MAwGA1UECBMFVG9reW8xFDASBgNVBAoTC3B5d2Vic29ja2V0MRQwEgYDVQQDEwtw
|
||||
eXdlYnNvY2tldDAeFw0xMjA2MDYwNzI1MDhaFw0zOTEwMjMwNzI1MDhaMEkxCzAJ
|
||||
BgNVBAYTAkpQMQ4wDAYDVQQIEwVUb2t5bzEUMBIGA1UEChMLcHl3ZWJzb2NrZXQx
|
||||
FDASBgNVBAMTC3B5d2Vic29ja2V0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
|
||||
gQDeEM46WgSkHCmTXCOCGvIGAeYrpA/dd0l2iQOiId4Edcbi3fs1Jzqiko4SYis+
|
||||
H/R437aUyyfWy9Y311wI8Ak+yc4kLQDJ30rgmeX7I6ni1sk9lvoBiN5aibDPA2dv
|
||||
BIYd72IcVakHmi5mKnNbTGID+YKD22i/uEsLi5MRuFRzewIDAQABo4GPMIGMMAkG
|
||||
A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMCwGCWCGSAGG+EIBDQQfFh1PcGVu
|
||||
U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUgqFzixYMfOTTRpUT
|
||||
lRoywYTpBgAwHwYDVR0jBBgwFoAU6xZ3EJK7KHBpl5pvoGhOGjf75vMwDQYJKoZI
|
||||
hvcNAQEFBQADgYEAa7NGKQLfsMiOxNd/oB4NGusv39FIVzZflYwb8FHWUueNhDuf
|
||||
2O0inKq97puQHYSjTAvL62RzuvcVztpf24sVB6Yof7mMEZtk0/G+Uk/D2Fj+3lZj
|
||||
YztR7aeB+QVRcGMyCQ6UfgX+oVYYNJjVmR5OJziJkGrlzmA1AfXeNGCxy64=
|
||||
-----END CERTIFICATE-----
|
||||
Binary file not shown.
|
|
@ -0,0 +1,15 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXgIBAAKBgQDeEM46WgSkHCmTXCOCGvIGAeYrpA/dd0l2iQOiId4Edcbi3fs1
|
||||
Jzqiko4SYis+H/R437aUyyfWy9Y311wI8Ak+yc4kLQDJ30rgmeX7I6ni1sk9lvoB
|
||||
iN5aibDPA2dvBIYd72IcVakHmi5mKnNbTGID+YKD22i/uEsLi5MRuFRzewIDAQAB
|
||||
AoGBAIuCuV1Vcnb7rm8CwtgZP5XgmY8vSjxTldafa6XvawEYUTP0S77v/1llg1Yv
|
||||
UIV+I+PQgG9oVoYOl22LoimHS/Z3e1fsot5tDYszGe8/Gkst4oaReSoxvBUa6WXp
|
||||
QSo7YFCajuHtE+W/gzF+UHbdzzXIDjQZ314LNF5t+4UnsEPBAkEA+girImqWoM2t
|
||||
3UR8f8oekERwsmEMf9DH5YpH4cvUnvI+kwesC/r2U8Sho++fyEMUNm7aIXGqNLga
|
||||
ogAM+4NX4QJBAONdSxSay22egTGNoIhLndljWkuOt/9FWj2klf/4QxD4blMJQ5Oq
|
||||
QdOGAh7nVQjpPLQ5D7CBVAKpGM2CD+QJBtsCQEP2kz35pxPylG3urcC2mfQxBkkW
|
||||
ZCViBNP58GwJ0bOauTOSBEwFXWuLqTw8aDwxL49UNmqc0N0fpe2fAehj3UECQQCm
|
||||
FH/DjU8Lw7ybddjNtm6XXPuYNagxz3cbkB4B3FchDleIUDwMoVF0MW9bI5/54mV1
|
||||
QDk1tUKortxvQZJaAD4BAkEAhGOHQqPd6bBBoFBvpaLzPJMxwLKrB+Wtkq/QlC72
|
||||
ClRiMn2g8SALiIL3BDgGXKcKE/Wy7jo/af/JCzQ/cPqt/A==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Test for end-to-end with external server.
|
||||
|
||||
This test is not run by run_all.py because it requires some preparations.
|
||||
If you would like to run this test correctly, launch Apache with mod_python
|
||||
and mod_pywebsocket manually. In addition, you should pass allow_draft75 option
|
||||
and example path as handler_scan option and Apache's DocumentRoot.
|
||||
"""
|
||||
|
||||
|
||||
import optparse
|
||||
import sys
|
||||
import test.test_endtoend
|
||||
import unittest
|
||||
|
||||
|
||||
_DEFAULT_WEB_SOCKET_PORT = 80
|
||||
|
||||
|
||||
class EndToEndTestWithExternalServer(test.test_endtoend.EndToEndTest):
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('-p', '--port', dest='port', type='int',
|
||||
default=_DEFAULT_WEB_SOCKET_PORT,
|
||||
help='external test server port.')
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
test.test_endtoend._use_external_server = True
|
||||
test.test_endtoend._external_server_port = options.port
|
||||
|
||||
unittest.main(argv=[sys.argv[0]])
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
221
testing/web-platform/tests/tools/pywebsocket/src/test/mock.py
Normal file
221
testing/web-platform/tests/tools/pywebsocket/src/test/mock.py
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Mocks for testing.
|
||||
"""
|
||||
|
||||
|
||||
import Queue
|
||||
import threading
|
||||
|
||||
from mod_pywebsocket import common
|
||||
from mod_pywebsocket.stream import StreamHixie75
|
||||
|
||||
|
||||
class _MockConnBase(object):
|
||||
"""Base class of mocks for mod_python.apache.mp_conn.
|
||||
|
||||
This enables tests to check what is written to a (mock) mp_conn.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._write_data = []
|
||||
self.remote_addr = 'fake_address'
|
||||
|
||||
def write(self, data):
|
||||
"""Override mod_python.apache.mp_conn.write."""
|
||||
|
||||
self._write_data.append(data)
|
||||
|
||||
def written_data(self):
|
||||
"""Get bytes written to this mock."""
|
||||
|
||||
return ''.join(self._write_data)
|
||||
|
||||
|
||||
class MockConn(_MockConnBase):
|
||||
"""Mock for mod_python.apache.mp_conn.
|
||||
|
||||
This enables tests to specify what should be read from a (mock) mp_conn as
|
||||
well as to check what is written to it.
|
||||
"""
|
||||
|
||||
def __init__(self, read_data):
|
||||
"""Constructs an instance.
|
||||
|
||||
Args:
|
||||
read_data: bytes that should be returned when read* methods are
|
||||
called.
|
||||
"""
|
||||
|
||||
_MockConnBase.__init__(self)
|
||||
self._read_data = read_data
|
||||
self._read_pos = 0
|
||||
|
||||
def readline(self):
|
||||
"""Override mod_python.apache.mp_conn.readline."""
|
||||
|
||||
if self._read_pos >= len(self._read_data):
|
||||
return ''
|
||||
end_index = self._read_data.find('\n', self._read_pos) + 1
|
||||
if not end_index:
|
||||
end_index = len(self._read_data)
|
||||
return self._read_up_to(end_index)
|
||||
|
||||
def read(self, length):
|
||||
"""Override mod_python.apache.mp_conn.read."""
|
||||
|
||||
if self._read_pos >= len(self._read_data):
|
||||
return ''
|
||||
end_index = min(len(self._read_data), self._read_pos + length)
|
||||
return self._read_up_to(end_index)
|
||||
|
||||
def _read_up_to(self, end_index):
|
||||
line = self._read_data[self._read_pos:end_index]
|
||||
self._read_pos = end_index
|
||||
return line
|
||||
|
||||
|
||||
class MockBlockingConn(_MockConnBase):
|
||||
"""Blocking mock for mod_python.apache.mp_conn.
|
||||
|
||||
This enables tests to specify what should be read from a (mock) mp_conn as
|
||||
well as to check what is written to it.
|
||||
Callers of read* methods will block if there is no bytes available.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
_MockConnBase.__init__(self)
|
||||
self._queue = Queue.Queue()
|
||||
|
||||
def readline(self):
|
||||
"""Override mod_python.apache.mp_conn.readline."""
|
||||
line = ''
|
||||
while True:
|
||||
c = self._queue.get()
|
||||
line += c
|
||||
if c == '\n':
|
||||
return line
|
||||
|
||||
def read(self, length):
|
||||
"""Override mod_python.apache.mp_conn.read."""
|
||||
|
||||
data = ''
|
||||
for unused in range(length):
|
||||
data += self._queue.get()
|
||||
return data
|
||||
|
||||
def put_bytes(self, bytes):
|
||||
"""Put bytes to be read from this mock.
|
||||
|
||||
Args:
|
||||
bytes: bytes to be read.
|
||||
"""
|
||||
|
||||
for byte in bytes:
|
||||
self._queue.put(byte)
|
||||
|
||||
|
||||
class MockTable(dict):
|
||||
"""Mock table.
|
||||
|
||||
This mimics mod_python mp_table. Note that only the methods used by
|
||||
tests are overridden.
|
||||
"""
|
||||
|
||||
def __init__(self, copy_from={}):
|
||||
if isinstance(copy_from, dict):
|
||||
copy_from = copy_from.items()
|
||||
for key, value in copy_from:
|
||||
self.__setitem__(key, value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return super(MockTable, self).__getitem__(key.lower())
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
super(MockTable, self).__setitem__(key.lower(), value)
|
||||
|
||||
def get(self, key, def_value=None):
|
||||
return super(MockTable, self).get(key.lower(), def_value)
|
||||
|
||||
|
||||
class MockRequest(object):
|
||||
"""Mock request.
|
||||
|
||||
This mimics mod_python request.
|
||||
"""
|
||||
|
||||
def __init__(self, uri=None, headers_in={}, connection=None, method='GET',
|
||||
protocol='HTTP/1.1', is_https=False):
|
||||
"""Construct an instance.
|
||||
|
||||
Arguments:
|
||||
uri: URI of the request.
|
||||
headers_in: Request headers.
|
||||
connection: Connection used for the request.
|
||||
method: request method.
|
||||
is_https: Whether this request is over SSL.
|
||||
|
||||
See the document of mod_python Request for details.
|
||||
"""
|
||||
self.uri = uri
|
||||
self.unparsed_uri = uri
|
||||
self.connection = connection
|
||||
self.method = method
|
||||
self.protocol = protocol
|
||||
self.headers_in = MockTable(headers_in)
|
||||
# self.is_https_ needs to be accessible from tests. To avoid name
|
||||
# conflict with self.is_https(), it is named as such.
|
||||
self.is_https_ = is_https
|
||||
self.ws_stream = StreamHixie75(self, True)
|
||||
self.ws_close_code = None
|
||||
self.ws_close_reason = None
|
||||
self.ws_version = common.VERSION_HYBI00
|
||||
self.ws_deflate = False
|
||||
|
||||
def is_https(self):
|
||||
"""Return whether this request is over SSL."""
|
||||
return self.is_https_
|
||||
|
||||
|
||||
class MockDispatcher(object):
|
||||
"""Mock for dispatch.Dispatcher."""
|
||||
|
||||
def __init__(self):
|
||||
self.do_extra_handshake_called = False
|
||||
|
||||
def do_extra_handshake(self, conn_context):
|
||||
self.do_extra_handshake_called = True
|
||||
|
||||
def transfer_data(self, conn_context):
|
||||
pass
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,690 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""WebSocket client utility for testing mux extension.
|
||||
|
||||
This code should be independent from mod_pywebsocket. See the comment of
|
||||
client_for_testing.py.
|
||||
|
||||
NOTE: This code is far from robust like client_for_testing.py.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import Queue
|
||||
import base64
|
||||
import collections
|
||||
import email
|
||||
import email.parser
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
import socket
|
||||
import struct
|
||||
import threading
|
||||
|
||||
from mod_pywebsocket import util
|
||||
|
||||
from test import client_for_testing
|
||||
|
||||
|
||||
_CONTROL_CHANNEL_ID = 0
|
||||
_DEFAULT_CHANNEL_ID = 1
|
||||
|
||||
_MUX_OPCODE_ADD_CHANNEL_REQUEST = 0
|
||||
_MUX_OPCODE_ADD_CHANNEL_RESPONSE = 1
|
||||
_MUX_OPCODE_FLOW_CONTROL = 2
|
||||
_MUX_OPCODE_DROP_CHANNEL = 3
|
||||
_MUX_OPCODE_NEW_CHANNEL_SLOT = 4
|
||||
|
||||
|
||||
class _ControlBlock:
|
||||
def __init__(self, opcode):
|
||||
self.opcode = opcode
|
||||
|
||||
|
||||
def _parse_handshake_response(response):
|
||||
status_line, header_lines = response.split('\r\n', 1)
|
||||
|
||||
words = status_line.split(' ')
|
||||
if len(words) < 3:
|
||||
raise ValueError('Bad Status-Line syntax %r' % status_line)
|
||||
[version, response_code] = words[:2]
|
||||
if version != 'HTTP/1.1':
|
||||
raise ValueError('Bad response version %r' % version)
|
||||
|
||||
if response_code != '101':
|
||||
raise ValueError('Bad response code %r ' % response_code)
|
||||
headers = email.parser.Parser().parsestr(header_lines)
|
||||
return headers
|
||||
|
||||
|
||||
def _parse_channel_id(data, offset=0):
|
||||
length = len(data)
|
||||
remaining = length - offset
|
||||
|
||||
if remaining <= 0:
|
||||
raise Exception('No channel id found')
|
||||
|
||||
channel_id = ord(data[offset])
|
||||
channel_id_length = 1
|
||||
if channel_id & 0xe0 == 0xe0:
|
||||
if remaining < 4:
|
||||
raise Exception('Invalid channel id format')
|
||||
channel_id = struct.unpack('!L',
|
||||
data[offset:offset+4])[0] & 0x1fffffff
|
||||
channel_id_length = 4
|
||||
elif channel_id & 0xc0 == 0xc0:
|
||||
if remaining < 3:
|
||||
raise Exception('Invalid channel id format')
|
||||
channel_id = (((channel_id & 0x1f) << 16) +
|
||||
struct.unpack('!H', data[offset+1:offset+3])[0])
|
||||
channel_id_length = 3
|
||||
elif channel_id & 0x80 == 0x80:
|
||||
if remaining < 2:
|
||||
raise Exception('Invalid channel id format')
|
||||
channel_id = struct.unpack('!H', data[offset:offset+2])[0] & 0x3fff
|
||||
channel_id_length = 2
|
||||
|
||||
return channel_id, channel_id_length
|
||||
|
||||
|
||||
def _parse_number(data, offset=0):
|
||||
first_byte = ord(data[offset])
|
||||
if (first_byte & 0x80) != 0:
|
||||
raise Exception('The MSB of number field must be unset')
|
||||
first_byte = first_byte & 0x7f
|
||||
if first_byte == 127:
|
||||
if offset + 9 > len(data):
|
||||
raise Exception('Invalid number')
|
||||
return struct.unpack('!Q', data[offset+1:offset+9])[0], 9
|
||||
if first_byte == 126:
|
||||
if offset + 3 > len(data):
|
||||
raise Exception('Invalid number')
|
||||
return struct.unpack('!H', data[offset+1:offset+3])[0], 3
|
||||
return first_byte, 1
|
||||
|
||||
|
||||
def _parse_size_and_contents(data, offset=0):
|
||||
size, advance = _parse_number(data, offset)
|
||||
start_position = offset + advance
|
||||
end_position = start_position + size
|
||||
if len(data) < end_position:
|
||||
raise Exception('Invalid size of control block (%d < %d)' % (
|
||||
len(data), end_position))
|
||||
return data[start_position:end_position], size + advance
|
||||
|
||||
|
||||
def _parse_control_blocks(data):
|
||||
blocks = []
|
||||
length = len(data)
|
||||
pos = 0
|
||||
|
||||
while pos < length:
|
||||
first_byte = ord(data[pos])
|
||||
pos += 1
|
||||
opcode = (first_byte >> 5) & 0x7
|
||||
block = _ControlBlock(opcode)
|
||||
|
||||
# TODO(bashi): Support more opcode
|
||||
if opcode == _MUX_OPCODE_ADD_CHANNEL_RESPONSE:
|
||||
block.encode = first_byte & 3
|
||||
block.rejected = (first_byte >> 4) & 1
|
||||
|
||||
channel_id, advance = _parse_channel_id(data, pos)
|
||||
block.channel_id = channel_id
|
||||
pos += advance
|
||||
|
||||
encoded_handshake, advance = _parse_size_and_contents(data, pos)
|
||||
block.encoded_handshake = encoded_handshake
|
||||
pos += advance
|
||||
blocks.append(block)
|
||||
elif opcode == _MUX_OPCODE_DROP_CHANNEL:
|
||||
block.mux_error = (first_byte >> 4) & 1
|
||||
|
||||
channel_id, advance = _parse_channel_id(data, pos)
|
||||
block.channel_id = channel_id
|
||||
pos += advance
|
||||
|
||||
reason, advance = _parse_size_and_contents(data, pos)
|
||||
if len(reason) == 0:
|
||||
block.drop_code = None
|
||||
block.drop_message = ''
|
||||
elif len(reason) >= 2:
|
||||
block.drop_code = struct.unpack('!H', reason[:2])[0]
|
||||
block.drop_message = reason[2:]
|
||||
else:
|
||||
raise Exception('Invalid DropChannel')
|
||||
pos += advance
|
||||
blocks.append(block)
|
||||
elif opcode == _MUX_OPCODE_FLOW_CONTROL:
|
||||
channel_id, advance = _parse_channel_id(data, pos)
|
||||
block.channel_id = channel_id
|
||||
pos += advance
|
||||
send_quota, advance = _parse_number(data, pos)
|
||||
block.send_quota = send_quota
|
||||
pos += advance
|
||||
blocks.append(block)
|
||||
elif opcode == _MUX_OPCODE_NEW_CHANNEL_SLOT:
|
||||
fallback = first_byte & 1
|
||||
slots, advance = _parse_number(data, pos)
|
||||
pos += advance
|
||||
send_quota, advance = _parse_number(data, pos)
|
||||
pos += advance
|
||||
if fallback == 1 and (slots != 0 or send_quota != 0):
|
||||
raise Exception('slots and send_quota must be zero if F bit '
|
||||
'is set')
|
||||
block.fallback = fallback
|
||||
block.slots = slots
|
||||
block.send_quota = send_quota
|
||||
blocks.append(block)
|
||||
else:
|
||||
raise Exception(
|
||||
'Unsupported mux opcode %d received' % opcode)
|
||||
|
||||
return blocks
|
||||
|
||||
|
||||
def _encode_channel_id(channel_id):
|
||||
if channel_id < 0:
|
||||
raise ValueError('Channel id %d must not be negative' % channel_id)
|
||||
|
||||
if channel_id < 2 ** 7:
|
||||
return chr(channel_id)
|
||||
if channel_id < 2 ** 14:
|
||||
return struct.pack('!H', 0x8000 + channel_id)
|
||||
if channel_id < 2 ** 21:
|
||||
first = chr(0xc0 + (channel_id >> 16))
|
||||
return first + struct.pack('!H', channel_id & 0xffff)
|
||||
if channel_id < 2 ** 29:
|
||||
return struct.pack('!L', 0xe0000000 + channel_id)
|
||||
|
||||
raise ValueError('Channel id %d is too large' % channel_id)
|
||||
|
||||
|
||||
def _encode_number(number):
|
||||
if number <= 125:
|
||||
return chr(number)
|
||||
elif number < (1 << 16):
|
||||
return chr(0x7e) + struct.pack('!H', number)
|
||||
elif number < (1 << 63):
|
||||
return chr(0x7f) + struct.pack('!Q', number)
|
||||
else:
|
||||
raise Exception('Invalid number')
|
||||
|
||||
|
||||
def _create_add_channel_request(channel_id, encoded_handshake,
|
||||
encoding=0):
|
||||
length = len(encoded_handshake)
|
||||
handshake_length = _encode_number(length)
|
||||
|
||||
first_byte = (_MUX_OPCODE_ADD_CHANNEL_REQUEST << 5) | encoding
|
||||
return (chr(first_byte) + _encode_channel_id(channel_id) +
|
||||
handshake_length + encoded_handshake)
|
||||
|
||||
|
||||
def _create_flow_control(channel_id, replenished_quota):
|
||||
first_byte = (_MUX_OPCODE_FLOW_CONTROL << 5)
|
||||
return (chr(first_byte) + _encode_channel_id(channel_id) +
|
||||
_encode_number(replenished_quota))
|
||||
|
||||
|
||||
class _MuxReaderThread(threading.Thread):
|
||||
"""Mux reader thread.
|
||||
|
||||
Reads frames and passes them to the mux client. This thread accesses
|
||||
private functions/variables of the mux client.
|
||||
"""
|
||||
|
||||
def __init__(self, mux):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self._mux = mux
|
||||
self._stop_requested = False
|
||||
|
||||
def _receive_message(self):
|
||||
first_opcode = None
|
||||
pending_payload = []
|
||||
while not self._stop_requested:
|
||||
fin, rsv1, rsv2, rsv3, opcode, payload_length = (
|
||||
client_for_testing.read_frame_header(self._mux._socket))
|
||||
|
||||
if not first_opcode:
|
||||
if opcode == client_for_testing.OPCODE_TEXT:
|
||||
raise Exception('Received a text message on physical '
|
||||
'connection')
|
||||
if opcode == client_for_testing.OPCODE_CONTINUATION:
|
||||
raise Exception('Received an intermediate frame but '
|
||||
'fragmentation was not started')
|
||||
if (opcode == client_for_testing.OPCODE_BINARY or
|
||||
opcode == client_for_testing.OPCODE_PONG or
|
||||
opcode == client_for_testing.OPCODE_PONG or
|
||||
opcode == client_for_testing.OPCODE_CLOSE):
|
||||
first_opcode = opcode
|
||||
else:
|
||||
raise Exception('Received an undefined opcode frame: %d' %
|
||||
opcode)
|
||||
|
||||
elif opcode != client_for_testing.OPCODE_CONTINUATION:
|
||||
raise Exception('Received a new opcode before '
|
||||
'terminating fragmentation')
|
||||
|
||||
payload = client_for_testing.receive_bytes(
|
||||
self._mux._socket, payload_length)
|
||||
|
||||
if self._mux._incoming_frame_filter is not None:
|
||||
payload = self._mux._incoming_frame_filter.filter(payload)
|
||||
|
||||
pending_payload.append(payload)
|
||||
|
||||
if fin:
|
||||
break
|
||||
|
||||
if self._stop_requested:
|
||||
return None, None
|
||||
|
||||
message = ''.join(pending_payload)
|
||||
return first_opcode, message
|
||||
|
||||
def request_stop(self):
|
||||
self._stop_requested = True
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
while not self._stop_requested:
|
||||
# opcode is OPCODE_BINARY or control opcodes when a message
|
||||
# is succesfully received.
|
||||
opcode, message = self._receive_message()
|
||||
if not opcode:
|
||||
return
|
||||
if opcode == client_for_testing.OPCODE_BINARY:
|
||||
channel_id, advance = _parse_channel_id(message)
|
||||
self._mux._dispatch_frame(channel_id, message[advance:])
|
||||
else:
|
||||
self._mux._process_control_message(opcode, message)
|
||||
finally:
|
||||
self._mux._notify_reader_done()
|
||||
|
||||
|
||||
class _InnerFrame(object):
|
||||
def __init__(self, fin, rsv1, rsv2, rsv3, opcode, payload):
|
||||
self.fin = fin
|
||||
self.rsv1 = rsv1
|
||||
self.rsv2 = rsv2
|
||||
self.rsv3 = rsv3
|
||||
self.opcode = opcode
|
||||
self.payload = payload
|
||||
|
||||
|
||||
class _LogicalChannelData(object):
|
||||
def __init__(self):
|
||||
self.queue = Queue.Queue()
|
||||
self.send_quota = 0
|
||||
self.receive_quota = 0
|
||||
|
||||
|
||||
class MuxClient(object):
|
||||
"""WebSocket mux client.
|
||||
|
||||
Note that this class is NOT thread-safe. Do not access an instance of this
|
||||
class from multiple threads at a same time.
|
||||
"""
|
||||
|
||||
def __init__(self, options):
|
||||
self._logger = util.get_class_logger(self)
|
||||
|
||||
self._options = options
|
||||
self._options.enable_mux()
|
||||
self._stream = None
|
||||
self._socket = None
|
||||
self._handshake = client_for_testing.WebSocketHandshake(self._options)
|
||||
self._incoming_frame_filter = None
|
||||
self._outgoing_frame_filter = None
|
||||
|
||||
self._is_active = False
|
||||
self._read_thread = None
|
||||
self._control_blocks_condition = threading.Condition()
|
||||
self._control_blocks = []
|
||||
self._channel_slots = collections.deque()
|
||||
self._logical_channels_condition = threading.Condition();
|
||||
self._logical_channels = {}
|
||||
self._timeout = 2
|
||||
self._physical_connection_close_event = None
|
||||
self._physical_connection_close_message = None
|
||||
|
||||
def _parse_inner_frame(self, data):
|
||||
if len(data) == 0:
|
||||
raise Exception('Invalid encapsulated frame received')
|
||||
|
||||
first_byte = ord(data[0])
|
||||
fin = (first_byte << 7) & 1
|
||||
rsv1 = (first_byte << 6) & 1
|
||||
rsv2 = (first_byte << 5) & 1
|
||||
rsv3 = (first_byte << 4) & 1
|
||||
opcode = first_byte & 0xf
|
||||
|
||||
if self._outgoing_frame_filter:
|
||||
payload = self._outgoing_frame_filter.filter(
|
||||
data[1:])
|
||||
else:
|
||||
payload = data[1:]
|
||||
|
||||
return _InnerFrame(fin, rsv1, rsv2, rsv3, opcode, payload)
|
||||
|
||||
def _process_mux_control_blocks(self):
|
||||
for block in self._control_blocks:
|
||||
if block.opcode == _MUX_OPCODE_ADD_CHANNEL_RESPONSE:
|
||||
# AddChannelResponse will be handled in add_channel().
|
||||
continue
|
||||
elif block.opcode == _MUX_OPCODE_FLOW_CONTROL:
|
||||
try:
|
||||
self._logical_channels_condition.acquire()
|
||||
if not block.channel_id in self._logical_channels:
|
||||
raise Exception('Invalid flow control received for '
|
||||
'channel id %d' % block.channel_id)
|
||||
self._logical_channels[block.channel_id].send_quota += (
|
||||
block.send_quota)
|
||||
self._logical_channels_condition.notify()
|
||||
finally:
|
||||
self._logical_channels_condition.release()
|
||||
elif block.opcode == _MUX_OPCODE_NEW_CHANNEL_SLOT:
|
||||
self._channel_slots.extend([block.send_quota] * block.slots)
|
||||
|
||||
def _dispatch_frame(self, channel_id, payload):
|
||||
if channel_id == _CONTROL_CHANNEL_ID:
|
||||
try:
|
||||
self._control_blocks_condition.acquire()
|
||||
self._control_blocks += _parse_control_blocks(payload)
|
||||
self._process_mux_control_blocks()
|
||||
self._control_blocks_condition.notify()
|
||||
finally:
|
||||
self._control_blocks_condition.release()
|
||||
else:
|
||||
try:
|
||||
self._logical_channels_condition.acquire()
|
||||
if not channel_id in self._logical_channels:
|
||||
raise Exception('Received logical frame on channel id '
|
||||
'%d, which is not established' %
|
||||
channel_id)
|
||||
|
||||
inner_frame = self._parse_inner_frame(payload)
|
||||
self._logical_channels[channel_id].receive_quota -= (
|
||||
len(inner_frame.payload))
|
||||
if self._logical_channels[channel_id].receive_quota < 0:
|
||||
raise Exception('The server violates quota on '
|
||||
'channel id %d' % channel_id)
|
||||
finally:
|
||||
self._logical_channels_condition.release()
|
||||
self._logical_channels[channel_id].queue.put(inner_frame)
|
||||
|
||||
def _process_control_message(self, opcode, message):
|
||||
# Ping/Pong are not supported.
|
||||
if opcode == client_for_testing.OPCODE_CLOSE:
|
||||
self._physical_connection_close_message = message
|
||||
if self._is_active:
|
||||
self._stream.send_close(
|
||||
code=client_for_testing.STATUS_NORMAL_CLOSURE, reason='')
|
||||
self._read_thread.request_stop()
|
||||
|
||||
if self._physical_connection_close_event:
|
||||
self._physical_connection_close_event.set()
|
||||
|
||||
def _notify_reader_done(self):
|
||||
self._logger.debug('Read thread terminated.')
|
||||
self.close_socket()
|
||||
|
||||
def _assert_channel_slot_available(self):
|
||||
try:
|
||||
self._control_blocks_condition.acquire()
|
||||
if len(self._channel_slots) == 0:
|
||||
# Wait once
|
||||
self._control_blocks_condition.wait(timeout=self._timeout)
|
||||
finally:
|
||||
self._control_blocks_condition.release()
|
||||
|
||||
if len(self._channel_slots) == 0:
|
||||
raise Exception('Failed to receive NewChannelSlot')
|
||||
|
||||
def _assert_send_quota_available(self, channel_id):
|
||||
try:
|
||||
self._logical_channels_condition.acquire()
|
||||
if self._logical_channels[channel_id].send_quota == 0:
|
||||
# Wait once
|
||||
self._logical_channels_condition.wait(timeout=self._timeout)
|
||||
finally:
|
||||
self._logical_channels_condition.release()
|
||||
|
||||
if self._logical_channels[channel_id].send_quota == 0:
|
||||
raise Exception('Failed to receive FlowControl for channel id %d' %
|
||||
channel_id)
|
||||
|
||||
def connect(self):
|
||||
self._socket = client_for_testing.connect_socket_with_retry(
|
||||
self._options.server_host,
|
||||
self._options.server_port,
|
||||
self._options.socket_timeout,
|
||||
self._options.use_tls)
|
||||
|
||||
self._handshake.handshake(self._socket)
|
||||
self._stream = client_for_testing.WebSocketStream(
|
||||
self._socket, self._handshake)
|
||||
|
||||
self._logical_channels[_DEFAULT_CHANNEL_ID] = _LogicalChannelData()
|
||||
|
||||
self._read_thread = _MuxReaderThread(self)
|
||||
self._read_thread.start()
|
||||
|
||||
self._assert_channel_slot_available()
|
||||
self._assert_send_quota_available(_DEFAULT_CHANNEL_ID)
|
||||
|
||||
self._is_active = True
|
||||
self._logger.info('Connection established')
|
||||
|
||||
def add_channel(self, channel_id, options):
|
||||
if not self._is_active:
|
||||
raise Exception('Mux client is not active')
|
||||
|
||||
if channel_id in self._logical_channels:
|
||||
raise Exception('Channel id %d already exists' % channel_id)
|
||||
|
||||
try:
|
||||
send_quota = self._channel_slots.popleft()
|
||||
except IndexError, e:
|
||||
raise Exception('No channel slots: %r' % e)
|
||||
|
||||
# Create AddChannel request
|
||||
request_line = 'GET %s HTTP/1.1\r\n' % options.resource
|
||||
fields = []
|
||||
if options.server_port == client_for_testing.DEFAULT_PORT:
|
||||
fields.append('Host: %s\r\n' % options.server_host.lower())
|
||||
else:
|
||||
fields.append('Host: %s:%d\r\n' % (options.server_host.lower(),
|
||||
options.server_port))
|
||||
fields.append('Origin: %s\r\n' % options.origin.lower())
|
||||
fields.append('Connection: Upgrade\r\n')
|
||||
|
||||
if len(options.extensions) > 0:
|
||||
fields.append('Sec-WebSocket-Extensions: %s\r\n' %
|
||||
', '.join(options.extensions))
|
||||
|
||||
handshake = request_line + ''.join(fields) + '\r\n'
|
||||
add_channel_request = _create_add_channel_request(
|
||||
channel_id, handshake)
|
||||
payload = _encode_channel_id(_CONTROL_CHANNEL_ID) + add_channel_request
|
||||
self._stream.send_binary(payload)
|
||||
|
||||
# Wait AddChannelResponse
|
||||
self._logger.debug('Waiting AddChannelResponse for the request...')
|
||||
response = None
|
||||
try:
|
||||
self._control_blocks_condition.acquire()
|
||||
while True:
|
||||
for block in self._control_blocks:
|
||||
if block.opcode != _MUX_OPCODE_ADD_CHANNEL_RESPONSE:
|
||||
continue
|
||||
if block.channel_id == channel_id:
|
||||
response = block
|
||||
self._control_blocks.remove(response)
|
||||
break
|
||||
if response:
|
||||
break
|
||||
self._control_blocks_condition.wait(self._timeout)
|
||||
if not self._is_active:
|
||||
raise Exception('AddChannelRequest timed out')
|
||||
finally:
|
||||
self._control_blocks_condition.release()
|
||||
|
||||
# Validate AddChannelResponse
|
||||
if response.rejected:
|
||||
raise Exception('The server rejected AddChannelRequest')
|
||||
|
||||
fields = _parse_handshake_response(response.encoded_handshake)
|
||||
|
||||
# Should we reject when Upgrade, Connection, or Sec-WebSocket-Accept
|
||||
# headers exist?
|
||||
|
||||
self._logical_channels_condition.acquire()
|
||||
self._logical_channels[channel_id] = _LogicalChannelData()
|
||||
self._logical_channels[channel_id].send_quota = send_quota
|
||||
self._logical_channels_condition.release()
|
||||
|
||||
self._logger.debug('Logical channel %d established' % channel_id)
|
||||
|
||||
def _check_logical_channel_is_opened(self, channel_id):
|
||||
if not self._is_active:
|
||||
raise Exception('Mux client is not active')
|
||||
|
||||
if not channel_id in self._logical_channels:
|
||||
raise Exception('Logical channel %d is not established.')
|
||||
|
||||
def drop_channel(self, channel_id):
|
||||
# TODO(bashi): Implement
|
||||
pass
|
||||
|
||||
def send_flow_control(self, channel_id, replenished_quota):
|
||||
self._check_logical_channel_is_opened(channel_id)
|
||||
flow_control = _create_flow_control(channel_id, replenished_quota)
|
||||
payload = _encode_channel_id(_CONTROL_CHANNEL_ID) + flow_control
|
||||
# Replenish receive quota
|
||||
try:
|
||||
self._logical_channels_condition.acquire()
|
||||
self._logical_channels[channel_id].receive_quota += (
|
||||
replenished_quota)
|
||||
finally:
|
||||
self._logical_channels_condition.release()
|
||||
self._stream.send_binary(payload)
|
||||
|
||||
def send_message(self, channel_id, message, end=True, binary=False):
|
||||
self._check_logical_channel_is_opened(channel_id)
|
||||
|
||||
if binary:
|
||||
first_byte = (end << 7) | client_for_testing.OPCODE_BINARY
|
||||
else:
|
||||
first_byte = (end << 7) | client_for_testing.OPCODE_TEXT
|
||||
message = message.encode('utf-8')
|
||||
|
||||
try:
|
||||
self._logical_channels_condition.acquire()
|
||||
if self._logical_channels[channel_id].send_quota < len(message):
|
||||
raise Exception('Send quota violation: %d < %d' % (
|
||||
self._logical_channels[channel_id].send_quota,
|
||||
len(message)))
|
||||
|
||||
self._logical_channels[channel_id].send_quota -= len(message)
|
||||
finally:
|
||||
self._logical_channels_condition.release()
|
||||
payload = _encode_channel_id(channel_id) + chr(first_byte) + message
|
||||
self._stream.send_binary(payload)
|
||||
|
||||
def assert_receive(self, channel_id, payload, binary=False):
|
||||
self._check_logical_channel_is_opened(channel_id)
|
||||
|
||||
try:
|
||||
inner_frame = self._logical_channels[channel_id].queue.get(
|
||||
timeout=self._timeout)
|
||||
except Queue.Empty, e:
|
||||
raise Exception('Cannot receive message from channel id %d' %
|
||||
channel_id)
|
||||
|
||||
if binary:
|
||||
opcode = client_for_testing.OPCODE_BINARY
|
||||
else:
|
||||
opcode = client_for_testing.OPCODE_TEXT
|
||||
|
||||
if inner_frame.opcode != opcode:
|
||||
raise Exception('Unexpected opcode received (%r != %r)' %
|
||||
(expected_opcode, inner_frame.opcode))
|
||||
|
||||
if inner_frame.payload != payload:
|
||||
raise Exception('Unexpected payload received')
|
||||
|
||||
def send_close(self, channel_id, code=None, reason=''):
|
||||
self._check_logical_channel_is_opened(channel_id)
|
||||
|
||||
if code is not None:
|
||||
body = struct.pack('!H', code) + reason.encode('utf-8')
|
||||
else:
|
||||
body = ''
|
||||
|
||||
first_byte = (1 << 7) | client_for_testing.OPCODE_CLOSE
|
||||
payload = _encode_channel_id(channel_id) + chr(first_byte) + body
|
||||
self._stream.send_binary(payload)
|
||||
|
||||
def assert_receive_close(self, channel_id):
|
||||
self._check_logical_channel_is_opened(channel_id)
|
||||
|
||||
try:
|
||||
inner_frame = self._logical_channels[channel_id].queue.get(
|
||||
timeout=self._timeout)
|
||||
except Queue.Empty, e:
|
||||
raise Exception('Cannot receive message from channel id %d' %
|
||||
channel_id)
|
||||
if inner_frame.opcode != client_for_testing.OPCODE_CLOSE:
|
||||
raise Exception('Didn\'t receive close frame')
|
||||
|
||||
def send_physical_connection_close(self, code=None, reason=''):
|
||||
self._physical_connection_close_event = threading.Event()
|
||||
self._stream.send_close(code, reason)
|
||||
|
||||
# This method can be used only after calling
|
||||
# send_physical_connection_close().
|
||||
def assert_physical_connection_receive_close(
|
||||
self, code=client_for_testing.STATUS_NORMAL_CLOSURE, reason=''):
|
||||
self._physical_connection_close_event.wait(timeout=self._timeout)
|
||||
if (not self._physical_connection_close_event.isSet() or
|
||||
not self._physical_connection_close_message):
|
||||
raise Exception('Didn\'t receive closing handshake')
|
||||
|
||||
def close_socket(self):
|
||||
self._is_active = False
|
||||
self._socket.close()
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Run all tests in the same directory.
|
||||
|
||||
This suite is expected to be run under pywebsocket's src directory, i.e. the
|
||||
directory containing mod_pywebsocket, test, etc.
|
||||
|
||||
To change loggin level, please specify --log-level option.
|
||||
python test/run_test.py --log-level debug
|
||||
|
||||
To pass any option to unittest module, please specify options after '--'. For
|
||||
example, run this for making the test runner verbose.
|
||||
python test/run_test.py --log-level debug -- -v
|
||||
"""
|
||||
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
|
||||
_TEST_MODULE_PATTERN = re.compile(r'^(test_.+)\.py$')
|
||||
|
||||
|
||||
def _list_test_modules(directory):
|
||||
module_names = []
|
||||
for filename in os.listdir(directory):
|
||||
match = _TEST_MODULE_PATTERN.search(filename)
|
||||
if match:
|
||||
module_names.append(match.group(1))
|
||||
return module_names
|
||||
|
||||
|
||||
def _suite():
|
||||
loader = unittest.TestLoader()
|
||||
return loader.loadTestsFromNames(
|
||||
_list_test_modules(os.path.join(os.path.split(__file__)[0], '.')))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('--log-level', '--log_level', type='choice',
|
||||
dest='log_level', default='warning',
|
||||
choices=['debug', 'info', 'warning', 'warn', 'error',
|
||||
'critical'])
|
||||
options, args = parser.parse_args()
|
||||
logging.basicConfig(
|
||||
level=logging.getLevelName(options.log_level.upper()),
|
||||
format='%(levelname)s %(asctime)s '
|
||||
'%(filename)s:%(lineno)d] '
|
||||
'%(message)s',
|
||||
datefmt='%H:%M:%S')
|
||||
unittest.main(defaultTest='_suite', argv=[sys.argv[0]] + args)
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright 2009, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Configuration for testing.
|
||||
|
||||
Test files should import this module before mod_pywebsocket.
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
# Add the parent directory to sys.path to enable importing mod_pywebsocket.
|
||||
sys.path.insert(0, os.path.join(os.path.split(__file__)[0], '..'))
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Tests for dispatch module."""
|
||||
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import set_sys_path # Update sys.path to locate mod_pywebsocket module.
|
||||
|
||||
from mod_pywebsocket import dispatch
|
||||
from mod_pywebsocket import handshake
|
||||
from test import mock
|
||||
|
||||
|
||||
_TEST_HANDLERS_DIR = os.path.join(
|
||||
os.path.split(__file__)[0], 'testdata', 'handlers')
|
||||
|
||||
_TEST_HANDLERS_SUB_DIR = os.path.join(_TEST_HANDLERS_DIR, 'sub')
|
||||
|
||||
|
||||
class DispatcherTest(unittest.TestCase):
|
||||
"""A unittest for dispatch module."""
|
||||
|
||||
def test_normalize_path(self):
|
||||
self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'),
|
||||
dispatch._normalize_path('/a/b'))
|
||||
self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'),
|
||||
dispatch._normalize_path('\\a\\b'))
|
||||
self.assertEqual(os.path.abspath('/a/b').replace('\\', '/'),
|
||||
dispatch._normalize_path('/a/c/../b'))
|
||||
self.assertEqual(os.path.abspath('abc').replace('\\', '/'),
|
||||
dispatch._normalize_path('abc'))
|
||||
|
||||
def test_converter(self):
|
||||
converter = dispatch._create_path_to_resource_converter('/a/b')
|
||||
# Python built by MSC inserts a drive name like 'C:\' via realpath().
|
||||
# Converter Generator expands provided path using realpath() and uses
|
||||
# the path including a drive name to verify the prefix.
|
||||
os_root = os.path.realpath('/')
|
||||
self.assertEqual('/h', converter(os_root + 'a/b/h_wsh.py'))
|
||||
self.assertEqual('/c/h', converter(os_root + 'a/b/c/h_wsh.py'))
|
||||
self.assertEqual(None, converter(os_root + 'a/b/h.py'))
|
||||
self.assertEqual(None, converter('a/b/h_wsh.py'))
|
||||
|
||||
converter = dispatch._create_path_to_resource_converter('a/b')
|
||||
self.assertEqual('/h', converter(dispatch._normalize_path(
|
||||
'a/b/h_wsh.py')))
|
||||
|
||||
converter = dispatch._create_path_to_resource_converter('/a/b///')
|
||||
self.assertEqual('/h', converter(os_root + 'a/b/h_wsh.py'))
|
||||
self.assertEqual('/h', converter(dispatch._normalize_path(
|
||||
'/a/b/../b/h_wsh.py')))
|
||||
|
||||
converter = dispatch._create_path_to_resource_converter(
|
||||
'/a/../a/b/../b/')
|
||||
self.assertEqual('/h', converter(os_root + 'a/b/h_wsh.py'))
|
||||
|
||||
converter = dispatch._create_path_to_resource_converter(r'\a\b')
|
||||
self.assertEqual('/h', converter(os_root + r'a\b\h_wsh.py'))
|
||||
self.assertEqual('/h', converter(os_root + r'a/b/h_wsh.py'))
|
||||
|
||||
def test_enumerate_handler_file_paths(self):
|
||||
paths = list(
|
||||
dispatch._enumerate_handler_file_paths(_TEST_HANDLERS_DIR))
|
||||
paths.sort()
|
||||
self.assertEqual(8, len(paths))
|
||||
expected_paths = [
|
||||
os.path.join(_TEST_HANDLERS_DIR, 'abort_by_user_wsh.py'),
|
||||
os.path.join(_TEST_HANDLERS_DIR, 'blank_wsh.py'),
|
||||
os.path.join(_TEST_HANDLERS_DIR, 'origin_check_wsh.py'),
|
||||
os.path.join(_TEST_HANDLERS_DIR, 'sub',
|
||||
'exception_in_transfer_wsh.py'),
|
||||
os.path.join(_TEST_HANDLERS_DIR, 'sub', 'non_callable_wsh.py'),
|
||||
os.path.join(_TEST_HANDLERS_DIR, 'sub', 'plain_wsh.py'),
|
||||
os.path.join(_TEST_HANDLERS_DIR, 'sub',
|
||||
'wrong_handshake_sig_wsh.py'),
|
||||
os.path.join(_TEST_HANDLERS_DIR, 'sub',
|
||||
'wrong_transfer_sig_wsh.py'),
|
||||
]
|
||||
for expected, actual in zip(expected_paths, paths):
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_source_handler_file(self):
|
||||
self.assertRaises(
|
||||
dispatch.DispatchException, dispatch._source_handler_file, '')
|
||||
self.assertRaises(
|
||||
dispatch.DispatchException, dispatch._source_handler_file, 'def')
|
||||
self.assertRaises(
|
||||
dispatch.DispatchException, dispatch._source_handler_file, '1/0')
|
||||
self.failUnless(dispatch._source_handler_file(
|
||||
'def web_socket_do_extra_handshake(request):pass\n'
|
||||
'def web_socket_transfer_data(request):pass\n'))
|
||||
|
||||
def test_source_warnings(self):
|
||||
dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
|
||||
warnings = dispatcher.source_warnings()
|
||||
warnings.sort()
|
||||
expected_warnings = [
|
||||
(os.path.realpath(os.path.join(
|
||||
_TEST_HANDLERS_DIR, 'blank_wsh.py')) +
|
||||
': web_socket_do_extra_handshake is not defined.'),
|
||||
(os.path.realpath(os.path.join(
|
||||
_TEST_HANDLERS_DIR, 'sub', 'non_callable_wsh.py')) +
|
||||
': web_socket_do_extra_handshake is not callable.'),
|
||||
(os.path.realpath(os.path.join(
|
||||
_TEST_HANDLERS_DIR, 'sub', 'wrong_handshake_sig_wsh.py')) +
|
||||
': web_socket_do_extra_handshake is not defined.'),
|
||||
(os.path.realpath(os.path.join(
|
||||
_TEST_HANDLERS_DIR, 'sub', 'wrong_transfer_sig_wsh.py')) +
|
||||
': web_socket_transfer_data is not defined.'),
|
||||
]
|
||||
self.assertEquals(4, len(warnings))
|
||||
for expected, actual in zip(expected_warnings, warnings):
|
||||
self.assertEquals(expected, actual)
|
||||
|
||||
def test_do_extra_handshake(self):
|
||||
dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
|
||||
request = mock.MockRequest()
|
||||
request.ws_resource = '/origin_check'
|
||||
request.ws_origin = 'http://example.com'
|
||||
dispatcher.do_extra_handshake(request) # Must not raise exception.
|
||||
|
||||
request.ws_origin = 'http://bad.example.com'
|
||||
try:
|
||||
dispatcher.do_extra_handshake(request)
|
||||
self.fail('Could not catch HandshakeException with 403 status')
|
||||
except handshake.HandshakeException, e:
|
||||
self.assertEquals(403, e.status)
|
||||
except Exception, e:
|
||||
self.fail('Unexpected exception: %r' % e)
|
||||
|
||||
def test_abort_extra_handshake(self):
|
||||
dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
|
||||
request = mock.MockRequest()
|
||||
request.ws_resource = '/abort_by_user'
|
||||
self.assertRaises(handshake.AbortedByUserException,
|
||||
dispatcher.do_extra_handshake, request)
|
||||
|
||||
def test_transfer_data(self):
|
||||
dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
|
||||
|
||||
request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
|
||||
request.ws_resource = '/origin_check'
|
||||
request.ws_protocol = 'p1'
|
||||
dispatcher.transfer_data(request)
|
||||
self.assertEqual('origin_check_wsh.py is called for /origin_check, p1'
|
||||
'\xff\x00',
|
||||
request.connection.written_data())
|
||||
|
||||
request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
|
||||
request.ws_resource = '/sub/plain'
|
||||
request.ws_protocol = None
|
||||
dispatcher.transfer_data(request)
|
||||
self.assertEqual('sub/plain_wsh.py is called for /sub/plain, None'
|
||||
'\xff\x00',
|
||||
request.connection.written_data())
|
||||
|
||||
request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
|
||||
request.ws_resource = '/sub/plain?'
|
||||
request.ws_protocol = None
|
||||
dispatcher.transfer_data(request)
|
||||
self.assertEqual('sub/plain_wsh.py is called for /sub/plain?, None'
|
||||
'\xff\x00',
|
||||
request.connection.written_data())
|
||||
|
||||
request = mock.MockRequest(connection=mock.MockConn('\xff\x00'))
|
||||
request.ws_resource = '/sub/plain?q=v'
|
||||
request.ws_protocol = None
|
||||
dispatcher.transfer_data(request)
|
||||
self.assertEqual('sub/plain_wsh.py is called for /sub/plain?q=v, None'
|
||||
'\xff\x00',
|
||||
request.connection.written_data())
|
||||
|
||||
def test_transfer_data_no_handler(self):
|
||||
dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
|
||||
for resource in ['/blank', '/sub/non_callable',
|
||||
'/sub/no_wsh_at_the_end', '/does/not/exist']:
|
||||
request = mock.MockRequest(connection=mock.MockConn(''))
|
||||
request.ws_resource = resource
|
||||
request.ws_protocol = 'p2'
|
||||
try:
|
||||
dispatcher.transfer_data(request)
|
||||
self.fail()
|
||||
except dispatch.DispatchException, e:
|
||||
self.failUnless(str(e).find('No handler') != -1)
|
||||
except Exception:
|
||||
self.fail()
|
||||
|
||||
def test_transfer_data_handler_exception(self):
|
||||
dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
|
||||
request = mock.MockRequest(connection=mock.MockConn(''))
|
||||
request.ws_resource = '/sub/exception_in_transfer'
|
||||
request.ws_protocol = 'p3'
|
||||
try:
|
||||
dispatcher.transfer_data(request)
|
||||
self.fail()
|
||||
except Exception, e:
|
||||
self.failUnless(str(e).find('Intentional') != -1,
|
||||
'Unexpected exception: %s' % e)
|
||||
|
||||
def test_abort_transfer_data(self):
|
||||
dispatcher = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
|
||||
request = mock.MockRequest()
|
||||
request.ws_resource = '/abort_by_user'
|
||||
self.assertRaises(handshake.AbortedByUserException,
|
||||
dispatcher.transfer_data, request)
|
||||
|
||||
def test_scan_dir(self):
|
||||
disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
|
||||
self.assertEqual(4, len(disp._handler_suite_map))
|
||||
self.failUnless('/origin_check' in disp._handler_suite_map)
|
||||
self.failUnless(
|
||||
'/sub/exception_in_transfer' in disp._handler_suite_map)
|
||||
self.failUnless('/sub/plain' in disp._handler_suite_map)
|
||||
|
||||
def test_scan_sub_dir(self):
|
||||
disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, _TEST_HANDLERS_SUB_DIR)
|
||||
self.assertEqual(2, len(disp._handler_suite_map))
|
||||
self.failIf('/origin_check' in disp._handler_suite_map)
|
||||
self.failUnless(
|
||||
'/sub/exception_in_transfer' in disp._handler_suite_map)
|
||||
self.failUnless('/sub/plain' in disp._handler_suite_map)
|
||||
|
||||
def test_scan_sub_dir_as_root(self):
|
||||
disp = dispatch.Dispatcher(_TEST_HANDLERS_SUB_DIR,
|
||||
_TEST_HANDLERS_SUB_DIR)
|
||||
self.assertEqual(2, len(disp._handler_suite_map))
|
||||
self.failIf('/origin_check' in disp._handler_suite_map)
|
||||
self.failIf('/sub/exception_in_transfer' in disp._handler_suite_map)
|
||||
self.failIf('/sub/plain' in disp._handler_suite_map)
|
||||
self.failUnless('/exception_in_transfer' in disp._handler_suite_map)
|
||||
self.failUnless('/plain' in disp._handler_suite_map)
|
||||
|
||||
def test_scan_dir_must_under_root(self):
|
||||
dispatch.Dispatcher('a/b', 'a/b/c') # OK
|
||||
dispatch.Dispatcher('a/b///', 'a/b') # OK
|
||||
self.assertRaises(dispatch.DispatchException,
|
||||
dispatch.Dispatcher, 'a/b/c', 'a/b')
|
||||
|
||||
def test_resource_path_alias(self):
|
||||
disp = dispatch.Dispatcher(_TEST_HANDLERS_DIR, None)
|
||||
disp.add_resource_path_alias('/', '/origin_check')
|
||||
self.assertEqual(5, len(disp._handler_suite_map))
|
||||
self.failUnless('/origin_check' in disp._handler_suite_map)
|
||||
self.failUnless(
|
||||
'/sub/exception_in_transfer' in disp._handler_suite_map)
|
||||
self.failUnless('/sub/plain' in disp._handler_suite_map)
|
||||
self.failUnless('/' in disp._handler_suite_map)
|
||||
self.assertRaises(dispatch.DispatchException,
|
||||
disp.add_resource_path_alias, '/alias', '/not-exist')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,753 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""End-to-end tests for pywebsocket. Tests standalone.py by default. You
|
||||
can also test mod_pywebsocket hosted on an Apache server by setting
|
||||
_use_external_server to True and modifying _external_server_port to point to
|
||||
the port on which the Apache server is running.
|
||||
"""
|
||||
|
||||
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import set_sys_path # Update sys.path to locate mod_pywebsocket module.
|
||||
|
||||
from test import client_for_testing
|
||||
from test import mux_client_for_testing
|
||||
|
||||
|
||||
# Special message that tells the echo server to start closing handshake
|
||||
_GOODBYE_MESSAGE = 'Goodbye'
|
||||
|
||||
_SERVER_WARMUP_IN_SEC = 0.2
|
||||
|
||||
# If you want to use external server to run end to end tests, set following
|
||||
# parameters correctly.
|
||||
_use_external_server = False
|
||||
_external_server_port = 0
|
||||
|
||||
|
||||
# Test body functions
|
||||
def _echo_check_procedure(client):
|
||||
client.connect()
|
||||
|
||||
client.send_message('test')
|
||||
client.assert_receive('test')
|
||||
client.send_message('helloworld')
|
||||
client.assert_receive('helloworld')
|
||||
|
||||
client.send_close()
|
||||
client.assert_receive_close()
|
||||
|
||||
client.assert_connection_closed()
|
||||
|
||||
|
||||
def _echo_check_procedure_with_binary(client):
|
||||
client.connect()
|
||||
|
||||
client.send_message('binary', binary=True)
|
||||
client.assert_receive('binary', binary=True)
|
||||
client.send_message('\x00\x80\xfe\xff\x00\x80', binary=True)
|
||||
client.assert_receive('\x00\x80\xfe\xff\x00\x80', binary=True)
|
||||
|
||||
client.send_close()
|
||||
client.assert_receive_close()
|
||||
|
||||
client.assert_connection_closed()
|
||||
|
||||
|
||||
def _echo_check_procedure_with_goodbye(client):
|
||||
client.connect()
|
||||
|
||||
client.send_message('test')
|
||||
client.assert_receive('test')
|
||||
|
||||
client.send_message(_GOODBYE_MESSAGE)
|
||||
client.assert_receive(_GOODBYE_MESSAGE)
|
||||
|
||||
client.assert_receive_close()
|
||||
client.send_close()
|
||||
|
||||
client.assert_connection_closed()
|
||||
|
||||
|
||||
def _echo_check_procedure_with_code_and_reason(client, code, reason):
|
||||
client.connect()
|
||||
|
||||
client.send_close(code, reason)
|
||||
client.assert_receive_close(code, reason)
|
||||
|
||||
client.assert_connection_closed()
|
||||
|
||||
|
||||
def _unmasked_frame_check_procedure(client):
|
||||
client.connect()
|
||||
|
||||
client.send_message('test', mask=False)
|
||||
client.assert_receive_close(client_for_testing.STATUS_PROTOCOL_ERROR, '')
|
||||
|
||||
client.assert_connection_closed()
|
||||
|
||||
|
||||
def _mux_echo_check_procedure(mux_client):
|
||||
mux_client.connect()
|
||||
mux_client.send_flow_control(1, 1024)
|
||||
|
||||
logical_channel_options = client_for_testing.ClientOptions()
|
||||
logical_channel_options.server_host = 'localhost'
|
||||
logical_channel_options.server_port = 80
|
||||
logical_channel_options.origin = 'http://localhost'
|
||||
logical_channel_options.resource = '/echo'
|
||||
mux_client.add_channel(2, logical_channel_options)
|
||||
mux_client.send_flow_control(2, 1024)
|
||||
|
||||
mux_client.send_message(2, 'test')
|
||||
mux_client.assert_receive(2, 'test')
|
||||
|
||||
mux_client.add_channel(3, logical_channel_options)
|
||||
mux_client.send_flow_control(3, 1024)
|
||||
|
||||
mux_client.send_message(2, 'hello')
|
||||
mux_client.send_message(3, 'world')
|
||||
mux_client.assert_receive(2, 'hello')
|
||||
mux_client.assert_receive(3, 'world')
|
||||
|
||||
# Don't send close message on channel id 1 so that server-initiated
|
||||
# closing handshake won't occur.
|
||||
mux_client.send_close(2)
|
||||
mux_client.send_close(3)
|
||||
mux_client.assert_receive_close(2)
|
||||
mux_client.assert_receive_close(3)
|
||||
|
||||
mux_client.send_physical_connection_close()
|
||||
mux_client.assert_physical_connection_receive_close()
|
||||
|
||||
|
||||
class EndToEndTestBase(unittest.TestCase):
|
||||
"""Base class for end-to-end tests that launch pywebsocket standalone
|
||||
server as a separate process, connect to it using the client_for_testing
|
||||
module, and check if the server behaves correctly by exchanging opening
|
||||
handshake and frames over a TCP connection.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.server_stderr = None
|
||||
self.top_dir = os.path.join(os.path.split(__file__)[0], '..')
|
||||
os.putenv('PYTHONPATH', os.path.pathsep.join(sys.path))
|
||||
self.standalone_command = os.path.join(
|
||||
self.top_dir, 'mod_pywebsocket', 'standalone.py')
|
||||
self.document_root = os.path.join(self.top_dir, 'example')
|
||||
s = socket.socket()
|
||||
s.bind(('localhost', 0))
|
||||
(_, self.test_port) = s.getsockname()
|
||||
s.close()
|
||||
|
||||
self._options = client_for_testing.ClientOptions()
|
||||
self._options.server_host = 'localhost'
|
||||
self._options.origin = 'http://localhost'
|
||||
self._options.resource = '/echo'
|
||||
|
||||
# TODO(toyoshim): Eliminate launching a standalone server on using
|
||||
# external server.
|
||||
|
||||
if _use_external_server:
|
||||
self._options.server_port = _external_server_port
|
||||
else:
|
||||
self._options.server_port = self.test_port
|
||||
|
||||
# TODO(tyoshino): Use tearDown to kill the server.
|
||||
|
||||
def _run_python_command(self, commandline, stdout=None, stderr=None):
|
||||
return subprocess.Popen([sys.executable] + commandline, close_fds=True,
|
||||
stdout=stdout, stderr=stderr)
|
||||
|
||||
def _run_server(self):
|
||||
args = [self.standalone_command,
|
||||
'-H', 'localhost',
|
||||
'-V', 'localhost',
|
||||
'-p', str(self.test_port),
|
||||
'-P', str(self.test_port),
|
||||
'-d', self.document_root]
|
||||
|
||||
# Inherit the level set to the root logger by test runner.
|
||||
root_logger = logging.getLogger()
|
||||
log_level = root_logger.getEffectiveLevel()
|
||||
if log_level != logging.NOTSET:
|
||||
args.append('--log-level')
|
||||
args.append(logging.getLevelName(log_level).lower())
|
||||
|
||||
return self._run_python_command(args,
|
||||
stderr=self.server_stderr)
|
||||
|
||||
def _kill_process(self, pid):
|
||||
if sys.platform in ('win32', 'cygwin'):
|
||||
subprocess.call(
|
||||
('taskkill.exe', '/f', '/pid', str(pid)), close_fds=True)
|
||||
else:
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
|
||||
|
||||
class EndToEndHyBiTest(EndToEndTestBase):
|
||||
def setUp(self):
|
||||
EndToEndTestBase.setUp(self)
|
||||
|
||||
def _run_test_with_client_options(self, test_function, options):
|
||||
server = self._run_server()
|
||||
try:
|
||||
# TODO(tyoshino): add some logic to poll the server until it
|
||||
# becomes ready
|
||||
time.sleep(_SERVER_WARMUP_IN_SEC)
|
||||
|
||||
client = client_for_testing.create_client(options)
|
||||
try:
|
||||
test_function(client)
|
||||
finally:
|
||||
client.close_socket()
|
||||
finally:
|
||||
self._kill_process(server.pid)
|
||||
|
||||
def _run_test(self, test_function):
|
||||
self._run_test_with_client_options(test_function, self._options)
|
||||
|
||||
def _run_deflate_frame_test(self, test_function):
|
||||
server = self._run_server()
|
||||
try:
|
||||
time.sleep(_SERVER_WARMUP_IN_SEC)
|
||||
|
||||
self._options.enable_deflate_frame()
|
||||
client = client_for_testing.create_client(self._options)
|
||||
try:
|
||||
test_function(client)
|
||||
finally:
|
||||
client.close_socket()
|
||||
finally:
|
||||
self._kill_process(server.pid)
|
||||
|
||||
def _run_permessage_deflate_test(
|
||||
self, offer, response_checker, test_function):
|
||||
server = self._run_server()
|
||||
try:
|
||||
time.sleep(_SERVER_WARMUP_IN_SEC)
|
||||
|
||||
self._options.extensions += offer
|
||||
self._options.check_permessage_deflate = response_checker
|
||||
client = client_for_testing.create_client(self._options)
|
||||
|
||||
try:
|
||||
client.connect()
|
||||
|
||||
if test_function is not None:
|
||||
test_function(client)
|
||||
|
||||
client.assert_connection_closed()
|
||||
finally:
|
||||
client.close_socket()
|
||||
finally:
|
||||
self._kill_process(server.pid)
|
||||
|
||||
def _run_close_with_code_and_reason_test(self, test_function, code,
|
||||
reason):
|
||||
server = self._run_server()
|
||||
try:
|
||||
time.sleep(_SERVER_WARMUP_IN_SEC)
|
||||
|
||||
client = client_for_testing.create_client(self._options)
|
||||
try:
|
||||
test_function(client, code, reason)
|
||||
finally:
|
||||
client.close_socket()
|
||||
finally:
|
||||
self._kill_process(server.pid)
|
||||
|
||||
def _run_http_fallback_test(self, options, status):
|
||||
server = self._run_server()
|
||||
try:
|
||||
time.sleep(_SERVER_WARMUP_IN_SEC)
|
||||
|
||||
client = client_for_testing.create_client(options)
|
||||
try:
|
||||
client.connect()
|
||||
self.fail('Could not catch HttpStatusException')
|
||||
except client_for_testing.HttpStatusException, e:
|
||||
self.assertEqual(status, e.status)
|
||||
except Exception, e:
|
||||
self.fail('Catch unexpected exception')
|
||||
finally:
|
||||
client.close_socket()
|
||||
finally:
|
||||
self._kill_process(server.pid)
|
||||
|
||||
def _run_mux_test(self, test_function):
|
||||
server = self._run_server()
|
||||
try:
|
||||
time.sleep(_SERVER_WARMUP_IN_SEC)
|
||||
|
||||
client = mux_client_for_testing.MuxClient(self._options)
|
||||
try:
|
||||
test_function(client)
|
||||
finally:
|
||||
client.close_socket()
|
||||
finally:
|
||||
self._kill_process(server.pid)
|
||||
|
||||
def test_echo(self):
|
||||
self._run_test(_echo_check_procedure)
|
||||
|
||||
def test_echo_binary(self):
|
||||
self._run_test(_echo_check_procedure_with_binary)
|
||||
|
||||
def test_echo_server_close(self):
|
||||
self._run_test(_echo_check_procedure_with_goodbye)
|
||||
|
||||
def test_unmasked_frame(self):
|
||||
self._run_test(_unmasked_frame_check_procedure)
|
||||
|
||||
def test_echo_deflate_frame(self):
|
||||
self._run_deflate_frame_test(_echo_check_procedure)
|
||||
|
||||
def test_echo_deflate_frame_server_close(self):
|
||||
self._run_deflate_frame_test(
|
||||
_echo_check_procedure_with_goodbye)
|
||||
|
||||
def test_echo_permessage_deflate(self):
|
||||
def test_function(client):
|
||||
# From the examples in the spec.
|
||||
compressed_hello = '\xf2\x48\xcd\xc9\xc9\x07\x00'
|
||||
client._stream.send_data(
|
||||
compressed_hello,
|
||||
client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
client._stream.assert_receive_binary(
|
||||
compressed_hello,
|
||||
opcode=client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
|
||||
client.send_close()
|
||||
client.assert_receive_close()
|
||||
|
||||
def response_checker(parameter):
|
||||
self.assertEquals('permessage-deflate', parameter.name())
|
||||
self.assertEquals([], parameter.get_parameters())
|
||||
|
||||
self._run_permessage_deflate_test(
|
||||
['permessage-deflate'],
|
||||
response_checker,
|
||||
test_function)
|
||||
|
||||
def test_echo_permessage_deflate_two_frames(self):
|
||||
def test_function(client):
|
||||
# From the examples in the spec.
|
||||
client._stream.send_data(
|
||||
'\xf2\x48\xcd',
|
||||
client_for_testing.OPCODE_TEXT,
|
||||
end=False,
|
||||
rsv1=1)
|
||||
client._stream.send_data(
|
||||
'\xc9\xc9\x07\x00',
|
||||
client_for_testing.OPCODE_TEXT)
|
||||
client._stream.assert_receive_binary(
|
||||
'\xf2\x48\xcd\xc9\xc9\x07\x00',
|
||||
opcode=client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
|
||||
client.send_close()
|
||||
client.assert_receive_close()
|
||||
|
||||
def response_checker(parameter):
|
||||
self.assertEquals('permessage-deflate', parameter.name())
|
||||
self.assertEquals([], parameter.get_parameters())
|
||||
|
||||
self._run_permessage_deflate_test(
|
||||
['permessage-deflate'],
|
||||
response_checker,
|
||||
test_function)
|
||||
|
||||
def test_echo_permessage_deflate_two_messages(self):
|
||||
def test_function(client):
|
||||
# From the examples in the spec.
|
||||
client._stream.send_data(
|
||||
'\xf2\x48\xcd\xc9\xc9\x07\x00',
|
||||
client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
client._stream.send_data(
|
||||
'\xf2\x00\x11\x00\x00',
|
||||
client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
client._stream.assert_receive_binary(
|
||||
'\xf2\x48\xcd\xc9\xc9\x07\x00',
|
||||
opcode=client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
client._stream.assert_receive_binary(
|
||||
'\xf2\x00\x11\x00\x00',
|
||||
opcode=client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
|
||||
client.send_close()
|
||||
client.assert_receive_close()
|
||||
|
||||
def response_checker(parameter):
|
||||
self.assertEquals('permessage-deflate', parameter.name())
|
||||
self.assertEquals([], parameter.get_parameters())
|
||||
|
||||
self._run_permessage_deflate_test(
|
||||
['permessage-deflate'],
|
||||
response_checker,
|
||||
test_function)
|
||||
|
||||
def test_echo_permessage_deflate_two_msgs_server_no_context_takeover(self):
|
||||
def test_function(client):
|
||||
# From the examples in the spec.
|
||||
client._stream.send_data(
|
||||
'\xf2\x48\xcd\xc9\xc9\x07\x00',
|
||||
client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
client._stream.send_data(
|
||||
'\xf2\x00\x11\x00\x00',
|
||||
client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
client._stream.assert_receive_binary(
|
||||
'\xf2\x48\xcd\xc9\xc9\x07\x00',
|
||||
opcode=client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
client._stream.assert_receive_binary(
|
||||
'\xf2\x48\xcd\xc9\xc9\x07\x00',
|
||||
opcode=client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
|
||||
client.send_close()
|
||||
client.assert_receive_close()
|
||||
|
||||
def response_checker(parameter):
|
||||
self.assertEquals('permessage-deflate', parameter.name())
|
||||
self.assertEquals([('server_no_context_takeover', None)],
|
||||
parameter.get_parameters())
|
||||
|
||||
self._run_permessage_deflate_test(
|
||||
['permessage-deflate; server_no_context_takeover'],
|
||||
response_checker,
|
||||
test_function)
|
||||
|
||||
def test_echo_permessage_deflate_preference(self):
|
||||
def test_function(client):
|
||||
# From the examples in the spec.
|
||||
compressed_hello = '\xf2\x48\xcd\xc9\xc9\x07\x00'
|
||||
client._stream.send_data(
|
||||
compressed_hello,
|
||||
client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
client._stream.assert_receive_binary(
|
||||
compressed_hello,
|
||||
opcode=client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
|
||||
client.send_close()
|
||||
client.assert_receive_close()
|
||||
|
||||
def response_checker(parameter):
|
||||
self.assertEquals('permessage-deflate', parameter.name())
|
||||
self.assertEquals([], parameter.get_parameters())
|
||||
|
||||
self._run_permessage_deflate_test(
|
||||
['permessage-deflate', 'deflate-frame'],
|
||||
response_checker,
|
||||
test_function)
|
||||
|
||||
def test_echo_permessage_deflate_with_parameters(self):
|
||||
def test_function(client):
|
||||
# From the examples in the spec.
|
||||
compressed_hello = '\xf2\x48\xcd\xc9\xc9\x07\x00'
|
||||
client._stream.send_data(
|
||||
compressed_hello,
|
||||
client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
client._stream.assert_receive_binary(
|
||||
compressed_hello,
|
||||
opcode=client_for_testing.OPCODE_TEXT,
|
||||
rsv1=1)
|
||||
|
||||
client.send_close()
|
||||
client.assert_receive_close()
|
||||
|
||||
def response_checker(parameter):
|
||||
self.assertEquals('permessage-deflate', parameter.name())
|
||||
self.assertEquals([('server_max_window_bits', '10'),
|
||||
('server_no_context_takeover', None)],
|
||||
parameter.get_parameters())
|
||||
|
||||
self._run_permessage_deflate_test(
|
||||
['permessage-deflate; server_max_window_bits=10; '
|
||||
'server_no_context_takeover'],
|
||||
response_checker,
|
||||
test_function)
|
||||
|
||||
def test_echo_permessage_deflate_with_bad_server_max_window_bits(self):
|
||||
def test_function(client):
|
||||
client.send_close()
|
||||
client.assert_receive_close()
|
||||
|
||||
def response_checker(parameter):
|
||||
raise Exception('Unexpected acceptance of permessage-deflate')
|
||||
|
||||
self._run_permessage_deflate_test(
|
||||
['permessage-deflate; server_max_window_bits=3000000'],
|
||||
response_checker,
|
||||
test_function)
|
||||
|
||||
def test_echo_permessage_deflate_with_bad_server_max_window_bits(self):
|
||||
def test_function(client):
|
||||
client.send_close()
|
||||
client.assert_receive_close()
|
||||
|
||||
def response_checker(parameter):
|
||||
raise Exception('Unexpected acceptance of permessage-deflate')
|
||||
|
||||
self._run_permessage_deflate_test(
|
||||
['permessage-deflate; server_max_window_bits=3000000'],
|
||||
response_checker,
|
||||
test_function)
|
||||
|
||||
def test_echo_permessage_deflate_with_undefined_parameter(self):
|
||||
def test_function(client):
|
||||
client.send_close()
|
||||
client.assert_receive_close()
|
||||
|
||||
def response_checker(parameter):
|
||||
raise Exception('Unexpected acceptance of permessage-deflate')
|
||||
|
||||
self._run_permessage_deflate_test(
|
||||
['permessage-deflate; foo=bar'],
|
||||
response_checker,
|
||||
test_function)
|
||||
|
||||
def test_echo_close_with_code_and_reason(self):
|
||||
self._options.resource = '/close'
|
||||
self._run_close_with_code_and_reason_test(
|
||||
_echo_check_procedure_with_code_and_reason, 3333, 'sunsunsunsun')
|
||||
|
||||
def test_echo_close_with_empty_body(self):
|
||||
self._options.resource = '/close'
|
||||
self._run_close_with_code_and_reason_test(
|
||||
_echo_check_procedure_with_code_and_reason, None, '')
|
||||
|
||||
def test_mux_echo(self):
|
||||
self._run_mux_test(_mux_echo_check_procedure)
|
||||
|
||||
def test_close_on_protocol_error(self):
|
||||
"""Tests that the server sends a close frame with protocol error status
|
||||
code when the client sends data with some protocol error.
|
||||
"""
|
||||
|
||||
def test_function(client):
|
||||
client.connect()
|
||||
|
||||
# Intermediate frame without any preceding start of fragmentation
|
||||
# frame.
|
||||
client.send_frame_of_arbitrary_bytes('\x80\x80', '')
|
||||
client.assert_receive_close(
|
||||
client_for_testing.STATUS_PROTOCOL_ERROR)
|
||||
|
||||
self._run_test(test_function)
|
||||
|
||||
def test_close_on_unsupported_frame(self):
|
||||
"""Tests that the server sends a close frame with unsupported operation
|
||||
status code when the client sends data asking some operation that is
|
||||
not supported by the server.
|
||||
"""
|
||||
|
||||
def test_function(client):
|
||||
client.connect()
|
||||
|
||||
# Text frame with RSV3 bit raised.
|
||||
client.send_frame_of_arbitrary_bytes('\x91\x80', '')
|
||||
client.assert_receive_close(
|
||||
client_for_testing.STATUS_UNSUPPORTED_DATA)
|
||||
|
||||
self._run_test(test_function)
|
||||
|
||||
def test_close_on_invalid_frame(self):
|
||||
"""Tests that the server sends a close frame with invalid frame payload
|
||||
data status code when the client sends an invalid frame like containing
|
||||
invalid UTF-8 character.
|
||||
"""
|
||||
|
||||
def test_function(client):
|
||||
client.connect()
|
||||
|
||||
# Text frame with invalid UTF-8 string.
|
||||
client.send_message('\x80', raw=True)
|
||||
client.assert_receive_close(
|
||||
client_for_testing.STATUS_INVALID_FRAME_PAYLOAD_DATA)
|
||||
|
||||
self._run_test(test_function)
|
||||
|
||||
def test_close_on_internal_endpoint_error(self):
|
||||
"""Tests that the server sends a close frame with internal endpoint
|
||||
error status code when the handler does bad operation.
|
||||
"""
|
||||
|
||||
self._options.resource = '/internal_error'
|
||||
|
||||
def test_function(client):
|
||||
client.connect()
|
||||
client.assert_receive_close(
|
||||
client_for_testing.STATUS_INTERNAL_ENDPOINT_ERROR)
|
||||
|
||||
self._run_test(test_function)
|
||||
|
||||
# TODO(toyoshim): Add tests to verify invalid absolute uri handling like
|
||||
# host unmatch, port unmatch and invalid port description (':' without port
|
||||
# number).
|
||||
|
||||
def test_absolute_uri(self):
|
||||
"""Tests absolute uri request."""
|
||||
|
||||
options = self._options
|
||||
options.resource = 'ws://localhost:%d/echo' % options.server_port
|
||||
self._run_test_with_client_options(_echo_check_procedure, options)
|
||||
|
||||
def test_origin_check(self):
|
||||
"""Tests http fallback on origin check fail."""
|
||||
|
||||
options = self._options
|
||||
options.resource = '/origin_check'
|
||||
# Server shows warning message for http 403 fallback. This warning
|
||||
# message is confusing. Following pipe disposes warning messages.
|
||||
self.server_stderr = subprocess.PIPE
|
||||
self._run_http_fallback_test(options, 403)
|
||||
|
||||
def test_version_check(self):
|
||||
"""Tests http fallback on version check fail."""
|
||||
|
||||
options = self._options
|
||||
options.version = 99
|
||||
self._run_http_fallback_test(options, 400)
|
||||
|
||||
|
||||
class EndToEndHyBi00Test(EndToEndTestBase):
|
||||
def setUp(self):
|
||||
EndToEndTestBase.setUp(self)
|
||||
|
||||
def _run_test(self, test_function):
|
||||
server = self._run_server()
|
||||
try:
|
||||
time.sleep(_SERVER_WARMUP_IN_SEC)
|
||||
|
||||
client = client_for_testing.create_client_hybi00(self._options)
|
||||
try:
|
||||
test_function(client)
|
||||
finally:
|
||||
client.close_socket()
|
||||
finally:
|
||||
self._kill_process(server.pid)
|
||||
|
||||
def test_echo(self):
|
||||
self._run_test(_echo_check_procedure)
|
||||
|
||||
def test_echo_server_close(self):
|
||||
self._run_test(_echo_check_procedure_with_goodbye)
|
||||
|
||||
|
||||
class EndToEndTestWithEchoClient(EndToEndTestBase):
|
||||
def setUp(self):
|
||||
EndToEndTestBase.setUp(self)
|
||||
|
||||
def _check_example_echo_client_result(
|
||||
self, expected, stdoutdata, stderrdata):
|
||||
actual = stdoutdata.decode("utf-8")
|
||||
if actual != expected:
|
||||
raise Exception('Unexpected result on example echo client: '
|
||||
'%r (expected) vs %r (actual)' %
|
||||
(expected, actual))
|
||||
if stderrdata is not None:
|
||||
raise Exception('Unexpected error message on example echo '
|
||||
'client: %r' % stderrdata)
|
||||
|
||||
def test_example_echo_client(self):
|
||||
"""Tests that the echo_client.py example can talk with the server."""
|
||||
|
||||
server = self._run_server()
|
||||
try:
|
||||
time.sleep(_SERVER_WARMUP_IN_SEC)
|
||||
|
||||
client_command = os.path.join(
|
||||
self.top_dir, 'example', 'echo_client.py')
|
||||
|
||||
# Expected output for the default messages.
|
||||
default_expectation = ('Send: Hello\n' 'Recv: Hello\n'
|
||||
u'Send: \u65e5\u672c\n' u'Recv: \u65e5\u672c\n'
|
||||
'Send close\n' 'Recv ack\n')
|
||||
|
||||
args = [client_command,
|
||||
'-p', str(self._options.server_port)]
|
||||
client = self._run_python_command(args, stdout=subprocess.PIPE)
|
||||
stdoutdata, stderrdata = client.communicate()
|
||||
self._check_example_echo_client_result(
|
||||
default_expectation, stdoutdata, stderrdata)
|
||||
|
||||
# Process a big message for which extended payload length is used.
|
||||
# To handle extended payload length, ws_version attribute will be
|
||||
# accessed. This test checks that ws_version is correctly set.
|
||||
big_message = 'a' * 1024
|
||||
args = [client_command,
|
||||
'-p', str(self._options.server_port),
|
||||
'-m', big_message]
|
||||
client = self._run_python_command(args, stdout=subprocess.PIPE)
|
||||
stdoutdata, stderrdata = client.communicate()
|
||||
expected = ('Send: %s\nRecv: %s\nSend close\nRecv ack\n' %
|
||||
(big_message, big_message))
|
||||
self._check_example_echo_client_result(
|
||||
expected, stdoutdata, stderrdata)
|
||||
|
||||
# Test the permessage-deflate extension.
|
||||
args = [client_command,
|
||||
'-p', str(self._options.server_port),
|
||||
'--use_permessage_deflate']
|
||||
client = self._run_python_command(args, stdout=subprocess.PIPE)
|
||||
stdoutdata, stderrdata = client.communicate()
|
||||
self._check_example_echo_client_result(
|
||||
default_expectation, stdoutdata, stderrdata)
|
||||
finally:
|
||||
self._kill_process(server.pid)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Tests for extensions module."""
|
||||
|
||||
|
||||
import unittest
|
||||
import zlib
|
||||
|
||||
import set_sys_path # Update sys.path to locate mod_pywebsocket module.
|
||||
|
||||
from mod_pywebsocket import common
|
||||
from mod_pywebsocket import extensions
|
||||
|
||||
|
||||
class ExtensionsTest(unittest.TestCase):
|
||||
"""A unittest for non-class methods in extensions.py"""
|
||||
|
||||
def test_parse_window_bits(self):
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, None)
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, 'foobar')
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, ' 8 ')
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, 'a8a')
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, '00000')
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, '00008')
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, '0x8')
|
||||
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, '9.5')
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, '8.0')
|
||||
|
||||
self.assertTrue(extensions._parse_window_bits, '8')
|
||||
self.assertTrue(extensions._parse_window_bits, '15')
|
||||
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, '-8')
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, '0')
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, '7')
|
||||
|
||||
self.assertRaises(ValueError, extensions._parse_window_bits, '16')
|
||||
self.assertRaises(
|
||||
ValueError, extensions._parse_window_bits, '10000000')
|
||||
|
||||
|
||||
class CompressionMethodParameterParserTest(unittest.TestCase):
|
||||
"""A unittest for _parse_compression_method which parses the compression
|
||||
method description used by perframe-compression and permessage-compression
|
||||
extension in their "method" extension parameter.
|
||||
"""
|
||||
|
||||
def test_parse_method_simple(self):
|
||||
method_list = extensions._parse_compression_method('foo')
|
||||
self.assertEqual(1, len(method_list))
|
||||
method = method_list[0]
|
||||
self.assertEqual('foo', method.name())
|
||||
self.assertEqual(0, len(method.get_parameters()))
|
||||
|
||||
def test_parse_method_with_parameter(self):
|
||||
method_list = extensions._parse_compression_method('foo; x; y=10')
|
||||
self.assertEqual(1, len(method_list))
|
||||
method = method_list[0]
|
||||
self.assertEqual('foo', method.name())
|
||||
self.assertEqual(2, len(method.get_parameters()))
|
||||
self.assertTrue(method.has_parameter('x'))
|
||||
self.assertEqual(None, method.get_parameter_value('x'))
|
||||
self.assertTrue(method.has_parameter('y'))
|
||||
self.assertEqual('10', method.get_parameter_value('y'))
|
||||
|
||||
def test_parse_method_with_quoted_parameter(self):
|
||||
method_list = extensions._parse_compression_method(
|
||||
'foo; x="Hello World"; y=10')
|
||||
self.assertEqual(1, len(method_list))
|
||||
method = method_list[0]
|
||||
self.assertEqual('foo', method.name())
|
||||
self.assertEqual(2, len(method.get_parameters()))
|
||||
self.assertTrue(method.has_parameter('x'))
|
||||
self.assertEqual('Hello World', method.get_parameter_value('x'))
|
||||
self.assertTrue(method.has_parameter('y'))
|
||||
self.assertEqual('10', method.get_parameter_value('y'))
|
||||
|
||||
def test_parse_method_multiple(self):
|
||||
method_list = extensions._parse_compression_method('foo, bar')
|
||||
self.assertEqual(2, len(method_list))
|
||||
self.assertEqual('foo', method_list[0].name())
|
||||
self.assertEqual(0, len(method_list[0].get_parameters()))
|
||||
self.assertEqual('bar', method_list[1].name())
|
||||
self.assertEqual(0, len(method_list[1].get_parameters()))
|
||||
|
||||
def test_parse_method_multiple_methods_with_quoted_parameter(self):
|
||||
method_list = extensions._parse_compression_method(
|
||||
'foo; x="Hello World", bar; y=10')
|
||||
self.assertEqual(2, len(method_list))
|
||||
self.assertEqual('foo', method_list[0].name())
|
||||
self.assertEqual(1, len(method_list[0].get_parameters()))
|
||||
self.assertTrue(method_list[0].has_parameter('x'))
|
||||
self.assertEqual('Hello World',
|
||||
method_list[0].get_parameter_value('x'))
|
||||
self.assertEqual('bar', method_list[1].name())
|
||||
self.assertEqual(1, len(method_list[1].get_parameters()))
|
||||
self.assertTrue(method_list[1].has_parameter('y'))
|
||||
self.assertEqual('10', method_list[1].get_parameter_value('y'))
|
||||
|
||||
def test_create_method_desc_simple(self):
|
||||
params = common.ExtensionParameter('foo')
|
||||
desc = extensions._create_accepted_method_desc('foo',
|
||||
params.get_parameters())
|
||||
self.assertEqual('foo', desc)
|
||||
|
||||
def test_create_method_desc_with_parameters(self):
|
||||
params = common.ExtensionParameter('foo')
|
||||
params.add_parameter('x', 'Hello, World')
|
||||
params.add_parameter('y', '10')
|
||||
desc = extensions._create_accepted_method_desc('foo',
|
||||
params.get_parameters())
|
||||
self.assertEqual('foo; x="Hello, World"; y=10', desc)
|
||||
|
||||
|
||||
class DeflateFrameExtensionProcessorParsingTest(unittest.TestCase):
|
||||
"""A unittest for checking that DeflateFrameExtensionProcessor parses given
|
||||
extension parameter correctly.
|
||||
"""
|
||||
|
||||
def test_registry(self):
|
||||
processor = extensions.get_extension_processor(
|
||||
common.ExtensionParameter('deflate-frame'))
|
||||
self.assertIsInstance(processor,
|
||||
extensions.DeflateFrameExtensionProcessor)
|
||||
|
||||
processor = extensions.get_extension_processor(
|
||||
common.ExtensionParameter('x-webkit-deflate-frame'))
|
||||
self.assertIsInstance(processor,
|
||||
extensions.DeflateFrameExtensionProcessor)
|
||||
|
||||
def test_minimal_offer(self):
|
||||
processor = extensions.DeflateFrameExtensionProcessor(
|
||||
common.ExtensionParameter('perframe-deflate'))
|
||||
|
||||
response = processor.get_extension_response()
|
||||
self.assertEqual('perframe-deflate', response.name())
|
||||
self.assertEqual(0, len(response.get_parameters()))
|
||||
|
||||
self.assertEqual(zlib.MAX_WBITS,
|
||||
processor._rfc1979_deflater._window_bits)
|
||||
self.assertFalse(processor._rfc1979_deflater._no_context_takeover)
|
||||
|
||||
def test_offer_with_max_window_bits(self):
|
||||
parameter = common.ExtensionParameter('perframe-deflate')
|
||||
parameter.add_parameter('max_window_bits', '10')
|
||||
processor = extensions.DeflateFrameExtensionProcessor(parameter)
|
||||
|
||||
response = processor.get_extension_response()
|
||||
self.assertEqual('perframe-deflate', response.name())
|
||||
self.assertEqual(0, len(response.get_parameters()))
|
||||
|
||||
self.assertEqual(10, processor._rfc1979_deflater._window_bits)
|
||||
|
||||
def test_offer_with_out_of_range_max_window_bits(self):
|
||||
parameter = common.ExtensionParameter('perframe-deflate')
|
||||
parameter.add_parameter('max_window_bits', '0')
|
||||
processor = extensions.DeflateFrameExtensionProcessor(parameter)
|
||||
|
||||
self.assertIsNone(processor.get_extension_response())
|
||||
|
||||
def test_offer_with_max_window_bits_without_value(self):
|
||||
parameter = common.ExtensionParameter('perframe-deflate')
|
||||
parameter.add_parameter('max_window_bits', None)
|
||||
processor = extensions.DeflateFrameExtensionProcessor(parameter)
|
||||
|
||||
self.assertIsNone(processor.get_extension_response())
|
||||
|
||||
def test_offer_with_no_context_takeover(self):
|
||||
parameter = common.ExtensionParameter('perframe-deflate')
|
||||
parameter.add_parameter('no_context_takeover', None)
|
||||
processor = extensions.DeflateFrameExtensionProcessor(parameter)
|
||||
|
||||
response = processor.get_extension_response()
|
||||
self.assertEqual('perframe-deflate', response.name())
|
||||
self.assertEqual(0, len(response.get_parameters()))
|
||||
|
||||
self.assertTrue(processor._rfc1979_deflater._no_context_takeover)
|
||||
|
||||
def test_offer_with_no_context_takeover_with_value(self):
|
||||
parameter = common.ExtensionParameter('perframe-deflate')
|
||||
parameter.add_parameter('no_context_takeover', 'foobar')
|
||||
processor = extensions.DeflateFrameExtensionProcessor(parameter)
|
||||
|
||||
self.assertIsNone(processor.get_extension_response())
|
||||
|
||||
def test_offer_with_unknown_parameter(self):
|
||||
parameter = common.ExtensionParameter('perframe-deflate')
|
||||
parameter.add_parameter('foo', 'bar')
|
||||
processor = extensions.DeflateFrameExtensionProcessor(parameter)
|
||||
|
||||
response = processor.get_extension_response()
|
||||
self.assertEqual('perframe-deflate', response.name())
|
||||
self.assertEqual(0, len(response.get_parameters()))
|
||||
|
||||
|
||||
class PerMessageDeflateExtensionProcessorParsingTest(unittest.TestCase):
|
||||
"""A unittest for checking that PerMessageDeflateExtensionProcessor parses
|
||||
given extension parameter correctly.
|
||||
"""
|
||||
|
||||
def test_registry(self):
|
||||
processor = extensions.get_extension_processor(
|
||||
common.ExtensionParameter('permessage-deflate'))
|
||||
self.assertIsInstance(processor,
|
||||
extensions.PerMessageDeflateExtensionProcessor)
|
||||
|
||||
def test_minimal_offer(self):
|
||||
processor = extensions.PerMessageDeflateExtensionProcessor(
|
||||
common.ExtensionParameter('permessage-deflate'))
|
||||
|
||||
response = processor.get_extension_response()
|
||||
self.assertEqual('permessage-deflate', response.name())
|
||||
self.assertEqual(0, len(response.get_parameters()))
|
||||
|
||||
self.assertEqual(zlib.MAX_WBITS,
|
||||
processor._rfc1979_deflater._window_bits)
|
||||
self.assertFalse(processor._rfc1979_deflater._no_context_takeover)
|
||||
|
||||
def test_offer_with_max_window_bits(self):
|
||||
parameter = common.ExtensionParameter('permessage-deflate')
|
||||
parameter.add_parameter('server_max_window_bits', '10')
|
||||
processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
|
||||
|
||||
response = processor.get_extension_response()
|
||||
self.assertEqual('permessage-deflate', response.name())
|
||||
self.assertEqual([('server_max_window_bits', '10')],
|
||||
response.get_parameters())
|
||||
|
||||
self.assertEqual(10, processor._rfc1979_deflater._window_bits)
|
||||
|
||||
def test_offer_with_out_of_range_max_window_bits(self):
|
||||
parameter = common.ExtensionParameter('permessage-deflate')
|
||||
parameter.add_parameter('server_max_window_bits', '0')
|
||||
processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
|
||||
|
||||
self.assertIsNone(processor.get_extension_response())
|
||||
|
||||
def test_offer_with_max_window_bits_without_value(self):
|
||||
parameter = common.ExtensionParameter('permessage-deflate')
|
||||
parameter.add_parameter('server_max_window_bits', None)
|
||||
processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
|
||||
|
||||
self.assertIsNone(processor.get_extension_response())
|
||||
|
||||
def test_offer_with_no_context_takeover(self):
|
||||
parameter = common.ExtensionParameter('permessage-deflate')
|
||||
parameter.add_parameter('server_no_context_takeover', None)
|
||||
processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
|
||||
|
||||
response = processor.get_extension_response()
|
||||
self.assertEqual('permessage-deflate', response.name())
|
||||
self.assertEqual([('server_no_context_takeover', None)],
|
||||
response.get_parameters())
|
||||
|
||||
self.assertTrue(processor._rfc1979_deflater._no_context_takeover)
|
||||
|
||||
def test_offer_with_no_context_takeover_with_value(self):
|
||||
parameter = common.ExtensionParameter('permessage-deflate')
|
||||
parameter.add_parameter('server_no_context_takeover', 'foobar')
|
||||
processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
|
||||
|
||||
self.assertIsNone(processor.get_extension_response())
|
||||
|
||||
def test_offer_with_unknown_parameter(self):
|
||||
parameter = common.ExtensionParameter('permessage-deflate')
|
||||
parameter.add_parameter('foo', 'bar')
|
||||
processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
|
||||
|
||||
self.assertIsNone(processor.get_extension_response())
|
||||
|
||||
|
||||
class PerMessageDeflateExtensionProcessorBuildingTest(unittest.TestCase):
|
||||
"""A unittest for checking that PerMessageDeflateExtensionProcessor builds
|
||||
a response based on specified options correctly.
|
||||
"""
|
||||
|
||||
def test_response_with_max_window_bits(self):
|
||||
parameter = common.ExtensionParameter('permessage-deflate')
|
||||
parameter.add_parameter('client_max_window_bits', None)
|
||||
processor = extensions.PerMessageDeflateExtensionProcessor(parameter)
|
||||
processor.set_client_max_window_bits(10)
|
||||
|
||||
response = processor.get_extension_response()
|
||||
self.assertEqual('permessage-deflate', response.name())
|
||||
self.assertEqual([('client_max_window_bits', '10')],
|
||||
response.get_parameters())
|
||||
|
||||
def test_response_with_max_window_bits_without_client_permission(self):
|
||||
processor = extensions.PerMessageDeflateExtensionProcessor(
|
||||
common.ExtensionParameter('permessage-deflate'))
|
||||
processor.set_client_max_window_bits(10)
|
||||
|
||||
response = processor.get_extension_response()
|
||||
self.assertIsNone(response)
|
||||
|
||||
def test_response_with_true_for_no_context_takeover(self):
|
||||
processor = extensions.PerMessageDeflateExtensionProcessor(
|
||||
common.ExtensionParameter('permessage-deflate'))
|
||||
|
||||
processor.set_client_no_context_takeover(True)
|
||||
|
||||
response = processor.get_extension_response()
|
||||
self.assertEqual('permessage-deflate', response.name())
|
||||
self.assertEqual([('client_no_context_takeover', None)],
|
||||
response.get_parameters())
|
||||
|
||||
def test_response_with_false_for_no_context_takeover(self):
|
||||
processor = extensions.PerMessageDeflateExtensionProcessor(
|
||||
common.ExtensionParameter('permessage-deflate'))
|
||||
|
||||
processor.set_client_no_context_takeover(False)
|
||||
|
||||
response = processor.get_extension_response()
|
||||
self.assertEqual('permessage-deflate', response.name())
|
||||
self.assertEqual(0, len(response.get_parameters()))
|
||||
|
||||
|
||||
class PerMessageCompressExtensionProcessorTest(unittest.TestCase):
|
||||
def test_registry(self):
|
||||
processor = extensions.get_extension_processor(
|
||||
common.ExtensionParameter('permessage-compress'))
|
||||
self.assertIsInstance(processor,
|
||||
extensions.PerMessageCompressExtensionProcessor)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Tests for handshake._base module."""
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
import set_sys_path # Update sys.path to locate mod_pywebsocket module.
|
||||
|
||||
from mod_pywebsocket.common import ExtensionParameter
|
||||
from mod_pywebsocket.common import ExtensionParsingException
|
||||
from mod_pywebsocket.common import format_extensions
|
||||
from mod_pywebsocket.common import parse_extensions
|
||||
from mod_pywebsocket.handshake._base import HandshakeException
|
||||
from mod_pywebsocket.handshake._base import validate_subprotocol
|
||||
|
||||
|
||||
class ValidateSubprotocolTest(unittest.TestCase):
|
||||
"""A unittest for validate_subprotocol method."""
|
||||
|
||||
def test_validate_subprotocol(self):
|
||||
# Should succeed.
|
||||
validate_subprotocol('sample')
|
||||
validate_subprotocol('Sample')
|
||||
validate_subprotocol('sample\x7eprotocol')
|
||||
|
||||
# Should fail.
|
||||
self.assertRaises(HandshakeException,
|
||||
validate_subprotocol,
|
||||
'')
|
||||
self.assertRaises(HandshakeException,
|
||||
validate_subprotocol,
|
||||
'sample\x09protocol')
|
||||
self.assertRaises(HandshakeException,
|
||||
validate_subprotocol,
|
||||
'sample\x19protocol')
|
||||
self.assertRaises(HandshakeException,
|
||||
validate_subprotocol,
|
||||
'sample\x20protocol')
|
||||
self.assertRaises(HandshakeException,
|
||||
validate_subprotocol,
|
||||
'sample\x7fprotocol')
|
||||
self.assertRaises(HandshakeException,
|
||||
validate_subprotocol,
|
||||
# "Japan" in Japanese
|
||||
u'\u65e5\u672c')
|
||||
|
||||
|
||||
_TEST_TOKEN_EXTENSION_DATA = [
|
||||
('foo', [('foo', [])]),
|
||||
('foo; bar', [('foo', [('bar', None)])]),
|
||||
('foo; bar=baz', [('foo', [('bar', 'baz')])]),
|
||||
('foo; bar=baz; car=cdr', [('foo', [('bar', 'baz'), ('car', 'cdr')])]),
|
||||
('foo; bar=baz, car; cdr',
|
||||
[('foo', [('bar', 'baz')]), ('car', [('cdr', None)])]),
|
||||
('a, b, c, d',
|
||||
[('a', []), ('b', []), ('c', []), ('d', [])]),
|
||||
]
|
||||
|
||||
|
||||
_TEST_QUOTED_EXTENSION_DATA = [
|
||||
('foo; bar=""', [('foo', [('bar', '')])]),
|
||||
('foo; bar=" baz "', [('foo', [('bar', ' baz ')])]),
|
||||
('foo; bar=",baz;"', [('foo', [('bar', ',baz;')])]),
|
||||
('foo; bar="\\\r\\\nbaz"', [('foo', [('bar', '\r\nbaz')])]),
|
||||
('foo; bar="\\"baz"', [('foo', [('bar', '"baz')])]),
|
||||
('foo; bar="\xbbbaz"', [('foo', [('bar', '\xbbbaz')])]),
|
||||
]
|
||||
|
||||
|
||||
_TEST_REDUNDANT_TOKEN_EXTENSION_DATA = [
|
||||
('foo \t ', [('foo', [])]),
|
||||
('foo; \r\n bar', [('foo', [('bar', None)])]),
|
||||
('foo; bar=\r\n \r\n baz', [('foo', [('bar', 'baz')])]),
|
||||
('foo ;bar = baz ', [('foo', [('bar', 'baz')])]),
|
||||
('foo,bar,,baz', [('foo', []), ('bar', []), ('baz', [])]),
|
||||
]
|
||||
|
||||
|
||||
_TEST_REDUNDANT_QUOTED_EXTENSION_DATA = [
|
||||
('foo; bar="\r\n \r\n baz"', [('foo', [('bar', ' baz')])]),
|
||||
]
|
||||
|
||||
|
||||
class ExtensionsParserTest(unittest.TestCase):
|
||||
|
||||
def _verify_extension_list(self, expected_list, actual_list):
|
||||
"""Verifies that ExtensionParameter objects in actual_list have the
|
||||
same members as extension definitions in expected_list. Extension
|
||||
definition used in this test is a pair of an extension name and a
|
||||
parameter dictionary.
|
||||
"""
|
||||
|
||||
self.assertEqual(len(expected_list), len(actual_list))
|
||||
for expected, actual in zip(expected_list, actual_list):
|
||||
(name, parameters) = expected
|
||||
self.assertEqual(name, actual._name)
|
||||
self.assertEqual(parameters, actual._parameters)
|
||||
|
||||
def test_parse(self):
|
||||
for formatted_string, definition in _TEST_TOKEN_EXTENSION_DATA:
|
||||
self._verify_extension_list(
|
||||
definition, parse_extensions(formatted_string))
|
||||
|
||||
def test_parse_quoted_data(self):
|
||||
for formatted_string, definition in _TEST_QUOTED_EXTENSION_DATA:
|
||||
self._verify_extension_list(
|
||||
definition, parse_extensions(formatted_string))
|
||||
|
||||
def test_parse_redundant_data(self):
|
||||
for (formatted_string,
|
||||
definition) in _TEST_REDUNDANT_TOKEN_EXTENSION_DATA:
|
||||
self._verify_extension_list(
|
||||
definition, parse_extensions(formatted_string))
|
||||
|
||||
def test_parse_redundant_quoted_data(self):
|
||||
for (formatted_string,
|
||||
definition) in _TEST_REDUNDANT_QUOTED_EXTENSION_DATA:
|
||||
self._verify_extension_list(
|
||||
definition, parse_extensions(formatted_string))
|
||||
|
||||
def test_parse_bad_data(self):
|
||||
_TEST_BAD_EXTENSION_DATA = [
|
||||
('foo; ; '),
|
||||
('foo; a a'),
|
||||
('foo foo'),
|
||||
(',,,'),
|
||||
('foo; bar='),
|
||||
('foo; bar="hoge'),
|
||||
('foo; bar="a\r"'),
|
||||
('foo; bar="\\\xff"'),
|
||||
('foo; bar=\ra'),
|
||||
]
|
||||
|
||||
for formatted_string in _TEST_BAD_EXTENSION_DATA:
|
||||
self.assertRaises(
|
||||
ExtensionParsingException, parse_extensions, formatted_string)
|
||||
|
||||
|
||||
class FormatExtensionsTest(unittest.TestCase):
|
||||
|
||||
def test_format_extensions(self):
|
||||
for formatted_string, definitions in _TEST_TOKEN_EXTENSION_DATA:
|
||||
extensions = []
|
||||
for definition in definitions:
|
||||
(name, parameters) = definition
|
||||
extension = ExtensionParameter(name)
|
||||
extension._parameters = parameters
|
||||
extensions.append(extension)
|
||||
self.assertEqual(
|
||||
formatted_string, format_extensions(extensions))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,534 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Tests for handshake module."""
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
import set_sys_path # Update sys.path to locate mod_pywebsocket module.
|
||||
from mod_pywebsocket import common
|
||||
from mod_pywebsocket.handshake._base import AbortedByUserException
|
||||
from mod_pywebsocket.handshake._base import HandshakeException
|
||||
from mod_pywebsocket.handshake._base import VersionException
|
||||
from mod_pywebsocket.handshake.hybi import Handshaker
|
||||
|
||||
import mock
|
||||
|
||||
|
||||
class RequestDefinition(object):
|
||||
"""A class for holding data for constructing opening handshake strings for
|
||||
testing the opening handshake processor.
|
||||
"""
|
||||
|
||||
def __init__(self, method, uri, headers):
|
||||
self.method = method
|
||||
self.uri = uri
|
||||
self.headers = headers
|
||||
|
||||
|
||||
def _create_good_request_def():
|
||||
return RequestDefinition(
|
||||
'GET', '/demo',
|
||||
{'Host': 'server.example.com',
|
||||
'Upgrade': 'websocket',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
|
||||
'Sec-WebSocket-Version': '13',
|
||||
'Origin': 'http://example.com'})
|
||||
|
||||
|
||||
def _create_request(request_def):
|
||||
conn = mock.MockConn('')
|
||||
return mock.MockRequest(
|
||||
method=request_def.method,
|
||||
uri=request_def.uri,
|
||||
headers_in=request_def.headers,
|
||||
connection=conn)
|
||||
|
||||
|
||||
def _create_handshaker(request):
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
return handshaker
|
||||
|
||||
|
||||
class SubprotocolChoosingDispatcher(object):
|
||||
"""A dispatcher for testing. This dispatcher sets the i-th subprotocol
|
||||
of requested ones to ws_protocol where i is given on construction as index
|
||||
argument. If index is negative, default_value will be set to ws_protocol.
|
||||
"""
|
||||
|
||||
def __init__(self, index, default_value=None):
|
||||
self.index = index
|
||||
self.default_value = default_value
|
||||
|
||||
def do_extra_handshake(self, conn_context):
|
||||
if self.index >= 0:
|
||||
conn_context.ws_protocol = conn_context.ws_requested_protocols[
|
||||
self.index]
|
||||
else:
|
||||
conn_context.ws_protocol = self.default_value
|
||||
|
||||
def transfer_data(self, conn_context):
|
||||
pass
|
||||
|
||||
|
||||
class HandshakeAbortedException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AbortingDispatcher(object):
|
||||
"""A dispatcher for testing. This dispatcher raises an exception in
|
||||
do_extra_handshake to reject the request.
|
||||
"""
|
||||
|
||||
def do_extra_handshake(self, conn_context):
|
||||
raise HandshakeAbortedException('An exception to reject the request')
|
||||
|
||||
def transfer_data(self, conn_context):
|
||||
pass
|
||||
|
||||
|
||||
class AbortedByUserDispatcher(object):
|
||||
"""A dispatcher for testing. This dispatcher raises an
|
||||
AbortedByUserException in do_extra_handshake to reject the request.
|
||||
"""
|
||||
|
||||
def do_extra_handshake(self, conn_context):
|
||||
raise AbortedByUserException('An AbortedByUserException to reject the '
|
||||
'request')
|
||||
|
||||
def transfer_data(self, conn_context):
|
||||
pass
|
||||
|
||||
|
||||
_EXPECTED_RESPONSE = (
|
||||
'HTTP/1.1 101 Switching Protocols\r\n'
|
||||
'Upgrade: websocket\r\n'
|
||||
'Connection: Upgrade\r\n'
|
||||
'Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n')
|
||||
|
||||
|
||||
class HandshakerTest(unittest.TestCase):
|
||||
"""A unittest for draft-ietf-hybi-thewebsocketprotocol-06 and later
|
||||
handshake processor.
|
||||
"""
|
||||
|
||||
def test_do_handshake(self):
|
||||
request = _create_request(_create_good_request_def())
|
||||
dispatcher = mock.MockDispatcher()
|
||||
handshaker = Handshaker(request, dispatcher)
|
||||
handshaker.do_handshake()
|
||||
|
||||
self.assertTrue(dispatcher.do_extra_handshake_called)
|
||||
|
||||
self.assertEqual(
|
||||
_EXPECTED_RESPONSE, request.connection.written_data())
|
||||
self.assertEqual('/demo', request.ws_resource)
|
||||
self.assertEqual('http://example.com', request.ws_origin)
|
||||
self.assertEqual(None, request.ws_protocol)
|
||||
self.assertEqual(None, request.ws_extensions)
|
||||
self.assertEqual(common.VERSION_HYBI_LATEST, request.ws_version)
|
||||
|
||||
def test_do_handshake_with_extra_headers(self):
|
||||
request_def = _create_good_request_def()
|
||||
# Add headers not related to WebSocket opening handshake.
|
||||
request_def.headers['FooKey'] = 'BarValue'
|
||||
request_def.headers['EmptyKey'] = ''
|
||||
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(
|
||||
_EXPECTED_RESPONSE, request.connection.written_data())
|
||||
|
||||
def test_do_handshake_with_capitalized_value(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['upgrade'] = 'WEBSOCKET'
|
||||
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(
|
||||
_EXPECTED_RESPONSE, request.connection.written_data())
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Connection'] = 'UPGRADE'
|
||||
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(
|
||||
_EXPECTED_RESPONSE, request.connection.written_data())
|
||||
|
||||
def test_do_handshake_with_multiple_connection_values(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Connection'] = 'Upgrade, keep-alive, , '
|
||||
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(
|
||||
_EXPECTED_RESPONSE, request.connection.written_data())
|
||||
|
||||
def test_aborting_handshake(self):
|
||||
handshaker = Handshaker(
|
||||
_create_request(_create_good_request_def()),
|
||||
AbortingDispatcher())
|
||||
# do_extra_handshake raises an exception. Check that it's not caught by
|
||||
# do_handshake.
|
||||
self.assertRaises(HandshakeAbortedException, handshaker.do_handshake)
|
||||
|
||||
def test_do_handshake_with_protocol(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Protocol'] = 'chat, superchat'
|
||||
|
||||
request = _create_request(request_def)
|
||||
handshaker = Handshaker(request, SubprotocolChoosingDispatcher(0))
|
||||
handshaker.do_handshake()
|
||||
|
||||
EXPECTED_RESPONSE = (
|
||||
'HTTP/1.1 101 Switching Protocols\r\n'
|
||||
'Upgrade: websocket\r\n'
|
||||
'Connection: Upgrade\r\n'
|
||||
'Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n'
|
||||
'Sec-WebSocket-Protocol: chat\r\n\r\n')
|
||||
|
||||
self.assertEqual(EXPECTED_RESPONSE, request.connection.written_data())
|
||||
self.assertEqual('chat', request.ws_protocol)
|
||||
|
||||
def test_do_handshake_protocol_not_in_request_but_in_response(self):
|
||||
request_def = _create_good_request_def()
|
||||
request = _create_request(request_def)
|
||||
handshaker = Handshaker(
|
||||
request, SubprotocolChoosingDispatcher(-1, 'foobar'))
|
||||
# No request has been made but ws_protocol is set. HandshakeException
|
||||
# must be raised.
|
||||
self.assertRaises(HandshakeException, handshaker.do_handshake)
|
||||
|
||||
def test_do_handshake_with_protocol_no_protocol_selection(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Protocol'] = 'chat, superchat'
|
||||
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
# ws_protocol is not set. HandshakeException must be raised.
|
||||
self.assertRaises(HandshakeException, handshaker.do_handshake)
|
||||
|
||||
def test_do_handshake_with_extensions(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Extensions'] = (
|
||||
'permessage-compress; method=deflate, unknown')
|
||||
|
||||
EXPECTED_RESPONSE = (
|
||||
'HTTP/1.1 101 Switching Protocols\r\n'
|
||||
'Upgrade: websocket\r\n'
|
||||
'Connection: Upgrade\r\n'
|
||||
'Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n'
|
||||
'Sec-WebSocket-Extensions: permessage-compress; method=deflate\r\n'
|
||||
'\r\n')
|
||||
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(EXPECTED_RESPONSE, request.connection.written_data())
|
||||
self.assertEqual(1, len(request.ws_extensions))
|
||||
extension = request.ws_extensions[0]
|
||||
self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
|
||||
extension.name())
|
||||
self.assertEqual(['method'], extension.get_parameter_names())
|
||||
self.assertEqual('deflate', extension.get_parameter_value('method'))
|
||||
self.assertEqual(1, len(request.ws_extension_processors))
|
||||
self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
|
||||
request.ws_extension_processors[0].name())
|
||||
|
||||
def test_do_handshake_with_permessage_compress(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Extensions'] = (
|
||||
'permessage-compress; method=deflate')
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(1, len(request.ws_extensions))
|
||||
self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
|
||||
request.ws_extensions[0].name())
|
||||
self.assertEqual(1, len(request.ws_extension_processors))
|
||||
self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
|
||||
request.ws_extension_processors[0].name())
|
||||
|
||||
def test_do_handshake_with_quoted_extensions(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Extensions'] = (
|
||||
'permessage-compress; method=deflate, , '
|
||||
'unknown; e = "mc^2"; ma="\r\n \\\rf "; pv=nrt')
|
||||
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(2, len(request.ws_requested_extensions))
|
||||
first_extension = request.ws_requested_extensions[0]
|
||||
self.assertEqual('permessage-compress', first_extension.name())
|
||||
self.assertEqual(['method'], first_extension.get_parameter_names())
|
||||
self.assertEqual('deflate',
|
||||
first_extension.get_parameter_value('method'))
|
||||
second_extension = request.ws_requested_extensions[1]
|
||||
self.assertEqual('unknown', second_extension.name())
|
||||
self.assertEqual(
|
||||
['e', 'ma', 'pv'], second_extension.get_parameter_names())
|
||||
self.assertEqual('mc^2', second_extension.get_parameter_value('e'))
|
||||
self.assertEqual(' \rf ', second_extension.get_parameter_value('ma'))
|
||||
self.assertEqual('nrt', second_extension.get_parameter_value('pv'))
|
||||
|
||||
def test_do_handshake_with_optional_headers(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['EmptyValue'] = ''
|
||||
request_def.headers['AKey'] = 'AValue'
|
||||
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(
|
||||
'AValue', request.headers_in['AKey'])
|
||||
self.assertEqual(
|
||||
'', request.headers_in['EmptyValue'])
|
||||
|
||||
def test_abort_extra_handshake(self):
|
||||
handshaker = Handshaker(
|
||||
_create_request(_create_good_request_def()),
|
||||
AbortedByUserDispatcher())
|
||||
# do_extra_handshake raises an AbortedByUserException. Check that it's
|
||||
# not caught by do_handshake.
|
||||
self.assertRaises(AbortedByUserException, handshaker.do_handshake)
|
||||
|
||||
def test_do_handshake_with_mux_and_deflate_frame(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Extensions'] = ('%s, %s' % (
|
||||
common.MUX_EXTENSION,
|
||||
common.DEFLATE_FRAME_EXTENSION))
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
# mux should be rejected.
|
||||
self.assertEqual(1, len(request.ws_extensions))
|
||||
self.assertEqual(common.DEFLATE_FRAME_EXTENSION,
|
||||
request.ws_extensions[0].name())
|
||||
self.assertEqual(2, len(request.ws_extension_processors))
|
||||
self.assertEqual(common.MUX_EXTENSION,
|
||||
request.ws_extension_processors[0].name())
|
||||
self.assertEqual(common.DEFLATE_FRAME_EXTENSION,
|
||||
request.ws_extension_processors[1].name())
|
||||
self.assertFalse(hasattr(request, 'mux_processor'))
|
||||
|
||||
def test_do_handshake_with_deflate_frame_and_mux(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Extensions'] = ('%s, %s' % (
|
||||
common.DEFLATE_FRAME_EXTENSION,
|
||||
common.MUX_EXTENSION))
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
# mux should be rejected.
|
||||
self.assertEqual(1, len(request.ws_extensions))
|
||||
first_extension = request.ws_extensions[0]
|
||||
self.assertEqual(common.DEFLATE_FRAME_EXTENSION,
|
||||
first_extension.name())
|
||||
self.assertEqual(2, len(request.ws_extension_processors))
|
||||
self.assertEqual(common.DEFLATE_FRAME_EXTENSION,
|
||||
request.ws_extension_processors[0].name())
|
||||
self.assertEqual(common.MUX_EXTENSION,
|
||||
request.ws_extension_processors[1].name())
|
||||
self.assertFalse(hasattr(request, 'mux'))
|
||||
|
||||
def test_do_handshake_with_permessage_compress_and_mux(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Extensions'] = (
|
||||
'%s; method=deflate, %s' % (
|
||||
common.PERMESSAGE_COMPRESSION_EXTENSION,
|
||||
common.MUX_EXTENSION))
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
|
||||
self.assertEqual(1, len(request.ws_extensions))
|
||||
self.assertEqual(common.MUX_EXTENSION,
|
||||
request.ws_extensions[0].name())
|
||||
self.assertEqual(2, len(request.ws_extension_processors))
|
||||
self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
|
||||
request.ws_extension_processors[0].name())
|
||||
self.assertEqual(common.MUX_EXTENSION,
|
||||
request.ws_extension_processors[1].name())
|
||||
self.assertTrue(hasattr(request, 'mux_processor'))
|
||||
self.assertTrue(request.mux_processor.is_active())
|
||||
mux_extensions = request.mux_processor.extensions()
|
||||
self.assertEqual(1, len(mux_extensions))
|
||||
self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
|
||||
mux_extensions[0].name())
|
||||
|
||||
def test_do_handshake_with_mux_and_permessage_compress(self):
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Extensions'] = (
|
||||
'%s, %s; method=deflate' % (
|
||||
common.MUX_EXTENSION,
|
||||
common.PERMESSAGE_COMPRESSION_EXTENSION))
|
||||
request = _create_request(request_def)
|
||||
handshaker = _create_handshaker(request)
|
||||
handshaker.do_handshake()
|
||||
# mux should be rejected.
|
||||
self.assertEqual(1, len(request.ws_extensions))
|
||||
first_extension = request.ws_extensions[0]
|
||||
self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
|
||||
first_extension.name())
|
||||
self.assertEqual(2, len(request.ws_extension_processors))
|
||||
self.assertEqual(common.MUX_EXTENSION,
|
||||
request.ws_extension_processors[0].name())
|
||||
self.assertEqual(common.PERMESSAGE_COMPRESSION_EXTENSION,
|
||||
request.ws_extension_processors[1].name())
|
||||
self.assertFalse(hasattr(request, 'mux_processor'))
|
||||
|
||||
def test_bad_requests(self):
|
||||
bad_cases = [
|
||||
('HTTP request',
|
||||
RequestDefinition(
|
||||
'GET', '/demo',
|
||||
{'Host': 'www.google.com',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5;'
|
||||
' en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3'
|
||||
' GTB6 GTBA',
|
||||
'Accept':
|
||||
'text/html,application/xhtml+xml,application/xml;q=0.9,'
|
||||
'*/*;q=0.8',
|
||||
'Accept-Language': 'en-us,en;q=0.5',
|
||||
'Accept-Encoding': 'gzip,deflate',
|
||||
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
|
||||
'Keep-Alive': '300',
|
||||
'Connection': 'keep-alive'}), None, True)]
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
request_def.method = 'POST'
|
||||
bad_cases.append(('Wrong method', request_def, None, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
del request_def.headers['Host']
|
||||
bad_cases.append(('Missing Host', request_def, None, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
del request_def.headers['Upgrade']
|
||||
bad_cases.append(('Missing Upgrade', request_def, None, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Upgrade'] = 'nonwebsocket'
|
||||
bad_cases.append(('Wrong Upgrade', request_def, None, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
del request_def.headers['Connection']
|
||||
bad_cases.append(('Missing Connection', request_def, None, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Connection'] = 'Downgrade'
|
||||
bad_cases.append(('Wrong Connection', request_def, None, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
del request_def.headers['Sec-WebSocket-Key']
|
||||
bad_cases.append(('Missing Sec-WebSocket-Key', request_def, 400, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Key'] = (
|
||||
'dGhlIHNhbXBsZSBub25jZQ==garbage')
|
||||
bad_cases.append(('Wrong Sec-WebSocket-Key (with garbage on the tail)',
|
||||
request_def, 400, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Key'] = 'YQ==' # BASE64 of 'a'
|
||||
bad_cases.append(
|
||||
('Wrong Sec-WebSocket-Key (decoded value is not 16 octets long)',
|
||||
request_def, 400, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
# The last character right before == must be any of A, Q, w and g.
|
||||
request_def.headers['Sec-WebSocket-Key'] = (
|
||||
'AQIDBAUGBwgJCgsMDQ4PEC==')
|
||||
bad_cases.append(
|
||||
('Wrong Sec-WebSocket-Key (padding bits are not zero)',
|
||||
request_def, 400, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Key'] = (
|
||||
'dGhlIHNhbXBsZSBub25jZQ==,dGhlIHNhbXBsZSBub25jZQ==')
|
||||
bad_cases.append(
|
||||
('Wrong Sec-WebSocket-Key (multiple values)',
|
||||
request_def, 400, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
del request_def.headers['Sec-WebSocket-Version']
|
||||
bad_cases.append(('Missing Sec-WebSocket-Version', request_def, None,
|
||||
True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Version'] = '3'
|
||||
bad_cases.append(('Wrong Sec-WebSocket-Version', request_def, None,
|
||||
False))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Version'] = '13, 13'
|
||||
bad_cases.append(('Wrong Sec-WebSocket-Version (multiple values)',
|
||||
request_def, 400, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Protocol'] = 'illegal\x09protocol'
|
||||
bad_cases.append(('Illegal Sec-WebSocket-Protocol',
|
||||
request_def, 400, True))
|
||||
|
||||
request_def = _create_good_request_def()
|
||||
request_def.headers['Sec-WebSocket-Protocol'] = ''
|
||||
bad_cases.append(('Empty Sec-WebSocket-Protocol',
|
||||
request_def, 400, True))
|
||||
|
||||
for (case_name, request_def, expected_status,
|
||||
expect_handshake_exception) in bad_cases:
|
||||
request = _create_request(request_def)
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
try:
|
||||
handshaker.do_handshake()
|
||||
self.fail('No exception thrown for \'%s\' case' % case_name)
|
||||
except HandshakeException, e:
|
||||
self.assertTrue(expect_handshake_exception)
|
||||
self.assertEqual(expected_status, e.status)
|
||||
except VersionException, e:
|
||||
self.assertFalse(expect_handshake_exception)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,516 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Tests for handshake.hybi00 module."""
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
import set_sys_path # Update sys.path to locate mod_pywebsocket module.
|
||||
|
||||
from mod_pywebsocket.handshake._base import HandshakeException
|
||||
from mod_pywebsocket.handshake.hybi00 import Handshaker
|
||||
from mod_pywebsocket.handshake.hybi00 import _validate_subprotocol
|
||||
from test import mock
|
||||
|
||||
|
||||
_TEST_KEY1 = '4 @1 46546xW%0l 1 5'
|
||||
_TEST_KEY2 = '12998 5 Y3 1 .P00'
|
||||
_TEST_KEY3 = '^n:ds[4U'
|
||||
_TEST_CHALLENGE_RESPONSE = '8jKS\'y:G*Co,Wxa-'
|
||||
|
||||
|
||||
_GOOD_REQUEST = (
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Sec-WebSocket-Protocol': 'sample',
|
||||
'Upgrade': 'WebSocket',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3)
|
||||
|
||||
_GOOD_REQUEST_CAPITALIZED_HEADER_VALUES = (
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'UPGRADE',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Sec-WebSocket-Protocol': 'sample',
|
||||
'Upgrade': 'WEBSOCKET',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3)
|
||||
|
||||
_GOOD_REQUEST_CASE_MIXED_HEADER_NAMES = (
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'hOsT': 'example.com',
|
||||
'cOnNeCtIoN': 'Upgrade',
|
||||
'sEc-wEbsOcKeT-kEy2': _TEST_KEY2,
|
||||
'sEc-wEbsOcKeT-pRoToCoL': 'sample',
|
||||
'uPgRaDe': 'WebSocket',
|
||||
'sEc-wEbsOcKeT-kEy1': _TEST_KEY1,
|
||||
'oRiGiN': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3)
|
||||
|
||||
_GOOD_RESPONSE_DEFAULT_PORT = (
|
||||
'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
|
||||
'Upgrade: WebSocket\r\n'
|
||||
'Connection: Upgrade\r\n'
|
||||
'Sec-WebSocket-Location: ws://example.com/demo\r\n'
|
||||
'Sec-WebSocket-Origin: http://example.com\r\n'
|
||||
'Sec-WebSocket-Protocol: sample\r\n'
|
||||
'\r\n' +
|
||||
_TEST_CHALLENGE_RESPONSE)
|
||||
|
||||
_GOOD_RESPONSE_SECURE = (
|
||||
'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
|
||||
'Upgrade: WebSocket\r\n'
|
||||
'Connection: Upgrade\r\n'
|
||||
'Sec-WebSocket-Location: wss://example.com/demo\r\n'
|
||||
'Sec-WebSocket-Origin: http://example.com\r\n'
|
||||
'Sec-WebSocket-Protocol: sample\r\n'
|
||||
'\r\n' +
|
||||
_TEST_CHALLENGE_RESPONSE)
|
||||
|
||||
_GOOD_REQUEST_NONDEFAULT_PORT = (
|
||||
8081,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com:8081',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Sec-WebSocket-Protocol': 'sample',
|
||||
'Upgrade': 'WebSocket',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3)
|
||||
|
||||
_GOOD_RESPONSE_NONDEFAULT_PORT = (
|
||||
'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
|
||||
'Upgrade: WebSocket\r\n'
|
||||
'Connection: Upgrade\r\n'
|
||||
'Sec-WebSocket-Location: ws://example.com:8081/demo\r\n'
|
||||
'Sec-WebSocket-Origin: http://example.com\r\n'
|
||||
'Sec-WebSocket-Protocol: sample\r\n'
|
||||
'\r\n' +
|
||||
_TEST_CHALLENGE_RESPONSE)
|
||||
|
||||
_GOOD_RESPONSE_SECURE_NONDEF = (
|
||||
'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
|
||||
'Upgrade: WebSocket\r\n'
|
||||
'Connection: Upgrade\r\n'
|
||||
'Sec-WebSocket-Location: wss://example.com:8081/demo\r\n'
|
||||
'Sec-WebSocket-Origin: http://example.com\r\n'
|
||||
'Sec-WebSocket-Protocol: sample\r\n'
|
||||
'\r\n' +
|
||||
_TEST_CHALLENGE_RESPONSE)
|
||||
|
||||
_GOOD_REQUEST_NO_PROTOCOL = (
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Upgrade': 'WebSocket',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3)
|
||||
|
||||
_GOOD_RESPONSE_NO_PROTOCOL = (
|
||||
'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
|
||||
'Upgrade: WebSocket\r\n'
|
||||
'Connection: Upgrade\r\n'
|
||||
'Sec-WebSocket-Location: ws://example.com/demo\r\n'
|
||||
'Sec-WebSocket-Origin: http://example.com\r\n'
|
||||
'\r\n' +
|
||||
_TEST_CHALLENGE_RESPONSE)
|
||||
|
||||
_GOOD_REQUEST_WITH_OPTIONAL_HEADERS = (
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'EmptyValue': '',
|
||||
'Sec-WebSocket-Protocol': 'sample',
|
||||
'AKey': 'AValue',
|
||||
'Upgrade': 'WebSocket',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3)
|
||||
|
||||
# TODO(tyoshino): Include \r \n in key3, challenge response.
|
||||
|
||||
_GOOD_REQUEST_WITH_NONPRINTABLE_KEY = (
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': 'y R2 48 Q1O4 e|BV3 i5 1 u- 65',
|
||||
'Sec-WebSocket-Protocol': 'sample',
|
||||
'Upgrade': 'WebSocket',
|
||||
'Sec-WebSocket-Key1': '36 7 74 i 92 2\'m 9 0G',
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
''.join(map(chr, [0x01, 0xd1, 0xdd, 0x3b, 0xd1, 0x56, 0x63, 0xff])))
|
||||
|
||||
_GOOD_RESPONSE_WITH_NONPRINTABLE_KEY = (
|
||||
'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
|
||||
'Upgrade: WebSocket\r\n'
|
||||
'Connection: Upgrade\r\n'
|
||||
'Sec-WebSocket-Location: ws://example.com/demo\r\n'
|
||||
'Sec-WebSocket-Origin: http://example.com\r\n'
|
||||
'Sec-WebSocket-Protocol: sample\r\n'
|
||||
'\r\n' +
|
||||
''.join(map(chr, [0x0b, 0x99, 0xfa, 0x55, 0xbd, 0x01, 0x23, 0x7b,
|
||||
0x45, 0xa2, 0xf1, 0xd0, 0x87, 0x8a, 0xee, 0xeb])))
|
||||
|
||||
_GOOD_REQUEST_WITH_QUERY_PART = (
|
||||
80,
|
||||
'GET',
|
||||
'/demo?e=mc2',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Sec-WebSocket-Protocol': 'sample',
|
||||
'Upgrade': 'WebSocket',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3)
|
||||
|
||||
_GOOD_RESPONSE_WITH_QUERY_PART = (
|
||||
'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
|
||||
'Upgrade: WebSocket\r\n'
|
||||
'Connection: Upgrade\r\n'
|
||||
'Sec-WebSocket-Location: ws://example.com/demo?e=mc2\r\n'
|
||||
'Sec-WebSocket-Origin: http://example.com\r\n'
|
||||
'Sec-WebSocket-Protocol: sample\r\n'
|
||||
'\r\n' +
|
||||
_TEST_CHALLENGE_RESPONSE)
|
||||
|
||||
_BAD_REQUESTS = (
|
||||
( # HTTP request
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'www.google.com',
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5;'
|
||||
' en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3'
|
||||
' GTB6 GTBA',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,'
|
||||
'*/*;q=0.8',
|
||||
'Accept-Language': 'en-us,en;q=0.5',
|
||||
'Accept-Encoding': 'gzip,deflate',
|
||||
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
|
||||
'Keep-Alive': '300',
|
||||
'Connection': 'keep-alive',
|
||||
}),
|
||||
( # Wrong method
|
||||
80,
|
||||
'POST',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Sec-WebSocket-Protocol': 'sample',
|
||||
'Upgrade': 'WebSocket',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3),
|
||||
( # Missing Upgrade
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Sec-WebSocket-Protocol': 'sample',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3),
|
||||
( # Wrong Upgrade
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Sec-WebSocket-Protocol': 'sample',
|
||||
'Upgrade': 'NonWebSocket',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3),
|
||||
( # Empty WebSocket-Protocol
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Sec-WebSocket-Protocol': '',
|
||||
'Upgrade': 'WebSocket',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3),
|
||||
( # Wrong port number format
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com:0x50',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Sec-WebSocket-Protocol': 'sample',
|
||||
'Upgrade': 'WebSocket',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3),
|
||||
( # Header/connection port mismatch
|
||||
8080,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Sec-WebSocket-Protocol': 'sample',
|
||||
'Upgrade': 'WebSocket',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3),
|
||||
( # Illegal WebSocket-Protocol
|
||||
80,
|
||||
'GET',
|
||||
'/demo',
|
||||
{
|
||||
'Host': 'example.com',
|
||||
'Connection': 'Upgrade',
|
||||
'Sec-WebSocket-Key2': _TEST_KEY2,
|
||||
'Sec-WebSocket-Protocol': 'illegal\x09protocol',
|
||||
'Upgrade': 'WebSocket',
|
||||
'Sec-WebSocket-Key1': _TEST_KEY1,
|
||||
'Origin': 'http://example.com',
|
||||
},
|
||||
_TEST_KEY3),
|
||||
)
|
||||
|
||||
|
||||
def _create_request(request_def):
|
||||
data = ''
|
||||
if len(request_def) > 4:
|
||||
data = request_def[4]
|
||||
conn = mock.MockConn(data)
|
||||
conn.local_addr = ('0.0.0.0', request_def[0])
|
||||
return mock.MockRequest(
|
||||
method=request_def[1],
|
||||
uri=request_def[2],
|
||||
headers_in=request_def[3],
|
||||
connection=conn)
|
||||
|
||||
|
||||
def _create_get_memorized_lines(lines):
|
||||
"""Creates a function that returns the given string."""
|
||||
|
||||
def get_memorized_lines():
|
||||
return lines
|
||||
return get_memorized_lines
|
||||
|
||||
|
||||
def _create_requests_with_lines(request_lines_set):
|
||||
requests = []
|
||||
for lines in request_lines_set:
|
||||
request = _create_request(_GOOD_REQUEST)
|
||||
request.connection.get_memorized_lines = _create_get_memorized_lines(
|
||||
lines)
|
||||
requests.append(request)
|
||||
return requests
|
||||
|
||||
|
||||
class HyBi00HandshakerTest(unittest.TestCase):
|
||||
|
||||
def test_good_request_default_port(self):
|
||||
request = _create_request(_GOOD_REQUEST)
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(_GOOD_RESPONSE_DEFAULT_PORT,
|
||||
request.connection.written_data())
|
||||
self.assertEqual('/demo', request.ws_resource)
|
||||
self.assertEqual('http://example.com', request.ws_origin)
|
||||
self.assertEqual('ws://example.com/demo', request.ws_location)
|
||||
self.assertEqual('sample', request.ws_protocol)
|
||||
|
||||
def test_good_request_capitalized_header_values(self):
|
||||
request = _create_request(_GOOD_REQUEST_CAPITALIZED_HEADER_VALUES)
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(_GOOD_RESPONSE_DEFAULT_PORT,
|
||||
request.connection.written_data())
|
||||
|
||||
def test_good_request_case_mixed_header_names(self):
|
||||
request = _create_request(_GOOD_REQUEST_CASE_MIXED_HEADER_NAMES)
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(_GOOD_RESPONSE_DEFAULT_PORT,
|
||||
request.connection.written_data())
|
||||
|
||||
def test_good_request_secure_default_port(self):
|
||||
request = _create_request(_GOOD_REQUEST)
|
||||
request.connection.local_addr = ('0.0.0.0', 443)
|
||||
request.is_https_ = True
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(_GOOD_RESPONSE_SECURE,
|
||||
request.connection.written_data())
|
||||
self.assertEqual('sample', request.ws_protocol)
|
||||
|
||||
def test_good_request_nondefault_port(self):
|
||||
request = _create_request(_GOOD_REQUEST_NONDEFAULT_PORT)
|
||||
handshaker = Handshaker(request,
|
||||
mock.MockDispatcher())
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(_GOOD_RESPONSE_NONDEFAULT_PORT,
|
||||
request.connection.written_data())
|
||||
self.assertEqual('sample', request.ws_protocol)
|
||||
|
||||
def test_good_request_secure_non_default_port(self):
|
||||
request = _create_request(_GOOD_REQUEST_NONDEFAULT_PORT)
|
||||
request.is_https_ = True
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(_GOOD_RESPONSE_SECURE_NONDEF,
|
||||
request.connection.written_data())
|
||||
self.assertEqual('sample', request.ws_protocol)
|
||||
|
||||
def test_good_request_default_no_protocol(self):
|
||||
request = _create_request(_GOOD_REQUEST_NO_PROTOCOL)
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(_GOOD_RESPONSE_NO_PROTOCOL,
|
||||
request.connection.written_data())
|
||||
self.assertEqual(None, request.ws_protocol)
|
||||
|
||||
def test_good_request_optional_headers(self):
|
||||
request = _create_request(_GOOD_REQUEST_WITH_OPTIONAL_HEADERS)
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual('AValue',
|
||||
request.headers_in['AKey'])
|
||||
self.assertEqual('',
|
||||
request.headers_in['EmptyValue'])
|
||||
|
||||
def test_good_request_with_nonprintable_key(self):
|
||||
request = _create_request(_GOOD_REQUEST_WITH_NONPRINTABLE_KEY)
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(_GOOD_RESPONSE_WITH_NONPRINTABLE_KEY,
|
||||
request.connection.written_data())
|
||||
self.assertEqual('sample', request.ws_protocol)
|
||||
|
||||
def test_good_request_with_query_part(self):
|
||||
request = _create_request(_GOOD_REQUEST_WITH_QUERY_PART)
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
handshaker.do_handshake()
|
||||
self.assertEqual(_GOOD_RESPONSE_WITH_QUERY_PART,
|
||||
request.connection.written_data())
|
||||
self.assertEqual('ws://example.com/demo?e=mc2', request.ws_location)
|
||||
|
||||
def test_bad_requests(self):
|
||||
for request in map(_create_request, _BAD_REQUESTS):
|
||||
handshaker = Handshaker(request, mock.MockDispatcher())
|
||||
self.assertRaises(HandshakeException, handshaker.do_handshake)
|
||||
|
||||
|
||||
class HyBi00ValidateSubprotocolTest(unittest.TestCase):
|
||||
def test_validate_subprotocol(self):
|
||||
# should succeed.
|
||||
_validate_subprotocol('sample')
|
||||
_validate_subprotocol('Sample')
|
||||
_validate_subprotocol('sample\x7eprotocol')
|
||||
_validate_subprotocol('sample\x20protocol')
|
||||
|
||||
# should fail.
|
||||
self.assertRaises(HandshakeException,
|
||||
_validate_subprotocol,
|
||||
'')
|
||||
self.assertRaises(HandshakeException,
|
||||
_validate_subprotocol,
|
||||
'sample\x19protocol')
|
||||
self.assertRaises(HandshakeException,
|
||||
_validate_subprotocol,
|
||||
'sample\x7fprotocol')
|
||||
self.assertRaises(HandshakeException,
|
||||
_validate_subprotocol,
|
||||
# "Japan" in Japanese
|
||||
u'\u65e5\u672c')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Tests for http_header_util module."""
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
from mod_pywebsocket import http_header_util
|
||||
|
||||
|
||||
class UnitTest(unittest.TestCase):
|
||||
"""A unittest for http_header_util module."""
|
||||
|
||||
def test_parse_relative_uri(self):
|
||||
host, port, resource = http_header_util.parse_uri('/ws/test')
|
||||
self.assertEqual(None, host)
|
||||
self.assertEqual(None, port)
|
||||
self.assertEqual('/ws/test', resource)
|
||||
|
||||
def test_parse_absolute_uri(self):
|
||||
host, port, resource = http_header_util.parse_uri(
|
||||
'ws://localhost:10080/ws/test')
|
||||
self.assertEqual('localhost', host)
|
||||
self.assertEqual(10080, port)
|
||||
self.assertEqual('/ws/test', resource)
|
||||
|
||||
host, port, resource = http_header_util.parse_uri(
|
||||
'ws://example.com/ws/test')
|
||||
self.assertEqual('example.com', host)
|
||||
self.assertEqual(80, port)
|
||||
self.assertEqual('/ws/test', resource)
|
||||
|
||||
host, port, resource = http_header_util.parse_uri(
|
||||
'wss://example.com/')
|
||||
self.assertEqual('example.com', host)
|
||||
self.assertEqual(443, port)
|
||||
self.assertEqual('/', resource)
|
||||
|
||||
host, port, resource = http_header_util.parse_uri(
|
||||
'ws://example.com:8080')
|
||||
self.assertEqual('example.com', host)
|
||||
self.assertEqual(8080, port)
|
||||
self.assertEqual('/', resource)
|
||||
|
||||
def test_parse_invalid_uri(self):
|
||||
host, port, resource = http_header_util.parse_uri('ws:///')
|
||||
self.assertEqual(None, resource)
|
||||
|
||||
host, port, resource = http_header_util.parse_uri('ws://localhost:')
|
||||
self.assertEqual(None, resource)
|
||||
|
||||
host, port, resource = http_header_util.parse_uri('ws://localhost:/ws')
|
||||
self.assertEqual(None, resource)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Tests for memorizingfile module."""
|
||||
|
||||
|
||||
import StringIO
|
||||
import unittest
|
||||
|
||||
import set_sys_path # Update sys.path to locate mod_pywebsocket module.
|
||||
|
||||
from mod_pywebsocket import memorizingfile
|
||||
|
||||
|
||||
class UtilTest(unittest.TestCase):
|
||||
"""A unittest for memorizingfile module."""
|
||||
|
||||
def check(self, memorizing_file, num_read, expected_list):
|
||||
for unused in range(num_read):
|
||||
memorizing_file.readline()
|
||||
actual_list = memorizing_file.get_memorized_lines()
|
||||
self.assertEqual(len(expected_list), len(actual_list))
|
||||
for expected, actual in zip(expected_list, actual_list):
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def check_with_size(self, memorizing_file, read_size, expected_list):
|
||||
read_list = []
|
||||
read_line = ''
|
||||
while True:
|
||||
line = memorizing_file.readline(read_size)
|
||||
line_length = len(line)
|
||||
self.assertTrue(line_length <= read_size)
|
||||
if line_length == 0:
|
||||
if read_line != '':
|
||||
read_list.append(read_line)
|
||||
break
|
||||
read_line += line
|
||||
if line[line_length - 1] == '\n':
|
||||
read_list.append(read_line)
|
||||
read_line = ''
|
||||
actual_list = memorizing_file.get_memorized_lines()
|
||||
self.assertEqual(len(expected_list), len(actual_list))
|
||||
self.assertEqual(len(expected_list), len(read_list))
|
||||
for expected, actual, read in zip(expected_list, actual_list,
|
||||
read_list):
|
||||
self.assertEqual(expected, actual)
|
||||
self.assertEqual(expected, read)
|
||||
|
||||
def test_get_memorized_lines(self):
|
||||
memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO(
|
||||
'Hello\nWorld\nWelcome'))
|
||||
self.check(memorizing_file, 3, ['Hello\n', 'World\n', 'Welcome'])
|
||||
|
||||
def test_get_memorized_lines_limit_memorized_lines(self):
|
||||
memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO(
|
||||
'Hello\nWorld\nWelcome'), 2)
|
||||
self.check(memorizing_file, 3, ['Hello\n', 'World\n'])
|
||||
|
||||
def test_get_memorized_lines_empty_file(self):
|
||||
memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO(
|
||||
''))
|
||||
self.check(memorizing_file, 10, [])
|
||||
|
||||
def test_get_memorized_lines_with_size(self):
|
||||
for size in range(1, 10):
|
||||
memorizing_file = memorizingfile.MemorizingFile(StringIO.StringIO(
|
||||
'Hello\nWorld\nWelcome'))
|
||||
self.check_with_size(memorizing_file, size,
|
||||
['Hello\n', 'World\n', 'Welcome'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Tests for mock module."""
|
||||
|
||||
|
||||
import Queue
|
||||
import threading
|
||||
import unittest
|
||||
|
||||
import set_sys_path # Update sys.path to locate mod_pywebsocket module.
|
||||
|
||||
from test import mock
|
||||
|
||||
|
||||
class MockConnTest(unittest.TestCase):
|
||||
"""A unittest for MockConn class."""
|
||||
|
||||
def setUp(self):
|
||||
self._conn = mock.MockConn('ABC\r\nDEFG\r\n\r\nHIJK')
|
||||
|
||||
def test_readline(self):
|
||||
self.assertEqual('ABC\r\n', self._conn.readline())
|
||||
self.assertEqual('DEFG\r\n', self._conn.readline())
|
||||
self.assertEqual('\r\n', self._conn.readline())
|
||||
self.assertEqual('HIJK', self._conn.readline())
|
||||
self.assertEqual('', self._conn.readline())
|
||||
|
||||
def test_read(self):
|
||||
self.assertEqual('ABC\r\nD', self._conn.read(6))
|
||||
self.assertEqual('EFG\r\n\r\nHI', self._conn.read(9))
|
||||
self.assertEqual('JK', self._conn.read(10))
|
||||
self.assertEqual('', self._conn.read(10))
|
||||
|
||||
def test_read_and_readline(self):
|
||||
self.assertEqual('ABC\r\nD', self._conn.read(6))
|
||||
self.assertEqual('EFG\r\n', self._conn.readline())
|
||||
self.assertEqual('\r\nHIJK', self._conn.read(9))
|
||||
self.assertEqual('', self._conn.readline())
|
||||
|
||||
def test_write(self):
|
||||
self._conn.write('Hello\r\n')
|
||||
self._conn.write('World\r\n')
|
||||
self.assertEqual('Hello\r\nWorld\r\n', self._conn.written_data())
|
||||
|
||||
|
||||
class MockBlockingConnTest(unittest.TestCase):
|
||||
"""A unittest for MockBlockingConn class."""
|
||||
|
||||
def test_read(self):
|
||||
"""Tests that data put to MockBlockingConn by put_bytes method can be
|
||||
read from it.
|
||||
"""
|
||||
|
||||
class LineReader(threading.Thread):
|
||||
"""A test class that launches a thread, calls readline on the
|
||||
specified conn repeatedly and puts the read data to the specified
|
||||
queue.
|
||||
"""
|
||||
|
||||
def __init__(self, conn, queue):
|
||||
threading.Thread.__init__(self)
|
||||
self._queue = queue
|
||||
self._conn = conn
|
||||
self.setDaemon(True)
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
data = self._conn.readline()
|
||||
self._queue.put(data)
|
||||
|
||||
conn = mock.MockBlockingConn()
|
||||
queue = Queue.Queue()
|
||||
reader = LineReader(conn, queue)
|
||||
self.failUnless(queue.empty())
|
||||
conn.put_bytes('Foo bar\r\n')
|
||||
read = queue.get()
|
||||
self.assertEqual('Foo bar\r\n', read)
|
||||
|
||||
|
||||
class MockTableTest(unittest.TestCase):
|
||||
"""A unittest for MockTable class."""
|
||||
|
||||
def test_create_from_dict(self):
|
||||
table = mock.MockTable({'Key': 'Value'})
|
||||
self.assertEqual('Value', table.get('KEY'))
|
||||
self.assertEqual('Value', table['key'])
|
||||
|
||||
def test_create_from_list(self):
|
||||
table = mock.MockTable([('Key', 'Value')])
|
||||
self.assertEqual('Value', table.get('KEY'))
|
||||
self.assertEqual('Value', table['key'])
|
||||
|
||||
def test_create_from_tuple(self):
|
||||
table = mock.MockTable((('Key', 'Value'),))
|
||||
self.assertEqual('Value', table.get('KEY'))
|
||||
self.assertEqual('Value', table['key'])
|
||||
|
||||
def test_set_and_get(self):
|
||||
table = mock.MockTable()
|
||||
self.assertEqual(None, table.get('Key'))
|
||||
table['Key'] = 'Value'
|
||||
self.assertEqual('Value', table.get('Key'))
|
||||
self.assertEqual('Value', table.get('key'))
|
||||
self.assertEqual('Value', table.get('KEY'))
|
||||
self.assertEqual('Value', table['Key'])
|
||||
self.assertEqual('Value', table['key'])
|
||||
self.assertEqual('Value', table['KEY'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
File diff suppressed because it is too large
Load diff
2089
testing/web-platform/tests/tools/pywebsocket/src/test/test_mux.py
Normal file
2089
testing/web-platform/tests/tools/pywebsocket/src/test/test_mux.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Tests for stream module."""
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
import set_sys_path # Update sys.path to locate mod_pywebsocket module.
|
||||
|
||||
from mod_pywebsocket import common
|
||||
from mod_pywebsocket import stream
|
||||
|
||||
|
||||
class StreamTest(unittest.TestCase):
|
||||
"""A unittest for stream module."""
|
||||
|
||||
def test_create_header(self):
|
||||
# more, rsv1, ..., rsv4 are all true
|
||||
header = stream.create_header(common.OPCODE_TEXT, 1, 1, 1, 1, 1, 1)
|
||||
self.assertEqual('\xf1\x81', header)
|
||||
|
||||
# Maximum payload size
|
||||
header = stream.create_header(
|
||||
common.OPCODE_TEXT, (1 << 63) - 1, 0, 0, 0, 0, 0)
|
||||
self.assertEqual('\x01\x7f\x7f\xff\xff\xff\xff\xff\xff\xff', header)
|
||||
|
||||
# Invalid opcode 0x10
|
||||
self.assertRaises(ValueError,
|
||||
stream.create_header,
|
||||
0x10, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
# Invalid value 0xf passed to more parameter
|
||||
self.assertRaises(ValueError,
|
||||
stream.create_header,
|
||||
common.OPCODE_TEXT, 0, 0xf, 0, 0, 0, 0)
|
||||
|
||||
# Too long payload_length
|
||||
self.assertRaises(ValueError,
|
||||
stream.create_header,
|
||||
common.OPCODE_TEXT, 1 << 63, 0, 0, 0, 0, 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Tests for stream module."""
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
import set_sys_path # Update sys.path to locate mod_pywebsocket module.
|
||||
|
||||
from mod_pywebsocket.stream import StreamHixie75
|
||||
from test.test_msgutil import _create_request_hixie75
|
||||
|
||||
|
||||
class StreamHixie75Test(unittest.TestCase):
|
||||
"""A unittest for StreamHixie75 class."""
|
||||
|
||||
def test_payload_length(self):
|
||||
for length, bytes in ((0, '\x00'), (0x7f, '\x7f'), (0x80, '\x81\x00'),
|
||||
(0x1234, '\x80\xa4\x34')):
|
||||
test_stream = StreamHixie75(_create_request_hixie75(bytes))
|
||||
self.assertEqual(
|
||||
length, test_stream._read_payload_length_hixie75())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Tests for util module."""
|
||||
|
||||
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import set_sys_path # Update sys.path to locate mod_pywebsocket module.
|
||||
|
||||
from mod_pywebsocket import util
|
||||
|
||||
|
||||
_TEST_DATA_DIR = os.path.join(os.path.split(__file__)[0], 'testdata')
|
||||
|
||||
|
||||
class UtilTest(unittest.TestCase):
|
||||
"""A unittest for util module."""
|
||||
|
||||
def test_get_stack_trace(self):
|
||||
self.assertEqual('None\n', util.get_stack_trace())
|
||||
try:
|
||||
a = 1 / 0 # Intentionally raise exception.
|
||||
except Exception:
|
||||
trace = util.get_stack_trace()
|
||||
self.failUnless(trace.startswith('Traceback'))
|
||||
self.failUnless(trace.find('ZeroDivisionError') != -1)
|
||||
|
||||
def test_prepend_message_to_exception(self):
|
||||
exc = Exception('World')
|
||||
self.assertEqual('World', str(exc))
|
||||
util.prepend_message_to_exception('Hello ', exc)
|
||||
self.assertEqual('Hello World', str(exc))
|
||||
|
||||
def test_get_script_interp(self):
|
||||
cygwin_path = 'c:\\cygwin\\bin'
|
||||
cygwin_perl = os.path.join(cygwin_path, 'perl')
|
||||
self.assertEqual(None, util.get_script_interp(
|
||||
os.path.join(_TEST_DATA_DIR, 'README')))
|
||||
self.assertEqual(None, util.get_script_interp(
|
||||
os.path.join(_TEST_DATA_DIR, 'README'), cygwin_path))
|
||||
self.assertEqual('/usr/bin/perl -wT', util.get_script_interp(
|
||||
os.path.join(_TEST_DATA_DIR, 'hello.pl')))
|
||||
self.assertEqual(cygwin_perl + ' -wT', util.get_script_interp(
|
||||
os.path.join(_TEST_DATA_DIR, 'hello.pl'), cygwin_path))
|
||||
|
||||
def test_hexify(self):
|
||||
self.assertEqual('61 7a 41 5a 30 39 20 09 0d 0a 00 ff',
|
||||
util.hexify('azAZ09 \t\r\n\x00\xff'))
|
||||
|
||||
|
||||
class RepeatedXorMaskerTest(unittest.TestCase):
|
||||
"""A unittest for RepeatedXorMasker class."""
|
||||
|
||||
def test_mask(self):
|
||||
# Sample input e6,97,a5 is U+65e5 in UTF-8
|
||||
masker = util.RepeatedXorMasker('\xff\xff\xff\xff')
|
||||
result = masker.mask('\xe6\x97\xa5')
|
||||
self.assertEqual('\x19\x68\x5a', result)
|
||||
|
||||
masker = util.RepeatedXorMasker('\x00\x00\x00\x00')
|
||||
result = masker.mask('\xe6\x97\xa5')
|
||||
self.assertEqual('\xe6\x97\xa5', result)
|
||||
|
||||
masker = util.RepeatedXorMasker('\xe6\x97\xa5\x20')
|
||||
result = masker.mask('\xe6\x97\xa5')
|
||||
self.assertEqual('\x00\x00\x00', result)
|
||||
|
||||
def test_mask_twice(self):
|
||||
masker = util.RepeatedXorMasker('\x00\x7f\xff\x20')
|
||||
# mask[0], mask[1], ... will be used.
|
||||
result = masker.mask('\x00\x00\x00\x00\x00')
|
||||
self.assertEqual('\x00\x7f\xff\x20\x00', result)
|
||||
# mask[2], mask[0], ... will be used for the next call.
|
||||
result = masker.mask('\x00\x00\x00\x00\x00')
|
||||
self.assertEqual('\x7f\xff\x20\x00\x7f', result)
|
||||
|
||||
def test_mask_large_data(self):
|
||||
masker = util.RepeatedXorMasker('mASk')
|
||||
original = ''.join([chr(i % 256) for i in xrange(1000)])
|
||||
result = masker.mask(original)
|
||||
expected = ''.join(
|
||||
[chr((i % 256) ^ ord('mASk'[i % 4])) for i in xrange(1000)])
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
masker = util.RepeatedXorMasker('MaSk')
|
||||
first_part = 'The WebSocket Protocol enables two-way communication.'
|
||||
result = masker.mask(first_part)
|
||||
self.assertEqual(
|
||||
'\x19\t6K\x1a\x0418"\x028\x0e9A\x03\x19"\x15<\x08"\rs\x0e#'
|
||||
'\x001\x07(\x12s\x1f:\x0e~\x1c,\x18s\x08"\x0c>\x1e#\x080\n9'
|
||||
'\x08<\x05c',
|
||||
result)
|
||||
second_part = 'It has two parts: a handshake and the data transfer.'
|
||||
result = masker.mask(second_part)
|
||||
self.assertEqual(
|
||||
"('K%\x00 K9\x16<K=\x00!\x1f>[s\nm\t2\x05)\x12;\n&\x04s\n#"
|
||||
"\x05s\x1f%\x04s\x0f,\x152K9\x132\x05>\x076\x19c",
|
||||
result)
|
||||
|
||||
|
||||
def get_random_section(source, min_num_chunks):
|
||||
chunks = []
|
||||
bytes_chunked = 0
|
||||
|
||||
while bytes_chunked < len(source):
|
||||
chunk_size = random.randint(
|
||||
1,
|
||||
min(len(source) / min_num_chunks, len(source) - bytes_chunked))
|
||||
chunk = source[bytes_chunked:bytes_chunked + chunk_size]
|
||||
chunks.append(chunk)
|
||||
bytes_chunked += chunk_size
|
||||
|
||||
return chunks
|
||||
|
||||
|
||||
class InflaterDeflaterTest(unittest.TestCase):
|
||||
"""A unittest for _Inflater and _Deflater class."""
|
||||
|
||||
def test_inflate_deflate_default(self):
|
||||
input = b'hello' + '-' * 30000 + b'hello'
|
||||
inflater15 = util._Inflater(15)
|
||||
deflater15 = util._Deflater(15)
|
||||
inflater8 = util._Inflater(8)
|
||||
deflater8 = util._Deflater(8)
|
||||
|
||||
compressed15 = deflater15.compress_and_finish(input)
|
||||
compressed8 = deflater8.compress_and_finish(input)
|
||||
|
||||
inflater15.append(compressed15)
|
||||
inflater8.append(compressed8)
|
||||
|
||||
self.assertNotEqual(compressed15, compressed8)
|
||||
self.assertEqual(input, inflater15.decompress(-1))
|
||||
self.assertEqual(input, inflater8.decompress(-1))
|
||||
|
||||
def test_random_section(self):
|
||||
random.seed(a=0)
|
||||
source = ''.join(
|
||||
[chr(random.randint(0, 255)) for i in xrange(100 * 1024)])
|
||||
|
||||
chunked_input = get_random_section(source, 10)
|
||||
print "Input chunk sizes: %r" % [len(c) for c in chunked_input]
|
||||
|
||||
deflater = util._Deflater(15)
|
||||
compressed = []
|
||||
for chunk in chunked_input:
|
||||
compressed.append(deflater.compress(chunk))
|
||||
compressed.append(deflater.compress_and_finish(''))
|
||||
|
||||
chunked_expectation = get_random_section(source, 10)
|
||||
print ("Expectation chunk sizes: %r" %
|
||||
[len(c) for c in chunked_expectation])
|
||||
|
||||
inflater = util._Inflater(15)
|
||||
inflater.append(''.join(compressed))
|
||||
for chunk in chunked_expectation:
|
||||
decompressed = inflater.decompress(len(chunk))
|
||||
self.assertEqual(chunk, decompressed)
|
||||
|
||||
self.assertEqual('', inflater.decompress(-1))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
1
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/README
vendored
Normal file
1
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/README
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
Test data directory
|
||||
42
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/abort_by_user_wsh.py
vendored
Normal file
42
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/abort_by_user_wsh.py
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Copyright 2011, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
from mod_pywebsocket import handshake
|
||||
|
||||
|
||||
def web_socket_do_extra_handshake(request):
|
||||
raise handshake.AbortedByUserException("abort for test")
|
||||
|
||||
|
||||
def web_socket_transfer_data(request):
|
||||
raise handshake.AbortedByUserException("abort for test")
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
31
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/blank_wsh.py
vendored
Normal file
31
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/blank_wsh.py
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Copyright 2009, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
# intentionally left blank
|
||||
42
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/origin_check_wsh.py
vendored
Normal file
42
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/origin_check_wsh.py
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Copyright 2009, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
def web_socket_do_extra_handshake(request):
|
||||
if request.ws_origin == 'http://example.com':
|
||||
return
|
||||
raise ValueError('Unacceptable origin: %r' % request.ws_origin)
|
||||
|
||||
|
||||
def web_socket_transfer_data(request):
|
||||
request.connection.write('origin_check_wsh.py is called for %s, %s' %
|
||||
(request.ws_resource, request.ws_protocol))
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Copyright 2009, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Exception in web_socket_transfer_data().
|
||||
"""
|
||||
|
||||
|
||||
def web_socket_do_extra_handshake(request):
|
||||
pass
|
||||
|
||||
|
||||
def web_socket_transfer_data(request):
|
||||
raise Exception('Intentional Exception for %s, %s' %
|
||||
(request.ws_resource, request.ws_protocol))
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
45
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/no_wsh_at_the_end.py
vendored
Normal file
45
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/no_wsh_at_the_end.py
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright 2009, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Correct signatures, wrong file name.
|
||||
"""
|
||||
|
||||
|
||||
def web_socket_do_extra_handshake(request):
|
||||
pass
|
||||
|
||||
|
||||
def web_socket_transfer_data(request):
|
||||
request.connection.write(
|
||||
'sub/no_wsh_at_the_end.py is called for %s, %s' %
|
||||
(request.ws_resource, request.ws_protocol))
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
39
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/non_callable_wsh.py
vendored
Normal file
39
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/non_callable_wsh.py
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Copyright 2009, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Non-callable handlers.
|
||||
"""
|
||||
|
||||
|
||||
web_socket_do_extra_handshake = True
|
||||
web_socket_transfer_data = 1
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
40
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/plain_wsh.py
vendored
Normal file
40
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/handlers/sub/plain_wsh.py
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# Copyright 2009, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
def web_socket_do_extra_handshake(request):
|
||||
pass
|
||||
|
||||
|
||||
def web_socket_transfer_data(request):
|
||||
request.connection.write('sub/plain_wsh.py is called for %s, %s' %
|
||||
(request.ws_resource, request.ws_protocol))
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright 2009, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Wrong web_socket_do_extra_handshake signature.
|
||||
"""
|
||||
|
||||
|
||||
def no_web_socket_do_extra_handshake(request):
|
||||
pass
|
||||
|
||||
|
||||
def web_socket_transfer_data(request):
|
||||
request.connection.write(
|
||||
'sub/wrong_handshake_sig_wsh.py is called for %s, %s' %
|
||||
(request.ws_resource, request.ws_protocol))
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright 2009, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Wrong web_socket_transfer_data() signature.
|
||||
"""
|
||||
|
||||
|
||||
def web_socket_do_extra_handshake(request):
|
||||
pass
|
||||
|
||||
|
||||
def no_web_socket_transfer_data(request):
|
||||
request.connection.write(
|
||||
'sub/wrong_transfer_sig_wsh.py is called for %s, %s' %
|
||||
(request.ws_resource, request.ws_protocol))
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
32
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/hello.pl
vendored
Normal file
32
testing/web-platform/tests/tools/pywebsocket/src/test/testdata/hello.pl
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/perl -wT
|
||||
#
|
||||
# Copyright 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
print "Hello\n";
|
||||
Loading…
Add table
Add a link
Reference in a new issue