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
1 change: 1 addition & 0 deletions examples/bottle-app.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def info():
"vcpu_time_ms": vcpu_time,
"request_method": request.environ.get("REQUEST_METHOD"),
"path_info": request.environ.get("PATH_INFO"),
"request_headers": dict(request.headers),
}


Expand Down
1 change: 1 addition & 0 deletions examples/flask-app.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def info():
"request_method": request.environ.get("REQUEST_METHOD"),
"path_info": request.environ.get("PATH_INFO"),
"python_version": sys.version,
"request_headers": dict(request.headers),
}


Expand Down
43 changes: 43 additions & 0 deletions fastly_compute/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,49 @@ def start_response(
"HTTP_HOST": url.netloc or "localhost",
}

# Add incoming HTTP headers to environ (WSGI spec requires HTTP_ prefix)
# Use cursor-based iteration to read all header names
cursor = 0
while True:
header_names_str, next_cursor = req.get_header_names(
max_len=8192, cursor=cursor
)
if not header_names_str:
break

# Header names are NUL-separated
header_names_split = (
header_names_str.rstrip("\0").split("\0") if header_names_str else []
)

for header_name in header_names_split:
if not header_name:
continue

# Get the header value
header_value_bytes = req.get_header_value(header_name, max_len=8192)
if header_value_bytes is None:
continue

# See https://peps.python.org/pep-3333/ - ISO-8859-1 encoding
# should be used for bytes values across this boundary.
header_value_str = header_value_bytes.decode("iso-8859-1")

# Special handling for Content-Type and Content-Length (no HTTP_ prefix)
if header_name.lower() == "content-type":
environ["CONTENT_TYPE"] = header_value_str
elif header_name.lower() == "content-length":
environ["CONTENT_LENGTH"] = header_value_str
else:
# Convert to WSGI format: HTTP_ prefix, uppercase, hyphens to underscores
wsgi_key = "HTTP_" + header_name.upper().replace("-", "_")
environ[wsgi_key] = header_value_str

# If there are more headers, continue with next cursor
if next_cursor is None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not float this test up to the while condition, e.g. while cursor is not None?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered it but you would need to have an extra assignment of the value outside the loop in addition to inside the loop (basically, python doesn't have do-while or assignments with checks in the loop header).

I think it's probably better as-is.

break
cursor = next_cursor

try:
# Call the WSGI app and collect response body chunks
for body_chunk in app(environ, start_response):
Expand Down
2 changes: 2 additions & 0 deletions tests/test_bottle_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ def test_custom_headers(self):
headers = {"X-Custom-Header": "test-value"}
response = self.get("/info", headers=headers)
assert response.status_code == 200
data = response.json()
assert data["request_headers"]["X-Custom-Header"] == "test-value"

def test_error_endpoint_handling(self):
"""Test that the error endpoint returns 500 and triggers viceroy output display."""
Expand Down
3 changes: 2 additions & 1 deletion tests/test_flask_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def test_hello_endpoint(self):

def test_info_endpoint(self):
"""Test the info endpoint returns expected JSON with WIT data."""
response = self.get("/info")
response = self.get("/info", headers={"Test-Header": "test-value"})

assert response.status_code == 200
assert response.headers.get("content-type", "").startswith("application/json")
Expand All @@ -32,6 +32,7 @@ def test_info_endpoint(self):
# Check WIT API data
assert "vcpu_time_ms" in data
assert isinstance(data["vcpu_time_ms"], int)
assert data["request_headers"]["Test-Header"] == "test-value"

def test_error_endpoint_handling(self):
"""Test that the error endpoint returns 500."""
Expand Down