mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-10-27 10:42:28 +02:00
Add async_iterable_to_stream utility to avoid aiohttp compatibility issues
Create async_iterable_to_stream() in gns3server.utils.asyncio that converts an async iterable to an aiohttp StreamReader via a background feeder task. This bypasses aiohttp's AsyncIterablePayload which can cause 'Connection reset by peer' with certain HTTP servers. Use it in _run_http_query for the __aiter__ data path.
This commit is contained in:
parent
8e340f0ce8
commit
e659b64bf0
@ -32,7 +32,7 @@ else:
|
|||||||
from async_timeout import timeout as asynctimeout
|
from async_timeout import timeout as asynctimeout
|
||||||
|
|
||||||
from ..utils import parse_version
|
from ..utils import parse_version
|
||||||
from ..utils.asyncio import locking
|
from ..utils.asyncio import locking, async_iterable_to_stream
|
||||||
from ..controller.controller_error import (
|
from ..controller.controller_error import (
|
||||||
ControllerError,
|
ControllerError,
|
||||||
ControllerBadRequestError,
|
ControllerBadRequestError,
|
||||||
@ -537,6 +537,7 @@ class Compute:
|
|||||||
elif hasattr(data, "__aiter__"):
|
elif hasattr(data, "__aiter__"):
|
||||||
chunked = True
|
chunked = True
|
||||||
headers["content-type"] = "application/octet-stream"
|
headers["content-type"] = "application/octet-stream"
|
||||||
|
data = await async_iterable_to_stream(data)
|
||||||
# If the data is an open file we will iterate on it
|
# If the data is an open file we will iterate on it
|
||||||
elif isinstance(data, io.BufferedIOBase):
|
elif isinstance(data, io.BufferedIOBase):
|
||||||
chunked = True
|
chunked = True
|
||||||
|
|||||||
@ -136,3 +136,44 @@ def locking(f):
|
|||||||
return await f(oself, *args, **kwargs)
|
return await f(oself, *args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
async def async_iterable_to_stream(async_iter, limit=65536):
|
||||||
|
"""
|
||||||
|
Convert an async iterable into an aiohttp StreamReader.
|
||||||
|
|
||||||
|
This avoids passing async generators directly to aiohttp's payload
|
||||||
|
system, which can cause compatibility issues with certain HTTP servers.
|
||||||
|
|
||||||
|
:param async_iter: An async iterable that yields bytes
|
||||||
|
:param limit: Buffer limit for the StreamReader (default 64KB)
|
||||||
|
:returns: aiohttp.streams.StreamReader
|
||||||
|
"""
|
||||||
|
|
||||||
|
from aiohttp.streams import StreamReader
|
||||||
|
|
||||||
|
class _NoopProtocol:
|
||||||
|
_reading_paused = False
|
||||||
|
connected = True
|
||||||
|
|
||||||
|
def pause_reading(self):
|
||||||
|
self._reading_paused = True
|
||||||
|
|
||||||
|
def resume_reading(self, resume_parser=False):
|
||||||
|
self._reading_paused = False
|
||||||
|
|
||||||
|
reader = StreamReader(_NoopProtocol(), limit=limit)
|
||||||
|
|
||||||
|
async def _feed():
|
||||||
|
try:
|
||||||
|
async for chunk in async_iter:
|
||||||
|
reader.feed_data(chunk)
|
||||||
|
except GeneratorExit:
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
reader.feed_eof()
|
||||||
|
|
||||||
|
asyncio.ensure_future(_feed())
|
||||||
|
return reader
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user