mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-01 10:11:30 +08:00
722 lines
22 KiB
Python
722 lines
22 KiB
Python
# -*- coding: utf-8 -
|
|
#
|
|
# This file is part of gunicorn released under the MIT license.
|
|
# See the NOTICE for more information.
|
|
|
|
"""Tests for HTTP/2 request and body classes."""
|
|
|
|
import pytest
|
|
|
|
from gunicorn.http2.request import HTTP2Request, HTTP2Body
|
|
from gunicorn.http2.stream import HTTP2Stream
|
|
|
|
|
|
class MockConnection:
|
|
"""Mock HTTP/2 connection for testing."""
|
|
|
|
def __init__(self, initial_window_size=65535):
|
|
self.initial_window_size = initial_window_size
|
|
|
|
|
|
class MockConfig:
|
|
"""Mock gunicorn configuration."""
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
|
|
class TestHTTP2Body:
|
|
"""Test HTTP2Body class."""
|
|
|
|
def test_init_with_data(self):
|
|
body = HTTP2Body(b"Hello, World!")
|
|
assert len(body) == 13
|
|
|
|
def test_init_empty(self):
|
|
body = HTTP2Body(b"")
|
|
assert len(body) == 0
|
|
|
|
def test_read_all(self):
|
|
body = HTTP2Body(b"Test data")
|
|
assert body.read() == b"Test data"
|
|
assert body.read() == b"" # Already consumed
|
|
|
|
def test_read_with_size(self):
|
|
body = HTTP2Body(b"Hello, World!")
|
|
assert body.read(5) == b"Hello"
|
|
assert body.read(2) == b", "
|
|
assert body.read(100) == b"World!"
|
|
assert body.read(1) == b""
|
|
|
|
def test_read_none_size(self):
|
|
body = HTTP2Body(b"Test")
|
|
assert body.read(None) == b"Test"
|
|
|
|
def test_readline_basic(self):
|
|
body = HTTP2Body(b"Line1\nLine2\nLine3")
|
|
assert body.readline() == b"Line1\n"
|
|
assert body.readline() == b"Line2\n"
|
|
assert body.readline() == b"Line3"
|
|
|
|
def test_readline_with_size(self):
|
|
body = HTTP2Body(b"Hello\nWorld")
|
|
assert body.readline(3) == b"Hel"
|
|
assert body.readline(10) == b"lo\n"
|
|
|
|
def test_readline_no_newline(self):
|
|
body = HTTP2Body(b"No newline here")
|
|
assert body.readline() == b"No newline here"
|
|
|
|
def test_readline_empty(self):
|
|
body = HTTP2Body(b"")
|
|
assert body.readline() == b""
|
|
|
|
def test_readline_crlf(self):
|
|
body = HTTP2Body(b"Line1\r\nLine2")
|
|
# BytesIO readline includes \r\n
|
|
assert body.readline() == b"Line1\r\n"
|
|
|
|
def test_readlines_basic(self):
|
|
body = HTTP2Body(b"Line1\nLine2\nLine3")
|
|
lines = body.readlines()
|
|
assert lines == [b"Line1\n", b"Line2\n", b"Line3"]
|
|
|
|
def test_readlines_with_hint(self):
|
|
body = HTTP2Body(b"Line1\nLine2\nLine3\nLine4")
|
|
# Hint affects how many lines are returned
|
|
lines = body.readlines(hint=5)
|
|
assert len(lines) >= 1
|
|
|
|
def test_readlines_empty(self):
|
|
body = HTTP2Body(b"")
|
|
assert body.readlines() == []
|
|
|
|
def test_iter(self):
|
|
body = HTTP2Body(b"Line1\nLine2\nLine3")
|
|
lines = list(body)
|
|
assert lines == [b"Line1\n", b"Line2\n", b"Line3"]
|
|
|
|
def test_len(self):
|
|
body = HTTP2Body(b"12345")
|
|
assert len(body) == 5
|
|
|
|
def test_close(self):
|
|
body = HTTP2Body(b"test")
|
|
body.close()
|
|
# Should not raise
|
|
with pytest.raises(ValueError):
|
|
body.read()
|
|
|
|
|
|
class TestHTTP2BodyReadStrategies:
|
|
"""Test different reading strategies matching HTTP/1.x patterns."""
|
|
|
|
def test_read_all_at_once(self):
|
|
data = b"A" * 1000
|
|
body = HTTP2Body(data)
|
|
result = body.read()
|
|
assert result == data
|
|
|
|
def test_read_chunked(self):
|
|
data = b"A" * 100
|
|
body = HTTP2Body(data)
|
|
chunks = []
|
|
while True:
|
|
chunk = body.read(10)
|
|
if not chunk:
|
|
break
|
|
chunks.append(chunk)
|
|
assert b"".join(chunks) == data
|
|
assert len(chunks) == 10
|
|
|
|
def test_read_byte_by_byte(self):
|
|
data = b"Hello"
|
|
body = HTTP2Body(data)
|
|
result = []
|
|
for _ in range(len(data)):
|
|
result.append(body.read(1))
|
|
assert b"".join(result) == data
|
|
|
|
def test_readline_all_lines(self):
|
|
data = b"Line1\nLine2\nLine3\n"
|
|
body = HTTP2Body(data)
|
|
lines = []
|
|
while True:
|
|
line = body.readline()
|
|
if not line:
|
|
break
|
|
lines.append(line)
|
|
assert lines == [b"Line1\n", b"Line2\n", b"Line3\n"]
|
|
|
|
|
|
class TestHTTP2Request:
|
|
"""Test HTTP2Request class."""
|
|
|
|
def _make_stream(self, headers, body=b""):
|
|
"""Helper to create a stream with headers and body."""
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=1, connection=conn)
|
|
stream.receive_headers(headers, end_stream=(len(body) == 0))
|
|
if body:
|
|
stream.request_body.write(body)
|
|
stream.request_complete = True
|
|
return stream
|
|
|
|
def test_basic_get_request(self):
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/test'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.method == 'GET'
|
|
assert req.uri == '/test'
|
|
assert req.path == '/test'
|
|
assert req.scheme == 'https'
|
|
assert req.version == (2, 0)
|
|
|
|
def test_post_request_with_body(self):
|
|
stream = self._make_stream(
|
|
[
|
|
(':method', 'POST'),
|
|
(':path', '/submit'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'api.example.com'),
|
|
('content-type', 'application/json'),
|
|
('content-length', '13'),
|
|
],
|
|
body=b'{"key":"val"}'
|
|
)
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('192.168.1.1', 54321))
|
|
|
|
assert req.method == 'POST'
|
|
assert req.body.read() == b'{"key":"val"}'
|
|
assert req.content_type == 'application/json'
|
|
assert req.content_length == 13
|
|
|
|
def test_path_with_query_string(self):
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/search?q=test&page=1'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.path == '/search'
|
|
assert req.query == 'q=test&page=1'
|
|
assert req.uri == '/search?q=test&page=1'
|
|
|
|
def test_path_with_fragment(self):
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/page#section'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.path == '/page'
|
|
assert req.fragment == 'section'
|
|
|
|
def test_headers_uppercase_conversion(self):
|
|
"""HTTP/2 headers are lowercase, should be converted to uppercase."""
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
('content-type', 'text/html'),
|
|
('accept-language', 'en-US'),
|
|
('x-custom-header', 'custom-value'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
header_names = [h[0] for h in req.headers]
|
|
assert 'CONTENT-TYPE' in header_names
|
|
assert 'ACCEPT-LANGUAGE' in header_names
|
|
assert 'X-CUSTOM-HEADER' in header_names
|
|
|
|
def test_host_header_from_authority(self):
|
|
"""Host header should be generated from :authority pseudo-header."""
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'test.example.com:8080'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
host = req.get_header('HOST')
|
|
assert host == 'test.example.com:8080'
|
|
|
|
def test_authority_overrides_host_header(self):
|
|
""":authority MUST override Host header per RFC 9113 section 8.3.1."""
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'authority.example.com'),
|
|
('host', 'explicit.example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
# Count HOST headers - should be exactly one, from :authority
|
|
host_headers = [h for h in req.headers if h[0] == 'HOST']
|
|
assert len(host_headers) == 1
|
|
assert host_headers[0][1] == 'authority.example.com'
|
|
|
|
def test_get_header_case_insensitive(self):
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
('x-test-header', 'test-value'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.get_header('X-TEST-HEADER') == 'test-value'
|
|
assert req.get_header('x-test-header') == 'test-value'
|
|
assert req.get_header('X-Test-Header') == 'test-value'
|
|
|
|
def test_get_header_not_found(self):
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.get_header('X-Not-Exists') is None
|
|
|
|
def test_content_length_property(self):
|
|
stream = self._make_stream([
|
|
(':method', 'POST'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
('content-length', '42'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.content_length == 42
|
|
|
|
def test_content_length_none_when_missing(self):
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.content_length is None
|
|
|
|
def test_content_length_invalid_value(self):
|
|
stream = self._make_stream([
|
|
(':method', 'POST'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
('content-length', 'not-a-number'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.content_length is None
|
|
|
|
def test_content_type_property(self):
|
|
stream = self._make_stream([
|
|
(':method', 'POST'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
('content-type', 'application/json; charset=utf-8'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.content_type == 'application/json; charset=utf-8'
|
|
|
|
def test_content_type_none_when_missing(self):
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.content_type is None
|
|
|
|
|
|
class TestHTTP2RequestConnectionState:
|
|
"""Test connection state methods."""
|
|
|
|
def _make_stream(self, headers):
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=1, connection=conn)
|
|
stream.receive_headers(headers, end_stream=True)
|
|
return stream
|
|
|
|
def test_should_close_default_false(self):
|
|
"""HTTP/2 connections are persistent by default."""
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.should_close() is False
|
|
|
|
def test_force_close(self):
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
req.force_close()
|
|
assert req.should_close() is True
|
|
assert req.must_close is True
|
|
|
|
|
|
class TestHTTP2RequestTrailers:
|
|
"""Test request trailers handling."""
|
|
|
|
def test_no_trailers(self):
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=1, connection=conn)
|
|
stream.receive_headers([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
], end_stream=True)
|
|
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.trailers == []
|
|
|
|
def test_with_trailers(self):
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=1, connection=conn)
|
|
stream.receive_headers([
|
|
(':method', 'POST'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
], end_stream=False)
|
|
stream.state = stream.state # Keep state
|
|
stream.trailers = [
|
|
('grpc-status', '0'),
|
|
('grpc-message', 'OK'),
|
|
]
|
|
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert len(req.trailers) == 2
|
|
assert ('GRPC-STATUS', '0') in req.trailers
|
|
assert ('GRPC-MESSAGE', 'OK') in req.trailers
|
|
|
|
|
|
class TestHTTP2RequestMetadata:
|
|
"""Test request metadata properties."""
|
|
|
|
def _make_stream(self, headers, stream_id=1):
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=stream_id, connection=conn)
|
|
stream.receive_headers(headers, end_stream=True)
|
|
return stream
|
|
|
|
def test_version_is_http2(self):
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.version == (2, 0)
|
|
|
|
def test_req_number_is_stream_id(self):
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
], stream_id=5)
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.req_number == 5
|
|
|
|
def test_peer_addr(self):
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('10.0.0.1', 54321))
|
|
|
|
assert req.peer_addr == ('10.0.0.1', 54321)
|
|
assert req.remote_addr == ('10.0.0.1', 54321)
|
|
|
|
def test_proxy_protocol_info_none(self):
|
|
"""HTTP/2 doesn't use proxy protocol through data stream."""
|
|
stream = self._make_stream([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
])
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.proxy_protocol_info is None
|
|
|
|
|
|
class TestHTTP2RequestRepr:
|
|
"""Test request string representation."""
|
|
|
|
def test_repr_format(self):
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=3, connection=conn)
|
|
stream.receive_headers([
|
|
(':method', 'POST'),
|
|
(':path', '/api/users'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
], end_stream=True)
|
|
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
repr_str = repr(req)
|
|
assert "HTTP2Request" in repr_str
|
|
assert "method=POST" in repr_str
|
|
assert "path=/api/users" in repr_str
|
|
assert "stream_id=3" in repr_str
|
|
|
|
|
|
class TestHTTP2RequestDefaults:
|
|
"""Test default values when pseudo-headers are missing."""
|
|
|
|
def test_default_method(self):
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=1, connection=conn)
|
|
stream.receive_headers([
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
], end_stream=True)
|
|
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.method == 'GET'
|
|
|
|
def test_default_scheme(self):
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=1, connection=conn)
|
|
stream.receive_headers([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':authority', 'example.com'),
|
|
], end_stream=True)
|
|
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.scheme == 'https'
|
|
|
|
def test_default_path(self):
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=1, connection=conn)
|
|
stream.receive_headers([
|
|
(':method', 'GET'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
], end_stream=True)
|
|
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.uri == '/'
|
|
assert req.path == '/'
|
|
|
|
|
|
class TestHTTP2RequestPriority:
|
|
"""Test HTTP2Request priority attributes."""
|
|
|
|
def test_default_priority_values(self):
|
|
"""Test that request inherits default stream priority."""
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=1, connection=conn)
|
|
stream.receive_headers([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
], end_stream=True)
|
|
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
assert req.priority_weight == 16
|
|
assert req.priority_depends_on == 0
|
|
|
|
def test_custom_priority_values(self):
|
|
"""Test that request inherits custom stream priority."""
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=3, connection=conn)
|
|
|
|
# Update priority before creating request
|
|
stream.update_priority(weight=200, depends_on=1)
|
|
|
|
stream.receive_headers([
|
|
(':method', 'POST'),
|
|
(':path', '/api/data'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
], end_stream=False)
|
|
stream.receive_data(b'{"data": "test"}', end_stream=True)
|
|
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('192.168.1.100', 54321))
|
|
|
|
assert req.priority_weight == 200
|
|
assert req.priority_depends_on == 1
|
|
|
|
def test_priority_reflects_stream_at_request_creation(self):
|
|
"""Test that priority reflects stream state when request is created."""
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=1, connection=conn)
|
|
stream.receive_headers([
|
|
(':method', 'GET'),
|
|
(':path', '/'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
], end_stream=True)
|
|
|
|
cfg = MockConfig()
|
|
|
|
# Create request with default priority
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
assert req.priority_weight == 16
|
|
|
|
# Update stream priority after request was created
|
|
stream.update_priority(weight=256)
|
|
|
|
# Request should still have old value (captured at creation time)
|
|
assert req.priority_weight == 16
|
|
|
|
# Stream has new value
|
|
assert stream.priority_weight == 256
|
|
|
|
|
|
class MockWSGIConfig:
|
|
"""Mock gunicorn configuration with WSGI-required attributes."""
|
|
|
|
def __init__(self):
|
|
self.errorlog = '-'
|
|
self.workers = 1
|
|
|
|
|
|
class TestHTTP2RequestWSGIEnviron:
|
|
"""Test HTTP/2 priority in WSGI environ."""
|
|
|
|
def test_priority_in_wsgi_environ(self):
|
|
"""Test that HTTP/2 priority is added to WSGI environ."""
|
|
from unittest import mock
|
|
from gunicorn.http.wsgi import create
|
|
|
|
conn = MockConnection()
|
|
stream = HTTP2Stream(stream_id=1, connection=conn)
|
|
stream.update_priority(weight=128, depends_on=3)
|
|
stream.receive_headers([
|
|
(':method', 'GET'),
|
|
(':path', '/test'),
|
|
(':scheme', 'https'),
|
|
(':authority', 'example.com'),
|
|
], end_stream=True)
|
|
|
|
cfg = MockConfig()
|
|
req = HTTP2Request(stream, cfg, ('127.0.0.1', 12345))
|
|
|
|
# Create a mock socket
|
|
mock_sock = mock.Mock()
|
|
mock_sock.getsockname.return_value = ('127.0.0.1', 8443)
|
|
|
|
# Use WSGI config for environ creation
|
|
wsgi_cfg = MockWSGIConfig()
|
|
|
|
# Create WSGI environ
|
|
resp, environ = create(req, mock_sock, ('127.0.0.1', 12345), ('127.0.0.1', 8443), wsgi_cfg)
|
|
|
|
# Verify priority is in environ
|
|
assert environ.get('gunicorn.http2.priority_weight') == 128
|
|
assert environ.get('gunicorn.http2.priority_depends_on') == 3
|
|
|
|
def test_priority_not_in_environ_for_http1(self):
|
|
"""Test that HTTP/1 requests don't have priority keys."""
|
|
from unittest import mock
|
|
from gunicorn.http.wsgi import create
|
|
|
|
# Create a mock HTTP/1 request (no priority attributes)
|
|
mock_req = mock.Mock()
|
|
mock_req.headers = [('HOST', 'example.com')]
|
|
mock_req.scheme = 'https'
|
|
mock_req.path = '/test'
|
|
mock_req.query = ''
|
|
mock_req.fragment = ''
|
|
mock_req.method = 'GET'
|
|
mock_req.uri = '/test'
|
|
mock_req.version = (1, 1)
|
|
mock_req._expected_100_continue = False
|
|
mock_req.proxy_protocol_info = None
|
|
mock_req.body = mock.Mock()
|
|
|
|
# Remove priority attributes to simulate HTTP/1 request
|
|
del mock_req.priority_weight
|
|
del mock_req.priority_depends_on
|
|
|
|
wsgi_cfg = MockWSGIConfig()
|
|
|
|
mock_sock = mock.Mock()
|
|
mock_sock.getsockname.return_value = ('127.0.0.1', 8443)
|
|
|
|
resp, environ = create(mock_req, mock_sock, ('127.0.0.1', 12345), ('127.0.0.1', 8443), wsgi_cfg)
|
|
|
|
# HTTP/1 requests should not have priority keys
|
|
assert 'gunicorn.http2.priority_weight' not in environ
|
|
assert 'gunicorn.http2.priority_depends_on' not in environ
|