Skip to content

Commit 4fff6a8

Browse files
authored
Integration test (awslabs#235)
- elasticurl uses "h2;http/1.1" alpn string by default - --http2 and --http1_1 flags force "h2" or "http/1.1" and error if they don't get it - add h2 tests to integration-testing/http_client_test.py - add add_net_test_case(tls_download_medium_file_http2) Co-authored-by: Dengke Tang <[email protected]>
1 parent c604f6b commit 4fff6a8

File tree

4 files changed

+80
-22
lines changed

4 files changed

+80
-22
lines changed

bin/elasticurl/main.c

+21-3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ struct elasticurl_ctx {
7272
FILE *output;
7373
const char *trace_file;
7474
enum aws_log_level log_level;
75+
enum aws_http_version required_http_version;
7576
bool exchange_completed;
7677
bool bootstrap_shutdown_completed;
7778
};
@@ -104,7 +105,8 @@ static void s_usage(int exit_code) {
104105
fprintf(stderr, " -t, --trace FILE: dumps logs to FILE instead of stderr.\n");
105106
fprintf(stderr, " -v, --verbose: ERROR|INFO|DEBUG|TRACE: log level to configure. Default is none.\n");
106107
fprintf(stderr, " --version: print the version of elasticurl.\n");
107-
fprintf(stderr, " --http2: try to use HTTP/2");
108+
fprintf(stderr, " --http2: HTTP/2 connection required");
109+
fprintf(stderr, " --http1_1: HTTP/1.1 connection required");
108110
fprintf(stderr, " -h, --help\n");
109111
fprintf(stderr, " Display this message and quit.\n");
110112
exit(exit_code);
@@ -133,6 +135,7 @@ static struct aws_cli_option s_long_options[] = {
133135
{"verbose", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'v'},
134136
{"version", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'V'},
135137
{"http2", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'w'},
138+
{"http1_1", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'W'},
136139
{"help", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'h'},
137140
/* Per getopt(3) the last element of the array has to be filled with all zeros */
138141
{NULL, AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 0},
@@ -169,7 +172,7 @@ static void s_parse_options(int argc, char **argv, struct elasticurl_ctx *ctx) {
169172
while (true) {
170173
int option_index = 0;
171174
int c =
172-
aws_cli_getopt_long(argc, argv, "a:b:c:e:f:H:d:g:j:l:m:M:GPHiko:t:v:Vwh", s_long_options, &option_index);
175+
aws_cli_getopt_long(argc, argv, "a:b:c:e:f:H:d:g:j:l:m:M:GPHiko:t:v:VwWh", s_long_options, &option_index);
173176
if (c == -1) {
174177
break;
175178
}
@@ -277,6 +280,11 @@ static void s_parse_options(int argc, char **argv, struct elasticurl_ctx *ctx) {
277280
exit(0);
278281
case 'w':
279282
ctx->alpn = "h2";
283+
ctx->required_http_version = AWS_HTTP_VERSION_2;
284+
break;
285+
case 'W':
286+
ctx->alpn = "http/1.1";
287+
ctx->required_http_version = AWS_HTTP_VERSION_1_1;
280288
break;
281289
case 'h':
282290
s_usage(0);
@@ -453,6 +461,12 @@ static void s_on_signing_complete(struct aws_http_message *request, int error_co
453461

454462
static void s_on_client_connection_setup(struct aws_http_connection *connection, int error_code, void *user_data) {
455463
struct elasticurl_ctx *app_ctx = user_data;
464+
if (app_ctx->required_http_version) {
465+
if (aws_http_connection_get_version(connection) != app_ctx->required_http_version) {
466+
fprintf(stderr, "Error. The requested http version, %s, is not supported by the peer.", app_ctx->alpn);
467+
exit(1);
468+
}
469+
}
456470

457471
if (error_code) {
458472
fprintf(stderr, "Connection failed with error %s\n", aws_error_debug_str(error_code));
@@ -560,7 +574,7 @@ int main(int argc, char **argv) {
560574
app_ctx.connect_timeout = 3000;
561575
app_ctx.output = stdout;
562576
app_ctx.verb = "GET";
563-
app_ctx.alpn = "http/1.1";
577+
app_ctx.alpn = "h2;http/1.1";
564578
aws_mutex_init(&app_ctx.mutex);
565579
aws_hash_table_init(
566580
&app_ctx.signing_context,
@@ -675,6 +689,10 @@ int main(int argc, char **argv) {
675689
port = app_ctx.uri.port;
676690
}
677691
} else {
692+
if (app_ctx.required_http_version == AWS_HTTP_VERSION_2) {
693+
fprintf(stderr, "Error, we don't support h2c, please use TLS for HTTP2 connection");
694+
exit(1);
695+
}
678696
port = 80;
679697
if (app_ctx.uri.port) {
680698
port = app_ctx.uri.port;

integration-testing/http_client_test.py

+27-10
Original file line numberDiff line numberDiff line change
@@ -63,24 +63,41 @@ def compare_files(filename_expected, filename_other):
6363
print("filecmp says these files differ, but they are identical. what the heck.")
6464

6565
class SimpleTests(unittest.TestCase):
66-
#make a simple GET request and make sure it succeeds
67-
def test_simple_get(self):
68-
simple_get_args = elasticurl_cmd_prefix + ['-v', 'TRACE', 'http://example.com']
66+
def test_simple_get_h1(self):
67+
"""make a simple GET request via HTTP/1.1 and make sure it succeeds"""
68+
simple_get_args = elasticurl_cmd_prefix + ['-v', 'TRACE', '--http1_1', 'http://example.com']
6969
run_command(simple_get_args)
7070

71-
#make a simple POST request to make sure sending data succeeds
72-
def test_simple_post(self):
73-
simple_post_args = elasticurl_cmd_prefix + ['-v', 'TRACE', '-P', '-H', 'content-type: application/json', '-i', '-d', '\"{\'test\':\'testval\'}\"', 'http://httpbin.org/post']
71+
def test_simple_post_h1(self):
72+
"""make a simple POST request via HTTP/1.1 to make sure sending data succeeds"""
73+
simple_post_args = elasticurl_cmd_prefix + ['-v', 'TRACE', '--http1_1', '-P', '-H', 'content-type: application/json', '-i', '-d', '\"{\'test\':\'testval\'}\"', 'http://httpbin.org/post']
7474
run_command(simple_post_args)
7575

76-
#download a large file and compare the results with something we assume works (e.g. urllib)
77-
def test_simple_download(self):
78-
elasticurl_download_args = elasticurl_cmd_prefix + ['-v', 'TRACE', '-o', 'elastigirl.png', 'https://s3.amazonaws.com/code-sharing-aws-crt/elastigirl.png']
76+
def test_simple_download_h1(self):
77+
"""download a large file via HTTP/1.1 and compare the results with something we assume works (e.g. urllib)"""
78+
elasticurl_download_args = elasticurl_cmd_prefix + ['-v', 'TRACE', '--http1_1', '-o', 'elastigirl.png', 'https://s3.amazonaws.com/code-sharing-aws-crt/elastigirl.png']
7979
run_command(elasticurl_download_args)
80-
8180
urllib.request.urlretrieve('https://s3.amazonaws.com/code-sharing-aws-crt/elastigirl.png', 'elastigirl_expected.png')
8281

8382
compare_files('elastigirl_expected.png', 'elastigirl.png')
8483

84+
def test_simple_get_h2(self):
85+
"""make a simple GET request via HTTP2 and make sure it succeeds"""
86+
simple_get_args = elasticurl_cmd_prefix + ['-v', 'TRACE', '--http2', 'https://example.com']
87+
run_command(simple_get_args)
88+
89+
def test_simple_post_h2(self):
90+
"""make a simple POST request via HTTP2 to make sure sending data succeeds"""
91+
simple_post_args = elasticurl_cmd_prefix + ['-v', 'TRACE', '--http2', '-P', '-H', 'content-type: application/json', '-i', '-d', '\"{\'test\':\'testval\'}\"', 'https://httpbin.org/post']
92+
run_command(simple_post_args)
93+
94+
def test_simple_download_h2(self):
95+
"""download a large file via HTTP2 and compare the results with something we assume works (e.g. urllib)"""
96+
elasticurl_download_args = elasticurl_cmd_prefix + ['-v', 'TRACE', '--http2', '-o', 'elastigirl_h2.png', 'https://d1cz66xoahf9cl.cloudfront.net/elastigirl.png']
97+
run_command(elasticurl_download_args)
98+
urllib.request.urlretrieve('https://d1cz66xoahf9cl.cloudfront.net/elastigirl.png', 'elastigirl_expected.png')
99+
100+
compare_files('elastigirl_expected.png', 'elastigirl_h2.png')
101+
85102
if __name__ == '__main__':
86103
unittest.main(verbosity=2)

tests/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ add_test_case(strutil_trim_http_whitespace)
109109
add_test_case(strutil_is_http_token)
110110
add_test_case(strutil_is_lowercase_http_token)
111111

112-
add_net_test_case(tls_download_medium_file)
112+
add_net_test_case(tls_download_medium_file_h1)
113+
add_net_test_case(tls_download_medium_file_h2)
113114

114115
add_test_case(websocket_decoder_sanity_check)
115116
add_test_case(websocket_decoder_simplest_frame)

tests/test_tls.c

+30-8
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,20 @@ static void s_on_stream_complete(struct aws_http_stream *stream, int error_code,
103103
struct test_ctx *test = user_data;
104104
test->wait_result = error_code;
105105
test->stream_complete = true;
106+
aws_condition_variable_notify_one(&test->wait_cvar);
106107
}
107108

108109
static bool s_stream_wait_pred(void *user_data) {
109110
struct test_ctx *test = user_data;
110111
return test->wait_result || test->stream_complete;
111112
}
112113

113-
static int s_test_tls_download_medium_file(struct aws_allocator *allocator, void *ctx) {
114-
(void)ctx;
114+
static int s_test_tls_download_medium_file_general(
115+
struct aws_allocator *allocator,
116+
struct aws_byte_cursor url,
117+
bool h2_required) {
115118

116119
aws_http_library_init(allocator);
117-
118-
struct aws_byte_cursor url =
119-
aws_byte_cursor_from_c_str("https://aws-crt-test-stuff.s3.amazonaws.com/http_test_doc.txt");
120120
struct aws_uri uri;
121121
aws_uri_init_parse(&uri, allocator, &url);
122122

@@ -148,6 +148,8 @@ static int s_test_tls_download_medium_file(struct aws_allocator *allocator, void
148148
ASSERT_NOT_NULL(test.client_bootstrap = aws_client_bootstrap_new(test.alloc, &bootstrap_options));
149149
struct aws_tls_ctx_options tls_ctx_options;
150150
aws_tls_ctx_options_init_default_client(&tls_ctx_options, allocator);
151+
char *apln = h2_required ? "h2" : "http/1.1";
152+
aws_tls_ctx_options_set_alpn_list(&tls_ctx_options, apln);
151153
ASSERT_NOT_NULL(test.tls_ctx = aws_tls_client_ctx_new(allocator, &tls_ctx_options));
152154
struct aws_tls_connection_options tls_connection_options;
153155
aws_tls_connection_options_init_from_ctx(&tls_connection_options, test.tls_ctx);
@@ -168,6 +170,11 @@ static int s_test_tls_download_medium_file(struct aws_allocator *allocator, void
168170
ASSERT_SUCCESS(s_test_wait(&test, s_test_connection_setup_pred));
169171
ASSERT_INT_EQUALS(0, test.wait_result);
170172
ASSERT_NOT_NULL(test.client_connection);
173+
if (h2_required) {
174+
ASSERT_INT_EQUALS(aws_http_connection_get_version(test.client_connection), AWS_HTTP_VERSION_2);
175+
} else {
176+
ASSERT_INT_EQUALS(aws_http_connection_get_version(test.client_connection), AWS_HTTP_VERSION_1_1);
177+
}
171178

172179
struct aws_http_message *request = aws_http_message_new_request(allocator);
173180
ASSERT_NOT_NULL(request);
@@ -217,9 +224,24 @@ static int s_test_tls_download_medium_file(struct aws_allocator *allocator, void
217224
aws_mutex_clean_up(&test.wait_lock);
218225
aws_condition_variable_clean_up(&test.wait_cvar);
219226
aws_uri_clean_up(&uri);
220-
221227
aws_http_library_clean_up();
228+
return AWS_OP_SUCCESS;
229+
}
222230

223-
return 0;
231+
static int s_test_tls_download_medium_file_h1(struct aws_allocator *allocator, void *ctx) {
232+
(void)ctx;
233+
struct aws_byte_cursor url =
234+
aws_byte_cursor_from_c_str("https://aws-crt-test-stuff.s3.amazonaws.com/http_test_doc.txt");
235+
ASSERT_SUCCESS(s_test_tls_download_medium_file_general(allocator, url, false /*h2_required*/));
236+
return AWS_OP_SUCCESS;
237+
}
238+
AWS_TEST_CASE(tls_download_medium_file_h1, s_test_tls_download_medium_file_h1);
239+
240+
static int s_tls_download_medium_file_h2(struct aws_allocator *allocator, void *ctx) {
241+
(void)ctx;
242+
/* The cloudfront domain for aws-crt-test-stuff */
243+
struct aws_byte_cursor url = aws_byte_cursor_from_c_str("https://d1cz66xoahf9cl.cloudfront.net/http_test_doc.txt");
244+
ASSERT_SUCCESS(s_test_tls_download_medium_file_general(allocator, url, true /*h2_required*/));
245+
return AWS_OP_SUCCESS;
224246
}
225-
AWS_TEST_CASE(tls_download_medium_file, s_test_tls_download_medium_file);
247+
AWS_TEST_CASE(tls_download_medium_file_h2, s_tls_download_medium_file_h2);

0 commit comments

Comments
 (0)