event.stream_ended was not True when receving response after HEAD request
whitehatboxer opened this issue · comments
I used examples http3_client.py, and modified it to send HEAD request.
But I found it don't work, it seemed like that event.stream_ended was not True after receving entrie response from quic. So the coroutine was hang.
My h3 client class looks like as below:
class H3Model(QuicConnectionProtocol):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.h3_agent = H3Connection(self._quic)
self.request_streams = {}
self.stream_waiters = {}
def quic_event_received(self, event: QuicEvent):
events = self.h3_agent.handle_event(event)
for event in events:
self.handle_h3_event(event)
def handle_h3_event(self, event: H3Event):
if isinstance(event, (DataReceived, HeadersReceived)):
stream_id = event.stream_id
if stream_id in self.request_streams:
self.request_streams[stream_id].append(event)
if event.stream_ended:
waiter = self.stream_waiters.pop(stream_id)
waiter.set_result(self.request_streams.pop(stream_id))
async def request(
self,
uri: str,
method: str,
headers: dict,
data: str | None = None,
json: dict | None = None
):
h3_headers = [
(b":method", method.encode()),
(b":scheme", b"https"),
(b":path", uri.encode()),
]
if json:
h3_headers.append((b"content-type", b"application/json"))
for k, v in headers.items():
h3_headers.append((k.lower().encode(), v.encode()))
stream_id = self._quic.get_next_available_stream_id()
self.h3_agent.send_headers(stream_id, headers=h3_headers, end_stream=not data and not json)
if json:
self.h3_agent.send_data(stream_id, data=jsonlib.dumps(json).encode(), end_stream=True)
elif data:
self.h3_agent.send_data(stream_id, data=data.encode(), end_stream=True)
waiter = self._loop.create_future()
self.stream_waiters[stream_id] = waiter
self.request_streams[stream_id] = deque()
self.transmit()
http_events = await asyncio.shield(waiter)
# compose response
status = None
headers = {}
body = b""
for event in http_events:
if isinstance(event, HeadersReceived):
for k, v in event.headers:
if k == b":status":
status = int(v)
else:
headers[k.decode()] = v.decode()
elif isinstance(event, DataReceived):
body += event.data
resp = Response(
status_code=status,
headers=headers,
content=body
)
return resp
I have to change handle_h3_event(self, event: H3Event)
to work through it, the modified version looks like:
def handle_h3_event(self, event: H3Event):
if isinstance(event, (DataReceived, HeadersReceived)):
stream_id = event.stream_id
if stream_id in self.request_streams:
self.request_streams[stream_id].append(event)
if event.stream_ended or self.head_recorders[stream_id]:
waiter = self.stream_waiters.pop(stream_id)
waiter.set_result(self.request_streams.pop(stream_id))
self.head_recorders.pop(stream_id)
I wonder whether there was some wrong code in my file or other reason?
I haven't been able to reproduce this in local testing. If I have a client send a HEAD instead of a GET, it always get a response that ends the stream, and my client sees that in the events that it gets from aioquic. What server are you talking to?
I haven't been able to reproduce this in local testing. If I have a client send a HEAD instead of a GET, it always get a response that ends the stream, and my client sees that in the events that it gets from aioquic. What server are you talking to?
I used Nginx 1.26.0 as my test server.