Use unparsed_uri to build Sec-WebSocket-Location header.
Release note: Changed Sec-WebSocket-Location to include the query part.
BUG= https://code.google.com/p/pywebsocket/issues/detail?id=68
Review URL: https://codereview.appspot.com/7568046
diff --git a/src/mod_pywebsocket/handshake/_base.py b/src/mod_pywebsocket/handshake/_base.py
index fdc0ac4..c993a58 100644
--- a/src/mod_pywebsocket/handshake/_base.py
+++ b/src/mod_pywebsocket/handshake/_base.py
@@ -106,7 +106,7 @@
def parse_host_header(request):
- fields = request.headers_in['Host'].split(':', 1)
+ fields = request.headers_in[common.HOST_HEADER].split(':', 1)
if len(fields) == 1:
return fields[0], get_default_port(request.is_https())
try:
diff --git a/src/mod_pywebsocket/handshake/hybi00.py b/src/mod_pywebsocket/handshake/hybi00.py
index 26d153e..8757717 100644
--- a/src/mod_pywebsocket/handshake/hybi00.py
+++ b/src/mod_pywebsocket/handshake/hybi00.py
@@ -112,7 +112,7 @@
if (port != get_default_port(request.is_https())):
location_parts.append(':')
location_parts.append(str(port))
- location_parts.append(request.uri)
+ location_parts.append(request.unparsed_uri)
return ''.join(location_parts)
diff --git a/src/mod_pywebsocket/standalone.py b/src/mod_pywebsocket/standalone.py
index dba1d68..e9f0837 100755
--- a/src/mod_pywebsocket/standalone.py
+++ b/src/mod_pywebsocket/standalone.py
@@ -226,11 +226,23 @@
self.headers_in = request_handler.headers
def get_uri(self):
- """Getter to mimic request.uri."""
+ """Getter to mimic request.uri.
+
+ This method returns the raw data at the Request-URI part of the
+ Request-Line, while the uri method on the request object of mod_python
+ returns the path portion after parsing the raw data. This behavior is
+ kept for compatibility.
+ """
return self._request_handler.path
uri = property(get_uri)
+ def get_unparsed_uri(self):
+ """Getter to mimic request.unparsed_uri."""
+
+ return self._request_handler.path
+ unparsed_uri = property(get_unparsed_uri)
+
def get_method(self):
"""Getter to mimic request.method."""
diff --git a/src/test/mock.py b/src/test/mock.py
index 02d8371..b4757cc 100644
--- a/src/test/mock.py
+++ b/src/test/mock.py
@@ -186,6 +186,7 @@
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
diff --git a/src/test/test_handshake_hybi00.py b/src/test/test_handshake_hybi00.py
index 7818faf..73f9f27 100755
--- a/src/test/test_handshake_hybi00.py
+++ b/src/test/test_handshake_hybi00.py
@@ -43,6 +43,12 @@
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',
@@ -50,13 +56,13 @@
{
'Host': 'example.com',
'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'Sec-WebSocket-Protocol': 'sample',
'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U')
+ _TEST_KEY3)
_GOOD_REQUEST_CAPITALIZED_HEADER_VALUES = (
80,
@@ -65,13 +71,28 @@
{
'Host': 'example.com',
'Connection': 'UPGRADE',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'Sec-WebSocket-Protocol': 'sample',
'Upgrade': 'WEBSOCKET',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U')
+ _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'
@@ -80,8 +101,8 @@
'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'
- '8jKS\'y:G*Co,Wxa-')
+ '\r\n' +
+ _TEST_CHALLENGE_RESPONSE)
_GOOD_RESPONSE_SECURE = (
'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
@@ -90,8 +111,8 @@
'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'
- '8jKS\'y:G*Co,Wxa-')
+ '\r\n' +
+ _TEST_CHALLENGE_RESPONSE)
_GOOD_REQUEST_NONDEFAULT_PORT = (
8081,
@@ -100,13 +121,13 @@
{
'Host': 'example.com:8081',
'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'Sec-WebSocket-Protocol': 'sample',
'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U')
+ _TEST_KEY3)
_GOOD_RESPONSE_NONDEFAULT_PORT = (
'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
@@ -115,8 +136,8 @@
'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'
- '8jKS\'y:G*Co,Wxa-')
+ '\r\n' +
+ _TEST_CHALLENGE_RESPONSE)
_GOOD_RESPONSE_SECURE_NONDEF = (
'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
@@ -125,8 +146,8 @@
'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'
- '8jKS\'y:G*Co,Wxa-')
+ '\r\n' +
+ _TEST_CHALLENGE_RESPONSE)
_GOOD_REQUEST_NO_PROTOCOL = (
80,
@@ -135,12 +156,12 @@
{
'Host': 'example.com',
'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U')
+ _TEST_KEY3)
_GOOD_RESPONSE_NO_PROTOCOL = (
'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
@@ -148,8 +169,8 @@
'Connection: Upgrade\r\n'
'Sec-WebSocket-Location: ws://example.com/demo\r\n'
'Sec-WebSocket-Origin: http://example.com\r\n'
- '\r\n'
- '8jKS\'y:G*Co,Wxa-')
+ '\r\n' +
+ _TEST_CHALLENGE_RESPONSE)
_GOOD_REQUEST_WITH_OPTIONAL_HEADERS = (
80,
@@ -158,15 +179,15 @@
{
'Host': 'example.com',
'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'EmptyValue': '',
'Sec-WebSocket-Protocol': 'sample',
'AKey': 'AValue',
'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U')
+ _TEST_KEY3)
# TODO(tyoshino): Include \r \n in key3, challenge response.
@@ -196,6 +217,31 @@
''.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,
@@ -221,13 +267,13 @@
{
'Host': 'example.com',
'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'Sec-WebSocket-Protocol': 'sample',
'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U'),
+ _TEST_KEY3),
( # Missing Upgrade
80,
'GET',
@@ -235,12 +281,12 @@
{
'Host': 'example.com',
'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'Sec-WebSocket-Protocol': 'sample',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U'),
+ _TEST_KEY3),
( # Wrong Upgrade
80,
'GET',
@@ -248,13 +294,13 @@
{
'Host': 'example.com',
'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'Sec-WebSocket-Protocol': 'sample',
'Upgrade': 'NonWebSocket',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U'),
+ _TEST_KEY3),
( # Empty WebSocket-Protocol
80,
'GET',
@@ -262,13 +308,13 @@
{
'Host': 'example.com',
'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'Sec-WebSocket-Protocol': '',
'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U'),
+ _TEST_KEY3),
( # Wrong port number format
80,
'GET',
@@ -276,13 +322,13 @@
{
'Host': 'example.com:0x50',
'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'Sec-WebSocket-Protocol': 'sample',
'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U'),
+ _TEST_KEY3),
( # Header/connection port mismatch
8080,
'GET',
@@ -290,13 +336,13 @@
{
'Host': 'example.com',
'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'Sec-WebSocket-Protocol': 'sample',
'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U'),
+ _TEST_KEY3),
( # Illegal WebSocket-Protocol
80,
'GET',
@@ -304,13 +350,13 @@
{
'Host': 'example.com',
'Connection': 'Upgrade',
- 'Sec-WebSocket-Key2': '12998 5 Y3 1 .P00',
+ 'Sec-WebSocket-Key2': _TEST_KEY2,
'Sec-WebSocket-Protocol': 'illegal\x09protocol',
'Upgrade': 'WebSocket',
- 'Sec-WebSocket-Key1': '4 @1 46546xW%0l 1 5',
+ 'Sec-WebSocket-Key1': _TEST_KEY1,
'Origin': 'http://example.com',
},
- '^n:ds[4U'),
+ _TEST_KEY3),
)
@@ -365,6 +411,13 @@
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)
@@ -418,6 +471,14 @@
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())