Skip to content

Commit

Permalink
H2 unittest and integration test (#142)
Browse files Browse the repository at this point in the history
Co-authored-by: Dengke Tang <[email protected]>
  • Loading branch information
TingDaoK and TingDaoK authored Apr 30, 2020
1 parent bf06cac commit 7ed7639
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 10 deletions.
2 changes: 1 addition & 1 deletion aws-common-runtime/aws-c-common
Submodule aws-c-common updated 31 files
+1 −1 .builder/actions/clang-tidy.py
+14 −0 AWSCRTAndroidTestRunner/.gitignore
+3 −0 AWSCRTAndroidTestRunner/app/.gitignore
+48 −0 AWSCRTAndroidTestRunner/app/build.gradle
+13 −0 ...roidTestRunner/app/src/androidTest/java/software/amazon/awssdk/crt/awscrtandroidtestrunner/NativeTest.kt.in
+12 −0 ...TestRunner/app/src/androidTest/java/software/amazon/awssdk/crt/awscrtandroidtestrunner/NativeTestFixture.kt
+5 −0 AWSCRTAndroidTestRunner/app/src/main/AndroidManifest.xml
+58 −0 AWSCRTAndroidTestRunner/app/src/main/cpp/CMakeLists.txt
+31 −0 AWSCRTAndroidTestRunner/app/src/main/cpp/native-lib.cpp
+29 −0 AWSCRTAndroidTestRunner/build.gradle
+23 −0 AWSCRTAndroidTestRunner/gradle.properties
+ AWSCRTAndroidTestRunner/gradle/wrapper/gradle-wrapper.jar
+6 −0 AWSCRTAndroidTestRunner/gradle/wrapper/gradle-wrapper.properties
+172 −0 AWSCRTAndroidTestRunner/gradlew
+84 −0 AWSCRTAndroidTestRunner/gradlew.bat
+2 −0 AWSCRTAndroidTestRunner/settings.gradle
+4 −0 CMakeLists.txt
+1 −1 cmake/AwsFeatureTests.cmake
+8 −0 cmake/AwsTestHarness.cmake
+1 −1 include/aws/common/command_line_parser.h
+3 −0 include/aws/common/hash_table.h
+49 −0 include/aws/common/resource_name.h
+168 −0 source/android/logging.c
+8 −9 source/command_line_parser.c
+8 −0 source/hash_table.c
+7 −0 source/logging.c
+121 −0 source/resource_name.c
+47 −38 tests/CMakeLists.txt
+9 −0 tests/command_line_parser_test.c
+31 −0 tests/hash_table_test.c
+209 −0 tests/resource_name_test.c
2 changes: 1 addition & 1 deletion aws-common-runtime/aws-c-compression
17 changes: 15 additions & 2 deletions awscrt/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,19 @@ class HttpConnectionBase(NativeResource):
shutdown_future (concurrent.futures.Future): Completes when the connection has finished shutting down.
Future will contain a result of None, or an exception indicating why shutdown occurred.
Note that the connection may have been garbage-collected before this future completes.
_version (HttpVersion): Protocol version the connection used.
"""

__slots__ = ('shutdown_future')
__slots__ = ('shutdown_future', '_version')

def __init__(self):
super(HttpConnectionBase, self).__init__()
self.shutdown_future = Future()

@property
def version(self):
return self._version

def close(self):
"""
Close the connection.
Expand Down Expand Up @@ -94,9 +99,10 @@ def new(cls,
connection._host_name = host_name
connection._port = port

def on_connection_setup(binding, error_code):
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:
future.set_exception(awscrt.exceptions.from_code(error_code))
Expand Down Expand Up @@ -386,6 +392,13 @@ class HttpProxyAuthenticationType(IntEnum):
Basic = 1


class HttpVersion(IntEnum):
Unknown = 0
Http1_0 = 1
Http1_1 = 2
Http2 = 3


class HttpProxyOptions(object):
"""
Proxy options for HTTP clients.
Expand Down
29 changes: 29 additions & 0 deletions elasticurl.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,20 @@ def print_header_list(headers):
parser.add_argument(
'-p',
'--alpn',
default=["h2", "http/1.1"],
required=False,
help='STRING: protocol for ALPN. May be specified multiple times.',
action='append')
parser.add_argument(
'--http2',
required=False,
help='HTTP/2 connection required',
action="store_true")
parser.add_argument(
'--http1_1',
required=False,
help='HTTP/1.1 connection required',
action="store_true")
parser.add_argument(
'-v',
'--verbose',
Expand Down Expand Up @@ -117,6 +128,16 @@ def print_header_list(headers):

io.init_logging(log_level, log_output)


required_version = http.HttpVersion.Unknown
if args.http1_1:
required_version = http.HttpVersion.Http1_1
args.alpn = ["http/1.1"]
if args.http2:
required_version = http.HttpVersion.Http2
args.alpn = ["h2"]


# an event loop group is needed for IO operations. Unless you're a server or a client doing hundreds of connections
# you only want one of these.
event_loop_group = io.EventLoopGroup(1)
Expand All @@ -139,6 +160,8 @@ def print_header_list(headers):
else:
if scheme == 'http':
port = 80
if args.http2:
sys.exit("Error, we don't support h2c, please use TLS for HTTP/2 connection")


tls_connection_options = None
Expand Down Expand Up @@ -201,6 +224,12 @@ def on_incoming_body(http_stream, chunk, **kwargs):
connection = connect_future.result(10)
connection.shutdown_future.add_done_callback(on_connection_shutdown)

if required_version:
if connection.version != required_version:
error_msg = "Error. The requested HTTP version " + args.alpn[0] + " is not supported by the peer."
sys.exit(error_msg)


request = http.HttpRequest(args.method, body_stream=data_stream)

if args.get:
Expand Down
7 changes: 5 additions & 2 deletions source/http_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,21 @@ static void s_on_client_connection_setup(
if (aws_py_gilstate_ensure(&state)) {
return; /* Python has shut down. Nothing matters anymore, but don't crash */
}

enum aws_http_version http_version = AWS_HTTP_VERSION_UNKNOWN;
/* If setup was successful, encapsulate binding so we can pass it to python */
PyObject *capsule = NULL;
if (!error_code) {
capsule = PyCapsule_New(connection, s_capsule_name_http_connection, s_connection_capsule_destructor);
if (!capsule) {
error_code = AWS_ERROR_UNKNOWN;
}
http_version = aws_http_connection_get_version(native_connection);
}

/* Invoke on_setup, then clear our reference to it */
PyObject *result = PyObject_CallFunction(connection->on_setup, "(Oi)", capsule ? capsule : Py_None, error_code);
PyObject *result =
PyObject_CallFunction(connection->on_setup, "(Oii)", capsule ? capsule : Py_None, error_code, http_version);

if (result) {
Py_DECREF(result);
} else {
Expand Down
47 changes: 46 additions & 1 deletion test/test_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@

from __future__ import absolute_import
import awscrt.exceptions
from awscrt.http import HttpClientConnection, HttpClientStream, HttpHeaders, HttpProxyOptions, HttpRequest
from awscrt.http import HttpClientConnection, HttpClientStream, HttpHeaders, HttpProxyOptions, HttpRequest, HttpVersion
from awscrt.io import ClientBootstrap, ClientTlsContext, DefaultHostResolver, EventLoopGroup, TlsConnectionOptions, TlsContextOptions
from concurrent.futures import Future
from io import open # Python2's built-in open() doesn't return a stream
import os
import ssl
from test import NativeResourceTest
import threading
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse
import unittest

# Use a built-in Python HTTP server to test the awscrt's HTTP client
Expand Down Expand Up @@ -309,6 +313,47 @@ def test_proxy_http(self):
proxy_options = HttpProxyOptions(host_name=PROXY_HOST, port=PROXY_PORT)
self._test_get(secure=False, proxy_options=proxy_options)

def _new_h2_client_connection(self, url):
event_loop_group = EventLoopGroup()
host_resolver = DefaultHostResolver(event_loop_group)
bootstrap = ClientBootstrap(event_loop_group, host_resolver)

port = 443
scheme = 'https'
tls_ctx_options = TlsContextOptions()
tls_ctx = ClientTlsContext(tls_ctx_options)
tls_conn_opt = tls_ctx.new_connection_options()
tls_conn_opt.set_server_name(url.hostname)
tls_conn_opt.set_alpn_list(["h2"])

connection_future = HttpClientConnection.new(host_name=url.hostname,
port=port,
bootstrap=bootstrap,
tls_connection_options=tls_conn_opt)
return connection_future.result(self.timeout)

def test_h2_client(self):
url = urlparse("https://d1cz66xoahf9cl.cloudfront.net/http_test_doc.txt")
connection = self._new_h2_client_connection(url)
# check we set an h2 connection
self.assertEqual(connection.version, HttpVersion.Http2)

request = HttpRequest('GET', url.path)
request.headers.add('host', url.hostname)
response = Response()
stream = connection.request(request, response.on_response, response.on_body)
stream.activate()

# wait for stream to complete
stream_completion_result = stream.completion_future.result(self.timeout)

# check result
self.assertEqual(200, response.status_code)
self.assertEqual(200, stream_completion_result)
self.assertEqual(14428801, len(response.body))

self.assertEqual(None, connection.close().exception(self.timeout))


if __name__ == '__main__':
unittest.main()

0 comments on commit 7ed7639

Please sign in to comment.