@@ -31,6 +31,15 @@ static struct aws_http_connection_system_vtable s_default_system_vtable = {
3131
3232static const struct aws_http_connection_system_vtable * s_system_vtable_ptr = & s_default_system_vtable ;
3333
34+ void aws_http_client_bootstrap_destroy (struct aws_http_client_bootstrap * bootstrap ) {
35+ /* During allocating, the underlying stuctures should be allocated with the bootstrap by aws_mem_acquire_many. Thus,
36+ * we only need to clean up the first pointer which is the bootstrap */
37+ if (bootstrap -> alpn_string_map ) {
38+ aws_hash_table_clean_up (bootstrap -> alpn_string_map );
39+ }
40+ aws_mem_release (bootstrap -> alloc , bootstrap );
41+ }
42+
3443void aws_http_connection_set_system_vtable (const struct aws_http_connection_system_vtable * system_vtable ) {
3544 s_system_vtable_ptr = system_vtable ;
3645}
@@ -78,6 +87,7 @@ struct aws_http_connection *aws_http_connection_new_channel_handler(
7887 bool manual_window_management ,
7988 bool prior_knowledge_http2 ,
8089 size_t initial_window_size ,
90+ const struct aws_hash_table * alpn_string_map ,
8191 const struct aws_http1_connection_options * http1_options ,
8292 const struct aws_http2_connection_options * http2_options ) {
8393
@@ -124,7 +134,32 @@ struct aws_http_connection *aws_http_connection_new_channel_handler(
124134 struct aws_channel_handler * tls_handler = tls_slot -> handler ;
125135 struct aws_byte_buf protocol = aws_tls_handler_protocol (tls_handler );
126136 if (protocol .len ) {
127- if (aws_string_eq_byte_buf (s_alpn_protocol_http_1_1 , & protocol )) {
137+ bool customized = false;
138+ if (alpn_string_map ) {
139+ customized = true;
140+ struct aws_string * negotiated_result = aws_string_new_from_buf (alloc , & protocol );
141+ struct aws_hash_element * found = NULL ;
142+ aws_hash_table_find (alpn_string_map , (void * )negotiated_result , & found );
143+ if (found ) {
144+ version = (enum aws_http_version )(size_t )found -> value ;
145+ AWS_LOGF_DEBUG (
146+ AWS_LS_HTTP_CONNECTION ,
147+ "static: Customized ALPN protocol " PRInSTR " used. " PRInSTR " client connection established." ,
148+ AWS_BYTE_BUF_PRI (protocol ),
149+ AWS_BYTE_CURSOR_PRI (aws_http_version_to_str (version )));
150+ } else {
151+ AWS_LOGF_ERROR (
152+ AWS_LS_HTTP_CONNECTION ,
153+ "static: Customized ALPN protocol " PRInSTR
154+ " used. However the it's not found in the ALPN map provided." ,
155+ AWS_BYTE_BUF_PRI (protocol ));
156+ version = AWS_HTTP_VERSION_UNKNOWN ;
157+ }
158+ aws_string_destroy (negotiated_result );
159+ }
160+ if (customized ) {
161+ /* Do nothing */
162+ } else if (aws_string_eq_byte_buf (s_alpn_protocol_http_1_1 , & protocol )) {
128163 version = AWS_HTTP_VERSION_1_1 ;
129164 } else if (aws_string_eq_byte_buf (s_alpn_protocol_http_2 , & protocol )) {
130165 version = AWS_HTTP_VERSION_2 ;
@@ -350,6 +385,29 @@ struct aws_channel *aws_http_connection_get_channel(struct aws_http_connection *
350385 return connection -> channel_slot -> channel ;
351386}
352387
388+ int aws_http_alpn_map_init (struct aws_allocator * allocator , struct aws_hash_table * map ) {
389+ AWS_ASSERT (allocator );
390+ AWS_ASSERT (map );
391+ int result = aws_hash_table_init (
392+ map ,
393+ allocator ,
394+ 5 /* initial size */ ,
395+ aws_hash_string ,
396+ aws_hash_callback_string_eq ,
397+ aws_hash_callback_string_destroy ,
398+ NULL );
399+ if (result ) {
400+ /* OOM will crash */
401+ int error_code = aws_last_error ();
402+ AWS_LOGF_ERROR (
403+ AWS_LS_HTTP_CONNECTION ,
404+ "Failed to initialize ALPN map with error code %d (%s)" ,
405+ error_code ,
406+ aws_error_name (error_code ));
407+ }
408+ return result ;
409+ }
410+
353411void aws_http_connection_acquire (struct aws_http_connection * connection ) {
354412 AWS_ASSERT (connection );
355413 aws_atomic_fetch_add (& connection -> refcount , 1 );
@@ -417,6 +475,7 @@ static void s_server_bootstrap_on_accept_channel_setup(
417475 server -> manual_window_management ,
418476 false, /* prior_knowledge_http2 */
419477 server -> initial_window_size ,
478+ NULL , /* alpn_string_map */
420479 & http1_options ,
421480 & http2_options );
422481 if (!connection ) {
@@ -735,7 +794,7 @@ static void s_client_bootstrap_on_channel_setup(
735794 http_bootstrap -> on_setup (NULL , error_code , http_bootstrap -> user_data );
736795
737796 /* Clean up the http_bootstrap, it has no more work to do. */
738- aws_mem_release ( http_bootstrap -> alloc , http_bootstrap );
797+ aws_http_client_bootstrap_destroy ( http_bootstrap );
739798 return ;
740799 }
741800
@@ -749,6 +808,7 @@ static void s_client_bootstrap_on_channel_setup(
749808 http_bootstrap -> manual_window_management ,
750809 http_bootstrap -> prior_knowledge_http2 ,
751810 http_bootstrap -> initial_window_size ,
811+ http_bootstrap -> alpn_string_map ,
752812 & http_bootstrap -> http1_options ,
753813 & http_bootstrap -> http2_options );
754814 if (!http_bootstrap -> connection ) {
@@ -840,7 +900,7 @@ static void s_client_bootstrap_on_channel_shutdown(
840900 }
841901
842902 /* Clean up bootstrapper */
843- aws_mem_release ( http_bootstrap -> alloc , http_bootstrap );
903+ aws_http_client_bootstrap_destroy ( http_bootstrap );
844904}
845905
846906static int s_validate_http_client_connection_options (const struct aws_http_client_connection_options * options ) {
@@ -885,6 +945,61 @@ static int s_validate_http_client_connection_options(const struct aws_http_clien
885945 return AWS_OP_SUCCESS ;
886946}
887947
948+ struct s_copy_alpn_string_map_context {
949+ struct aws_hash_table * map ;
950+ struct aws_allocator * allocator ;
951+ };
952+
953+ /* put every item into the source to make a deep copy of the map */
954+ static int s_copy_alpn_string_map (void * context , struct aws_hash_element * item ) {
955+ struct s_copy_alpn_string_map_context * func_context = context ;
956+ struct aws_hash_table * dest = func_context -> map ;
957+ /* make a deep copy of the string and hash map will own the copy */
958+ struct aws_string * key_copy = aws_string_new_from_string (func_context -> allocator , item -> key );
959+ int was_created ;
960+ if (aws_hash_table_put (dest , key_copy , item -> value , & was_created )) {
961+ int error_code = aws_last_error ();
962+ AWS_LOGF_ERROR (
963+ AWS_LS_HTTP_CONNECTION ,
964+ "Failed to copy ALPN map with error code %d (%s)" ,
965+ error_code ,
966+ aws_error_name (error_code ));
967+ /* failed to put into the table, we need to clean up the copy ourselves */
968+ aws_string_destroy (key_copy );
969+ /* return error to stop iteration */
970+ return AWS_COMMON_HASH_TABLE_ITER_ERROR ;
971+ }
972+ if (!was_created ) {
973+ /* no new entry created, clean up the copy ourselves */
974+ aws_string_destroy (key_copy );
975+ }
976+ return AWS_COMMON_HASH_TABLE_ITER_CONTINUE ;
977+ }
978+
979+ int aws_http_alpn_map_init_copy (
980+ struct aws_allocator * allocator ,
981+ struct aws_hash_table * dest ,
982+ struct aws_hash_table * src ) {
983+ if (aws_http_alpn_map_init (allocator , dest )) {
984+ return AWS_OP_ERR ;
985+ }
986+ struct s_copy_alpn_string_map_context context ;
987+ context .allocator = allocator ;
988+ context .map = dest ;
989+ /* make a deep copy of the map */
990+ if (aws_hash_table_foreach (src , s_copy_alpn_string_map , & context )) {
991+ int error_code = aws_last_error ();
992+ AWS_LOGF_ERROR (
993+ AWS_LS_HTTP_CONNECTION ,
994+ "Failed to copy ALPN map with error code %d (%s)" ,
995+ error_code ,
996+ aws_error_name (error_code ));
997+ aws_hash_table_clean_up (dest );
998+ return AWS_OP_ERR ;
999+ }
1000+ return AWS_OP_SUCCESS ;
1001+ }
1002+
8881003int aws_http_client_connect_internal (
8891004 const struct aws_http_client_connection_options * orig_options ,
8901005 aws_http_proxy_request_transform_fn * proxy_request_transform ) {
@@ -899,6 +1014,10 @@ int aws_http_client_connect_internal(
8991014
9001015 /* make copy of options, and add defaults for missing optional structs */
9011016 struct aws_http_client_connection_options options = * orig_options ;
1017+ if (options .prior_knowledge_http2 && options .tls_options ) {
1018+ AWS_LOGF_ERROR (AWS_LS_HTTP_CONNECTION , "static: HTTP/2 prior knowledge only works with cleartext TCP." );
1019+ return aws_raise_error (AWS_ERROR_INVALID_ARGUMENT );
1020+ }
9021021
9031022 struct aws_http1_connection_options default_http1_options ;
9041023 AWS_ZERO_STRUCT (default_http1_options );
@@ -926,13 +1045,16 @@ int aws_http_client_connect_internal(
9261045 }
9271046
9281047 struct aws_http2_setting * setting_array = NULL ;
1048+ struct aws_hash_table * alpn_string_map = NULL ;
9291049 if (!aws_mem_acquire_many (
9301050 options .allocator ,
931- 2 ,
1051+ 3 ,
9321052 & http_bootstrap ,
9331053 sizeof (struct aws_http_client_bootstrap ),
9341054 & setting_array ,
935- options .http2_options -> num_initial_settings * sizeof (struct aws_http2_setting ))) {
1055+ options .http2_options -> num_initial_settings * sizeof (struct aws_http2_setting ),
1056+ & alpn_string_map ,
1057+ sizeof (struct aws_hash_table ))) {
9361058 goto error ;
9371059 }
9381060
@@ -959,6 +1081,13 @@ int aws_http_client_connect_internal(
9591081 http_bootstrap -> http2_options .initial_settings_array = setting_array ;
9601082 }
9611083
1084+ if (options .alpn_string_map ) {
1085+ if (aws_http_alpn_map_init_copy (options .allocator , alpn_string_map , options .alpn_string_map )) {
1086+ goto error ;
1087+ }
1088+ http_bootstrap -> alpn_string_map = alpn_string_map ;
1089+ }
1090+
9621091 if (options .monitoring_options ) {
9631092 http_bootstrap -> monitoring_options = * options .monitoring_options ;
9641093 }
@@ -998,7 +1127,7 @@ int aws_http_client_connect_internal(
9981127
9991128error :
10001129 if (http_bootstrap ) {
1001- aws_mem_release ( http_bootstrap -> alloc , http_bootstrap );
1130+ aws_http_client_bootstrap_destroy ( http_bootstrap );
10021131 }
10031132
10041133 if (host_name ) {
@@ -1070,6 +1199,5 @@ uint32_t aws_http_connection_get_next_stream_id(struct aws_http_connection *conn
10701199 } else {
10711200 connection -> next_stream_id += 2 ;
10721201 }
1073-
10741202 return next_id ;
10751203}
0 commit comments