Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions rolo/serving/twisted.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,9 @@ def writeHeaders(
else:
# newer twisted versions instead pass the headers object
for name, values in headers.getAllRawHeaders():
line = name + b": " + b",".join(values) + b"\r\n"
headerSequence.append(line)
for value in values:
line = name + b": " + value + b"\r\n"
headerSequence.append(line)

headerSequence.append(b"\r\n")
self.transport.writeSequence(headerSequence)
Expand Down
32 changes: 32 additions & 0 deletions tests/gateway/test_headers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import http.client
import json
from collections import defaultdict

import pytest
import requests
Expand All @@ -14,6 +16,8 @@ def handler(chain: HandlerChain, context: RequestContext, response: Response):
response.mimetype = "application/json"
response.headers["X-fOO_bar"] = "FooBar"
response.headers["content-md5"] = "af5e58f9a7c4682e1b410f2e9392a539"
response.headers.add("multi-value", "value1")
response.headers.add("multi-value", "value2")
return response

gateway = Gateway(request_handlers=[handler])
Expand All @@ -39,3 +43,31 @@ def handler(chain: HandlerChain, context: RequestContext, response: Response):
assert "X-fOO_bar" in response_headers
# even though it's a standard header, it should be in the original case
assert "content-md5" in response_headers
assert response_headers["multi-value"] == "value1, value2"


@pytest.mark.parametrize("serve_gateway", ["asgi", "twisted"], indirect=True)
def test_multivalue_header_handling(serve_gateway):
def handler(chain: HandlerChain, context: RequestContext, response: Response):
response.data = json.dumps({"headers": dict(context.request.headers)})
response.mimetype = "application/json"
response.headers.add("multi-value", "value1")
response.headers.add("multi-value", "value2")
return response

gateway = Gateway(request_handlers=[handler])

srv = serve_gateway(gateway)

# we need to use a low level HTTP client because `requests` does some header manipulation and concatenation which
# obscures the behavior
conn = http.client.HTTPConnection(host="127.0.0.1", port=srv.port)

conn.request("GET", url="/hello")
response = conn.getresponse()
response_headers = defaultdict(list)

for k, v in response.headers.items():
response_headers[k].append(v)

assert response_headers["multi-value"] == ["value1", "value2"]