@@ -461,10 +461,77 @@ static void s_cross_thread_work_task(struct aws_channel_task *channel_task, void
461461 }
462462}
463463
464+ static bool s_aws_http_stream_was_successful_connect (struct aws_h1_stream * stream ) {
465+ struct aws_http_stream * base = & stream -> base ;
466+ if (base -> request_method != AWS_HTTP_METHOD_CONNECT ) {
467+ return false;
468+ }
469+
470+ if (base -> client_data == NULL ) {
471+ return false;
472+ }
473+
474+ if (base -> client_data -> response_status != AWS_HTTP_STATUS_CODE_200_OK ) {
475+ return false;
476+ }
477+
478+ return true;
479+ }
480+
481+ /**
482+ * Validate and perform a protocol switch on a connection. Protocol switching essentially turns the connection's
483+ * handler into a dummy pass-through. It is valid to switch protocols to the same protocol resulting in a channel
484+ * that has a "dead" http handler in the middle of the channel (which negotiated the CONNECT through the proxy) and
485+ * a "live" handler on the end which takes the actual http requests. By doing this, we get the exact same
486+ * behavior whether we're transitioning to http or any other protocol: once the CONNECT succeeds
487+ * the first http handler is put in pass-through mode and a new protocol (which could be http) is tacked onto the end.
488+ */
489+ static int s_aws_http1_switch_protocols (struct aws_h1_connection * connection ) {
490+ AWS_FATAL_ASSERT (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
491+
492+ /* Switching protocols while there are multiple streams is too complex to deal with.
493+ * Ensure stream_list has exactly this 1 stream in it. */
494+ if (aws_linked_list_begin (& connection -> thread_data .stream_list ) !=
495+ aws_linked_list_rbegin (& connection -> thread_data .stream_list )) {
496+ AWS_LOGF_ERROR (
497+ AWS_LS_HTTP_CONNECTION ,
498+ "id=%p: Cannot switch protocols while further streams are pending, closing connection." ,
499+ (void * )& connection -> base );
500+
501+ return aws_raise_error (AWS_ERROR_INVALID_STATE );
502+ }
503+
504+ AWS_LOGF_TRACE (
505+ AWS_LS_HTTP_CONNECTION ,
506+ "id=%p: Connection has switched protocols, another channel handler must be installed to"
507+ " deal with further data." ,
508+ (void * )& connection -> base );
509+
510+ connection -> thread_data .has_switched_protocols = true;
511+ { /* BEGIN CRITICAL SECTION */
512+ aws_h1_connection_lock_synced_data (connection );
513+ connection -> synced_data .new_stream_error_code = AWS_ERROR_HTTP_SWITCHED_PROTOCOLS ;
514+ aws_h1_connection_unlock_synced_data (connection );
515+ } /* END CRITICAL SECTION */
516+
517+ return AWS_OP_SUCCESS ;
518+ }
519+
464520static void s_stream_complete (struct aws_h1_stream * stream , int error_code ) {
465521 struct aws_h1_connection * connection =
466522 AWS_CONTAINER_OF (stream -> base .owning_connection , struct aws_h1_connection , base );
467523
524+ /*
525+ * If this is the end of a successful CONNECT request, mark ourselves as pass-through since the proxy layer
526+ * will be tacking on a new http handler (and possibly a tls handler in-between).
527+ */
528+ if (error_code == AWS_ERROR_SUCCESS && s_aws_http_stream_was_successful_connect (stream )) {
529+ if (s_aws_http1_switch_protocols (connection )) {
530+ error_code = AWS_ERROR_HTTP_PROTOCOL_SWITCH_FAILURE ;
531+ s_shutdown_due_to_error (connection , error_code );
532+ }
533+ }
534+
468535 /* Remove stream from list. */
469536 aws_linked_list_remove (& stream -> node );
470537
@@ -1034,31 +1101,9 @@ static int s_mark_head_done(struct aws_h1_stream *incoming_stream) {
10341101 /* Only clients can receive informational headers.
10351102 * Check whether we're switching protocols */
10361103 if (incoming_stream -> base .client_data -> response_status == AWS_HTTP_STATUS_CODE_101_SWITCHING_PROTOCOLS ) {
1037-
1038- /* Switching protocols while there are multiple streams is too complex to deal with.
1039- * Ensure stream_list has exactly this 1 stream in it. */
1040- if (aws_linked_list_begin (& connection -> thread_data .stream_list ) !=
1041- aws_linked_list_rbegin (& connection -> thread_data .stream_list )) {
1042- AWS_LOGF_ERROR (
1043- AWS_LS_HTTP_CONNECTION ,
1044- "id=%p: Cannot switch protocols while further streams are pending, closing connection." ,
1045- (void * )& connection -> base );
1046-
1047- return aws_raise_error (AWS_ERROR_INVALID_STATE );
1104+ if (s_aws_http1_switch_protocols (connection )) {
1105+ return AWS_OP_ERR ;
10481106 }
1049-
1050- AWS_LOGF_TRACE (
1051- AWS_LS_HTTP_CONNECTION ,
1052- "id=%p: Connection has switched protocols, another channel handler must be installed to"
1053- " deal with further data." ,
1054- (void * )& connection -> base );
1055-
1056- connection -> thread_data .has_switched_protocols = true;
1057- { /* BEGIN CRITICAL SECTION */
1058- aws_h1_connection_lock_synced_data (connection );
1059- connection -> synced_data .new_stream_error_code = AWS_ERROR_HTTP_SWITCHED_PROTOCOLS ;
1060- aws_h1_connection_unlock_synced_data (connection );
1061- } /* END CRITICAL SECTION */
10621107 }
10631108 }
10641109
0 commit comments