mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-02 10:41:30 +08:00
Add streaming body support to HTTP2Stream
- Add _body_chunks, _body_event, _body_complete fields for streaming - Modify receive_data() to populate chunks queue alongside BytesIO - Add async read_body_chunk() method for streaming body reads This enables HTTP/2 request body streaming instead of buffering entire uploads, reducing memory usage for large file uploads.
This commit is contained in:
parent
464cbbfad5
commit
ea37eaaa6d
@ -71,6 +71,11 @@ class HTTP2Stream:
|
|||||||
self.priority_depends_on = 0
|
self.priority_depends_on = 0
|
||||||
self.priority_exclusive = False
|
self.priority_exclusive = False
|
||||||
|
|
||||||
|
# Streaming body support (avoids buffering entire uploads)
|
||||||
|
self._body_chunks = []
|
||||||
|
self._body_event = None # Lazy-init asyncio.Event
|
||||||
|
self._body_complete = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_client_stream(self):
|
def is_client_stream(self):
|
||||||
"""Check if this is a client-initiated stream (odd stream ID)."""
|
"""Check if this is a client-initiated stream (odd stream ID)."""
|
||||||
@ -122,7 +127,7 @@ class HTTP2Stream:
|
|||||||
self.request_complete = True
|
self.request_complete = True
|
||||||
|
|
||||||
def receive_data(self, data, end_stream=False):
|
def receive_data(self, data, end_stream=False):
|
||||||
"""Process received DATA frame.
|
"""Process received DATA frame with streaming support.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data: Bytes received
|
data: Bytes received
|
||||||
@ -137,11 +142,21 @@ class HTTP2Stream:
|
|||||||
f"Cannot receive data in state {self.state.name}"
|
f"Cannot receive data in state {self.state.name}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add to chunks queue for streaming reads
|
||||||
|
if data:
|
||||||
|
self._body_chunks.append(data)
|
||||||
|
if self._body_event:
|
||||||
|
self._body_event.set()
|
||||||
|
|
||||||
|
# Also write to legacy BytesIO for compatibility
|
||||||
self.request_body.write(data)
|
self.request_body.write(data)
|
||||||
|
|
||||||
if end_stream:
|
if end_stream:
|
||||||
self._half_close_remote()
|
self._half_close_remote()
|
||||||
self.request_complete = True
|
self.request_complete = True
|
||||||
|
self._body_complete = True
|
||||||
|
if self._body_event:
|
||||||
|
self._body_event.set()
|
||||||
|
|
||||||
def receive_trailers(self, trailers):
|
def receive_trailers(self, trailers):
|
||||||
"""Process received trailing headers.
|
"""Process received trailing headers.
|
||||||
@ -283,6 +298,31 @@ class HTTP2Stream:
|
|||||||
"""
|
"""
|
||||||
return self.request_body.getvalue()
|
return self.request_body.getvalue()
|
||||||
|
|
||||||
|
async def read_body_chunk(self):
|
||||||
|
"""Read next body chunk asynchronously for streaming.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bytes: Next chunk of body data, or None if body is complete.
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
# Initialize event lazily (avoids event loop issues at construction)
|
||||||
|
if self._body_event is None:
|
||||||
|
self._body_event = asyncio.Event()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Return chunk if available
|
||||||
|
if self._body_chunks:
|
||||||
|
return self._body_chunks.pop(0)
|
||||||
|
|
||||||
|
# No more data expected
|
||||||
|
if self._body_complete:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Wait for more data
|
||||||
|
self._body_event.clear()
|
||||||
|
await self._body_event.wait()
|
||||||
|
|
||||||
def get_pseudo_headers(self):
|
def get_pseudo_headers(self):
|
||||||
"""Extract HTTP/2 pseudo-headers from request headers.
|
"""Extract HTTP/2 pseudo-headers from request headers.
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user