Skip to content

Commit 0fe5d76

Browse files
committed
fix tests and doc
1 parent 76f0e33 commit 0fe5d76

File tree

2 files changed

+39
-81
lines changed

2 files changed

+39
-81
lines changed

awscrt/aio/http_asyncio.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
class HttpClientConnectionAsyncUnified(HttpClientConnectionBase):
2828
"""
29-
An async HTTP client connection.
29+
An async unified HTTP client connection for either a HTTP/1 or HTTP/2 connection.
3030
3131
Use `HttpClientConnectionAsync.new()` to establish a new connection.
3232
"""
@@ -89,23 +89,24 @@ def request(self,
8989
request: 'HttpRequest',
9090
async_body: AsyncIterator[bytes] = None,
9191
loop: Optional[asyncio.AbstractEventLoop] = None) -> 'HttpClientStreamAsyncUnified':
92-
"""Create `HttpClientStreamAsync` to carry out the request/response exchange.
92+
"""Create `HttpClientStreamAsyncUnified` to carry out the request/response exchange.
9393
9494
Args:
9595
request (HttpRequest): Definition for outgoing request.
96+
async_body (AsyncIterator[bytes], optional): Async iterator providing chunks of the request body.
97+
If provided, the body will be sent incrementally as chunks become available.
9698
loop (Optional[asyncio.AbstractEventLoop]): Event loop to use for async operations.
9799
If None, the current event loop is used.
98100
99101
Returns:
100-
HttpClientStreamAsync: Stream for the HTTP request/response exchange.
102+
HttpClientStreamAsyncUnified: Stream for the HTTP request/response exchange.
101103
"""
102-
# return HttpClientStreamAsyncBase(self, request, loop)
103-
pass
104+
raise NotImplementedError("Subclasses must implement request")
104105

105106

106107
class HttpClientConnectionAsync(HttpClientConnectionAsyncUnified):
107108
"""
108-
An async HTTP/1.1 client connection.
109+
An async HTTP/1.1 only client connection.
109110
110111
Use `HttpClientConnectionAsync.new()` to establish a new connection.
111112
"""
@@ -161,6 +162,8 @@ def request(self,
161162
162163
Args:
163164
request (HttpRequest): Definition for outgoing request.
165+
async_body (AsyncIterator[bytes], optional): Async iterator providing chunks of the request body.
166+
Not supported for HTTP/1.1 connections yet, use the request's body_stream instead.
164167
loop (Optional[asyncio.AbstractEventLoop]): Event loop to use for async operations.
165168
If None, the current event loop is used.
166169
@@ -172,7 +175,7 @@ def request(self,
172175

173176
class Http2ClientConnectionAsync(HttpClientConnectionAsyncUnified):
174177
"""
175-
An async HTTP/2 client connection.
178+
An async HTTP/2 only client connection.
176179
177180
Use `Http2ClientConnectionAsync.new()` to establish a new connection.
178181
"""
@@ -224,7 +227,8 @@ def request(self,
224227
225228
Args:
226229
request (HttpRequest): Definition for outgoing request.
227-
manual_write (bool): If True, enables manual data writing on the stream.
230+
async_body (AsyncIterator[bytes], optional): Async iterator providing chunks of the request body.
231+
If provided, the body will be sent incrementally as chunks become available from the iterator.
228232
loop (Optional[asyncio.AbstractEventLoop]): Event loop to use for async operations.
229233
If None, the current event loop is used.
230234

test/test_http_asyncio.py

Lines changed: 27 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -317,17 +317,22 @@ async def _test_h2_manual_write_exception(self):
317317

318318
request = HttpRequest('GET', url.path)
319319
request.headers.add('host', url.hostname)
320+
321+
# Create stream without using async_body parameter
322+
# (which would be needed to properly configure it for writing)
320323
stream = connection.request(request)
321324

325+
# The stream should have write_data attribute but using it should raise an exception
326+
# since the stream isn't properly configured for manual writing
322327
exception = None
323328
try:
324-
# If the stream is not configured to allow manual writes, this should throw an exception
325-
await stream.write_data(BytesIO(b'hello'), False)
326-
except RuntimeError as e:
329+
# Attempt to access internal write_data method which should raise an exception
330+
# since the stream wasn't created with async_body
331+
await stream._write_data(BytesIO(b'hello'), False)
332+
except (RuntimeError, AttributeError) as e:
327333
exception = e
328334

329335
self.assertIsNotNone(exception)
330-
331336
await connection.close()
332337

333338
def test_connect_http(self):
@@ -563,13 +568,18 @@ async def _test_h2_mock_server_manual_write(self):
563568

564569
request = HttpRequest('POST', self.mock_server_url.path)
565570
request.headers.add('host', self.mock_server_url.hostname)
566-
stream = connection.request(request, manual_write=True)
567571

568-
# Write data in chunks
569-
await stream.write_data(BytesIO(b'hello'), False)
570-
await stream.write_data(BytesIO(b'he123123'), False)
571-
await stream.write_data(None, False)
572-
await stream.write_data(BytesIO(b'hello'), True)
572+
# Create an async generator for the request body
573+
body_chunks = [b'hello', b'he123123', b'', b'hello']
574+
total_length = 0
575+
for i in body_chunks:
576+
total_length = total_length + len(i)
577+
578+
async def body_generator():
579+
for i in body_chunks:
580+
yield i
581+
582+
stream = connection.request(request, async_body=body_generator())
573583

574584
# Collect response
575585
response = Response()
@@ -578,7 +588,8 @@ async def _test_h2_mock_server_manual_write(self):
578588
# Check result
579589
self.assertEqual(200, status_code)
580590
self.assertEqual(200, response.status_code)
581-
591+
# mock server response the total length received, check if it matches what we sent
592+
self.assertEqual(total_length, int(response.body.decode()))
582593
await connection.close()
583594

584595
class DelayStream:
@@ -598,60 +609,6 @@ def read(self, _len):
598609
self._read = True
599610
return b'hello'
600611

601-
async def _test_h2_mock_server_manual_write_read_exception(self):
602-
connection = await self._new_mock_connection()
603-
# check we set an h2 connection
604-
self.assertEqual(connection.version, HttpVersion.Http2)
605-
606-
request = HttpRequest('POST', self.mock_server_url.path)
607-
request.headers.add('host', self.mock_server_url.hostname)
608-
stream = connection.request(request, manual_write=True)
609-
610-
# Try to write data with a bad stream that raises an exception
611-
exception = None
612-
data = self.DelayStream(bad_read=True)
613-
try:
614-
await stream.write_data(data, False)
615-
except Exception as e:
616-
exception = e
617-
stream_completion_exception = None
618-
try:
619-
await stream.wait_for_completion()
620-
except Exception as e:
621-
stream_completion_exception = e
622-
623-
self.assertIsNotNone(exception)
624-
self.assertIsNotNone(stream_completion_exception)
625-
# assert that the exception is the same as the one we got from write_data.
626-
self.assertEqual(str(exception), str(stream_completion_exception))
627-
await connection.close()
628-
629-
async def _test_h2_mock_server_manual_write_lifetime(self):
630-
connection = await self._new_mock_connection()
631-
# check we set an h2 connection
632-
self.assertEqual(connection.version, HttpVersion.Http2)
633-
634-
request = HttpRequest('POST', self.mock_server_url.path)
635-
request.headers.add('host', self.mock_server_url.hostname)
636-
stream = connection.request(request, manual_write=True)
637-
638-
# Create data stream and immediately delete the reference after writing
639-
data = self.DelayStream(bad_read=False)
640-
await stream.write_data(data, False)
641-
del data
642-
643-
# Finish the request
644-
await stream.write_data(None, True)
645-
646-
# Collect response
647-
response = Response()
648-
status_code = await response.collect_response(stream)
649-
650-
# Check result
651-
self.assertEqual(200, status_code)
652-
653-
await connection.close()
654-
655612
async def _test_h2_mock_server_settings(self):
656613
# Test with invalid settings - should throw an exception
657614
exception = None
@@ -669,9 +626,12 @@ async def _test_h2_mock_server_settings(self):
669626

670627
request = HttpRequest('POST', self.mock_server_url.path)
671628
request.headers.add('host', self.mock_server_url.hostname)
672-
stream = connection.request(request, manual_write=True)
673629

674-
await stream.write_data(BytesIO(b'hello'), True)
630+
# Create an async generator for the request body
631+
async def body_generator():
632+
yield b'hello'
633+
634+
stream = connection.request(request, async_body=body_generator())
675635

676636
response = Response()
677637
status_code = await response.collect_response(stream)
@@ -684,12 +644,6 @@ async def _test_h2_mock_server_settings(self):
684644
def test_h2_mock_server_manual_write(self):
685645
asyncio.run(self._test_h2_mock_server_manual_write())
686646

687-
def test_h2_mock_server_manual_write_read_exception(self):
688-
asyncio.run(self._test_h2_mock_server_manual_write_read_exception())
689-
690-
def test_h2_mock_server_manual_write_lifetime(self):
691-
asyncio.run(self._test_h2_mock_server_manual_write_lifetime())
692-
693647
def test_h2_mock_server_settings(self):
694648
asyncio.run(self._test_h2_mock_server_settings())
695649

0 commit comments

Comments
 (0)