-
Notifications
You must be signed in to change notification settings - Fork 45
H2 write stream #635
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
H2 write stream #635
Changes from 16 commits
6944ef0
9a5b0e5
2c116b0
5965b2e
cd862c6
57ebd82
9a30c83
8929cff
52b2d41
51959d2
7ff3ffb
2d07104
2a7a28c
fb7b558
93b0a0a
97dbaae
d697c50
3e71c93
f47d03c
53cb223
aa06439
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -112,6 +112,26 @@ def new(cls, | |
If successful, the Future will contain a new :class:`HttpClientConnection`. | ||
Otherwise, it will contain an exception. | ||
""" | ||
return HttpClientConnection._generic_new( | ||
host_name, | ||
port, | ||
bootstrap, | ||
socket_options, | ||
tls_connection_options, | ||
proxy_options) | ||
|
||
@staticmethod | ||
def _generic_new( | ||
host_name, | ||
port, | ||
bootstrap=None, | ||
socket_options=None, | ||
tls_connection_options=None, | ||
proxy_options=None, | ||
expected_version=None): | ||
""" | ||
Initialize the generic part of the HttpClientConnection class. | ||
""" | ||
assert isinstance(bootstrap, ClientBootstrap) or bootstrap is None | ||
assert isinstance(host_name, str) | ||
assert isinstance(port, int) | ||
|
@@ -126,24 +146,34 @@ def new(cls, | |
|
||
if not bootstrap: | ||
bootstrap = ClientBootstrap.get_or_create_static_default() | ||
|
||
connection = cls() | ||
connection._host_name = host_name | ||
connection._port = port | ||
shutdown_future = None | ||
|
||
def on_connection_setup(binding, error_code, http_version): | ||
if error_code == 0: | ||
connection._binding = binding | ||
connection._version = HttpVersion(http_version) | ||
future.set_result(connection) | ||
else: | ||
if expected_version and expected_version != http_version: | ||
# unexpected protocol version | ||
# AWS_ERROR_HTTP_UNSUPPORTED_PROTOCOL | ||
future.set_exception(awscrt.exceptions.from_code(2060)) | ||
return | ||
if error_code != 0: | ||
future.set_exception(awscrt.exceptions.from_code(error_code)) | ||
return | ||
if http_version == HttpVersion.Http2: | ||
connection = Http2ClientConnection() | ||
else: | ||
connection = HttpClientConnection() | ||
connection._host_name = host_name | ||
connection._port = port | ||
|
||
# on_shutdown MUST NOT reference the connection itself, just the shutdown_future within it. | ||
# Otherwise we create a circular reference that prevents the connection from getting GC'd. | ||
shutdown_future = connection.shutdown_future | ||
connection._binding = binding | ||
connection._version = HttpVersion(http_version) | ||
nonlocal shutdown_future | ||
shutdown_future = connection.shutdown_future | ||
TingDaoK marked this conversation as resolved.
Show resolved
Hide resolved
|
||
future.set_result(connection) | ||
|
||
def on_shutdown(error_code): | ||
if shutdown_future is None: | ||
# connection failed, ignore shutdown | ||
return | ||
if error_code: | ||
shutdown_future.set_exception(awscrt.exceptions.from_code(error_code)) | ||
else: | ||
|
@@ -219,6 +249,33 @@ def request(self, request, on_response=None, on_body=None): | |
return HttpClientStream(self, request, on_response, on_body) | ||
|
||
|
||
class Http2ClientConnection(HttpClientConnection): | ||
""" | ||
HTTP/2 client connection. | ||
|
||
This class extends HttpClientConnection with HTTP/2 specific functionality. | ||
""" | ||
@classmethod | ||
def new(cls, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. debatable: not sure it's worth giving this an explicit constructor? It's not doing anything to actually cause the user to get an HTTP/2 connection, like setting alpn=h2 or prior_knowledge_http2. It's just forcing an error if the user didn't end up getting an HTTP/2 connection. For better or worse, our API in C takes all possible options, because it's possible to kick off a connection and not know what you're going to get at the end. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we will add more to the connection, like settings, in the near future. |
||
host_name, | ||
port, | ||
bootstrap=None, | ||
socket_options=None, | ||
tls_connection_options=None, | ||
proxy_options=None): | ||
return HttpClientConnection._generic_new( | ||
host_name, | ||
port, | ||
bootstrap, | ||
socket_options, | ||
tls_connection_options, | ||
proxy_options, | ||
HttpVersion.Http2) | ||
|
||
def request(self, request, on_response=None, on_body=None, manual_write=False): | ||
return Http2ClientStream(self, request, on_response, on_body, manual_write) | ||
|
||
|
||
class HttpStreamBase(NativeResource): | ||
"""Base for HTTP stream classes""" | ||
__slots__ = ('_connection', '_completion_future', '_on_body_cb') | ||
|
@@ -258,9 +315,12 @@ class HttpClientStream(HttpStreamBase): | |
completes. If the exchange fails to complete, the Future will | ||
contain an exception indicating why it failed. | ||
""" | ||
__slots__ = ('_response_status_code', '_on_response_cb', '_on_body_cb', '_request') | ||
__slots__ = ('_response_status_code', '_on_response_cb', '_on_body_cb', '_request', '_version') | ||
|
||
def __init__(self, connection, request, on_response=None, on_body=None): | ||
self._generic_init(connection, request, on_response, on_body) | ||
|
||
def _generic_init(self, connection, request, on_response=None, on_body=None, http2_manual_write=False): | ||
assert isinstance(connection, HttpClientConnection) | ||
assert isinstance(request, HttpRequest) | ||
assert callable(on_response) or on_response is None | ||
|
@@ -273,8 +333,14 @@ def __init__(self, connection, request, on_response=None, on_body=None): | |
|
||
# keep HttpRequest alive until stream completes | ||
self._request = request | ||
self._version = connection.version | ||
|
||
self._binding = _awscrt.http_client_stream_new(self, connection, request, http2_manual_write) | ||
|
||
self._binding = _awscrt.http_client_stream_new(self, connection, request) | ||
@property | ||
def version(self): | ||
"""HttpVersion: Protocol used by this stream""" | ||
return self._version | ||
|
||
@property | ||
def response_status_code(self): | ||
|
@@ -307,6 +373,24 @@ def _on_complete(self, error_code): | |
self._completion_future.set_exception(awscrt.exceptions.from_code(error_code)) | ||
|
||
|
||
class Http2ClientStream(HttpClientStream): | ||
def __init__(self, connection, request, on_response=None, on_body=None, manual_write=False): | ||
super()._generic_init(connection, request, on_response, on_body, manual_write) | ||
|
||
def write_data(self, data_stream, end_stream=False): | ||
future = Future() | ||
body_stream = InputStream.wrap(data_stream, allow_none=True) | ||
|
||
def on_write_complete(error_code): | ||
if error_code: | ||
future.set_exception(awscrt.exceptions.from_code(error_code)) | ||
else: | ||
future.set_result(None) | ||
|
||
_awscrt.http2_client_stream_write_data(self, body_stream, end_stream, on_write_complete) | ||
return future | ||
|
||
|
||
class HttpMessageBase(NativeResource): | ||
""" | ||
Base for HttpRequest and HttpResponse classes. | ||
|
+1 −1 | .github/workflows/ci.yml | |
+1 −1 | README.md | |
+6 −50 | source/darwin/commoncrypto_aes.c |
+4 −4 | include/aws/http/exports.h | |
+3 −1 | include/aws/http/private/h2_frames.h | |
+6 −1 | source/h2_frames.c | |
+22 −32 | source/h2_stream.c | |
+1 −0 | tests/CMakeLists.txt | |
+3 −1 | tests/fuzz/fuzz_h2_decoder_correct.c | |
+4 −1 | tests/h2_test_helper.c | |
+3 −1 | tests/py_localhost/server.py | |
+1 −0 | tests/stream_test_helper.c | |
+1 −0 | tests/stream_test_helper.h | |
+64 −0 | tests/test_h2_client.c | |
+12 −3 | tests/test_h2_encoder.c |
+19 −1 | bin/benchmark/main.c | |
+0 −0 | include/aws/checksums/private/crc32_priv.h | |
+49 −0 | include/aws/checksums/private/crc_util.h | |
+1 −1 | source/arm/crc32c_arm.c | |
+7 −0 | source/checksums.c | |
+1 −1 | source/crc32.c | |
+1 −1 | source/crc_sw.c | |
+3 −15 | source/intel/asm/crc32c_sse42_asm.c | |
+4 −22 | source/intel/intrin/crc32c_sse42_avx512.c | |
+1 −1 | tests/crc_test.c |
Uh oh!
There was an error while loading. Please reload this page.