Skip to content

Commit

Permalink
Added support for raw_path in scope. (#268)
Browse files Browse the repository at this point in the history
As per django/asgiref#92

Required valid URI path fragments to be used in tests:
- Test case must ensure paths are correctly quoted before calling
   run_daphne_request() & co.

Co-authored-by: Carlton Gibson <carlton.gibson@noumenal.es>
  • Loading branch information
simonw and carltongibson committed Jul 3, 2019
1 parent ffd949f commit 333f464
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 12 deletions.
1 change: 1 addition & 0 deletions daphne/http_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ def process(self):
),
"method": self.method.decode("ascii"),
"path": unquote(self.path.decode("ascii")),
"raw_path": self.path,
"root_path": self.root_path,
"scheme": self.client_scheme,
"query_string": self.query_string,
Expand Down
1 change: 1 addition & 0 deletions daphne/ws_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def onConnect(self, request):
{
"type": "websocket",
"path": unquote(self.path.decode("ascii")),
"raw_path": self.path,
"headers": self.clean_headers,
"query_string": self._raw_query_string, # Passed by HTTP protocol
"client": self.client_addr,
Expand Down
7 changes: 1 addition & 6 deletions tests/http_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ def run_daphne_http(
# Send it the request. We have to do this the long way to allow
# duplicate headers.
conn = HTTPConnection(test_app.host, test_app.port, timeout=timeout)
# Make sure path is urlquoted and add any params
path = parse.quote(path)
if params:
path += "?" + parse.urlencode(params, doseq=True)
conn.putrequest(method, path, skip_accept_encoding=True, skip_host=True)
Expand Down Expand Up @@ -128,8 +126,6 @@ def websocket_handshake(
# Send it the request. We have to do this the long way to allow
# duplicate headers.
conn = HTTPConnection(test_app.host, test_app.port, timeout=timeout)
# Make sure path is urlquoted and add any params
path = parse.quote(path)
if params:
path += "?" + parse.urlencode(params, doseq=True)
conn.putrequest("GET", path, skip_accept_encoding=True, skip_host=True)
Expand Down Expand Up @@ -247,12 +243,11 @@ def assert_key_sets(self, required_keys, optional_keys, actual_keys):
# Assert that no other keys are present
self.assertEqual(set(), present_keys - required_keys - optional_keys)

def assert_valid_path(self, path, request_path):
def assert_valid_path(self, path):
"""
Checks the path is valid and already url-decoded.
"""
self.assertIsInstance(path, str)
self.assertEqual(path, request_path)
# Assert that it's already url decoded
self.assertEqual(path, parse.unquote(path))

Expand Down
15 changes: 12 additions & 3 deletions tests/test_http_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def assert_valid_http_scope(
"http_version",
"method",
"path",
"raw_path",
"query_string",
"headers",
},
Expand All @@ -40,7 +41,7 @@ def assert_valid_http_scope(
self.assertIsInstance(scope["method"], str)
self.assertEqual(scope["method"], method.upper())
# Path
self.assert_valid_path(scope["path"], path)
self.assert_valid_path(scope["path"])
# HTTP version
self.assertIn(scope["http_version"], ["1.0", "1.1", "1.2"])
# Scheme
Expand Down Expand Up @@ -134,13 +135,21 @@ def test_post_request(self, request_path, request_body):
self.assert_valid_http_scope(scope, "POST", request_path)
self.assert_valid_http_request_message(messages[0], body=request_body)

def test_raw_path(self):
"""
Tests that /foo%2Fbar produces raw_path and a decoded path
"""
scope, _ = self.run_daphne_request("GET", "/foo%2Fbar")
self.assertEqual(scope["path"], "/foo/bar")
self.assertEqual(scope["raw_path"], b"/foo%2Fbar")

@given(request_headers=http_strategies.headers())
@settings(max_examples=5, deadline=5000)
def test_headers(self, request_headers):
"""
Tests that HTTP header fields are handled as specified
"""
request_path = "/te st-à/"
request_path = parse.quote("/te st-à/")
scope, messages = self.run_daphne_request(
"OPTIONS", request_path, headers=request_headers
)
Expand All @@ -160,7 +169,7 @@ def test_duplicate_headers(self, request_headers):
header_name = request_headers[0][0]
duplicated_headers = [(header_name, header[1]) for header in request_headers]
# Run the request
request_path = "/te st-à/"
request_path = parse.quote("/te st-à/")
scope, messages = self.run_daphne_request(
"OPTIONS", request_path, headers=duplicated_headers
)
Expand Down
19 changes: 16 additions & 3 deletions tests/test_websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ def assert_valid_websocket_scope(
"""
# Check overall keys
self.assert_key_sets(
required_keys={"type", "path", "query_string", "headers"},
required_keys={"type", "path", "raw_path", "query_string", "headers"},
optional_keys={"scheme", "root_path", "client", "server", "subprotocols"},
actual_keys=scope.keys(),
)
# Check that it is the right type
self.assertEqual(scope["type"], "websocket")
# Path
self.assert_valid_path(scope["path"], path)
self.assert_valid_path(scope["path"])
# Scheme
self.assertIn(scope.get("scheme", "ws"), ["ws", "wss"])
if scheme:
Expand Down Expand Up @@ -161,7 +161,7 @@ def test_http_bits(self, request_path, request_params, request_headers):
test_app.add_send_messages([{"type": "websocket.accept"}])
self.websocket_handshake(
test_app,
path=request_path,
path=parse.quote(request_path),
params=request_params,
headers=request_headers,
)
Expand All @@ -172,6 +172,19 @@ def test_http_bits(self, request_path, request_params, request_headers):
)
self.assert_valid_websocket_connect_message(messages[0])

def test_raw_path(self):
"""
Tests that /foo%2Fbar produces raw_path and a decoded path
"""
with DaphneTestingInstance() as test_app:
test_app.add_send_messages([{"type": "websocket.accept"}])
self.websocket_handshake(test_app, path="/foo%2Fbar")
# Validate the scope and messages we got
scope, _ = test_app.get_received()

self.assertEqual(scope["path"], "/foo/bar")
self.assertEqual(scope["raw_path"], b"/foo%2Fbar")

def test_text_frames(self):
"""
Tests we can send and receive text frames.
Expand Down

0 comments on commit 333f464

Please sign in to comment.