22 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33 * SPDX-License-Identifier: Apache-2.0.
44 */
5+ #include <aws/common/error.h>
6+ #include <aws/io/future.h>
57#include <aws/http/private/h1_encoder.h>
8+ #include <aws/http/private/h1_connection.h>
69#include <aws/http/private/strutil.h>
710#include <aws/http/status_code.h>
811#include <aws/io/logging.h>
912#include <aws/io/stream.h>
13+ #include <aws/io/async_stream.h>
1014
1115#include <inttypes.h>
1216
@@ -249,6 +253,7 @@ int aws_h1_encoder_message_init_from_request(
249253 AWS_ZERO_STRUCT (* message );
250254
251255 message -> body = aws_input_stream_acquire (aws_http_message_get_body_stream (request ));
256+ message -> async_body = aws_async_input_stream_acquire (aws_http_message_get_async_body_stream (request ));
252257 message -> pending_chunk_list = pending_chunk_list ;
253258
254259 struct aws_byte_cursor method ;
@@ -693,6 +698,57 @@ static int s_switch_state(struct aws_h1_encoder *encoder, enum aws_h1_encoder_st
693698 return AWS_OP_SUCCESS ;
694699}
695700
701+ static void s_on_async_body_read_complete (void * user_data ) {
702+ struct aws_h1_encoder * encoder = user_data ;
703+
704+ struct aws_h1_connection * connection = encoder -> connection ;
705+
706+ int error = aws_future_bool_get_error (encoder -> pending_async_future );
707+ if (error ) {
708+ ENCODER_LOG (ERROR , encoder , "Encountered error after future was complete. Setting async_error, should be caught in the connection event loop." );
709+ encoder -> async_error = error ;
710+ }
711+
712+ bool eof = !error && aws_future_bool_get_result (encoder -> pending_async_future );
713+
714+ aws_future_bool_release (encoder -> pending_async_future );
715+ encoder -> pending_async_future = NULL ;
716+
717+ if (eof ) {
718+ s_switch_state (encoder , AWS_H1_ENCODER_STATE_DONE );
719+ } else {
720+ ENCODER_LOG (DEBUG , encoder , "Error occurred or buffer was full but eof not reached. We have to initiate a new encode request with a new buffer." );
721+ s_switch_state (encoder , AWS_H1_ENCODER_STATE_UNCHUNKED_BODY_STREAM );
722+ }
723+
724+ aws_h1_connection_try_write_outgoing_stream (connection );
725+ }
726+
727+ static int s_encode_stream_async (
728+ struct aws_h1_encoder * encoder ,
729+ struct aws_byte_buf * dst ,
730+ struct aws_async_input_stream * stream ) {
731+
732+ if (dst -> capacity == dst -> len ) {
733+ return AWS_OP_ERR ;
734+ }
735+
736+ ENCODER_LOG (TRACE , encoder , "Reading from async body stream." );
737+
738+ encoder -> pending_async_future = aws_async_input_stream_read_to_fill (stream , dst );
739+
740+ if (aws_future_bool_is_done (encoder -> pending_async_future )) {
741+ s_on_async_body_read_complete (encoder );
742+ return encoder -> async_error ;
743+ }
744+
745+ aws_future_bool_register_callback (encoder -> pending_async_future , s_on_async_body_read_complete , encoder );
746+
747+ s_switch_state (encoder , AWS_H1_ENCODER_STATE_ASYNC_WAITING );
748+
749+ return AWS_OP_SUCCESS ;
750+ }
751+
696752/* Initial state. Waits until a new message is set */
697753static int s_state_fn_init (struct aws_h1_encoder * encoder , struct aws_byte_buf * dst ) {
698754 (void )dst ;
@@ -720,7 +776,8 @@ static int s_state_fn_head(struct aws_h1_encoder *encoder, struct aws_byte_buf *
720776 aws_byte_buf_clean_up (& encoder -> message -> outgoing_head_buf );
721777
722778 /* Pick next state */
723- if (encoder -> message -> body && encoder -> message -> content_length ) {
779+ /* Experimentally supporting async streams for unchunked requests.*/
780+ if ((encoder -> message -> body || encoder -> message -> async_body ) && encoder -> message -> content_length ) {
724781 return s_switch_state (encoder , AWS_H1_ENCODER_STATE_UNCHUNKED_BODY_STREAM );
725782
726783 } else if (encoder -> message -> body && encoder -> message -> has_chunked_encoding_header ) {
@@ -736,6 +793,9 @@ static int s_state_fn_head(struct aws_h1_encoder *encoder, struct aws_byte_buf *
736793
737794/* Write out body with known Content-Length (not using chunked encoding). */
738795static int s_state_fn_unchunked_body_stream (struct aws_h1_encoder * encoder , struct aws_byte_buf * dst ) {
796+ if (encoder -> message -> async_body ) {
797+ return s_encode_stream_async (encoder , dst , encoder -> message -> async_body );
798+ }
739799 bool done ;
740800 if (s_encode_stream (encoder , dst , encoder -> message -> body , encoder -> message -> content_length , & done )) {
741801 return AWS_OP_ERR ;
@@ -750,6 +810,13 @@ static int s_state_fn_unchunked_body_stream(struct aws_h1_encoder *encoder, stru
750810 return s_switch_state (encoder , AWS_H1_ENCODER_STATE_DONE );
751811}
752812
813+ static int s_state_fn_async_waiting (struct aws_h1_encoder * encoder , struct aws_byte_buf * dst ) {
814+ (void ) dst ;
815+ ENCODER_LOG (ERROR , encoder , "This point should never be reached. We should come back to the encoder only after the state has changed from ASYNC WAITING" );
816+
817+ return AWS_OP_ERR ;
818+ }
819+
753820/* Write out body (of unknown Content-Length) using chunked encoding.
754821 * Each pass through this state writes out 1 chunk of body data (or nothing at all). */
755822static int s_state_fn_chunked_body_stream (struct aws_h1_encoder * encoder , struct aws_byte_buf * dst ) {
@@ -994,6 +1061,7 @@ static struct encoder_state_def s_encoder_states[] = {
9941061 [AWS_H1_ENCODER_STATE_INIT ] = {.fn = s_state_fn_init , .name = "INIT" },
9951062 [AWS_H1_ENCODER_STATE_HEAD ] = {.fn = s_state_fn_head , .name = "HEAD" },
9961063 [AWS_H1_ENCODER_STATE_UNCHUNKED_BODY_STREAM ] = {.fn = s_state_fn_unchunked_body_stream , .name = "BODY" },
1064+ [AWS_H1_ENCODER_STATE_ASYNC_WAITING ] = {.fn = s_state_fn_async_waiting , .name = "WAITING" },
9971065 [AWS_H1_ENCODER_STATE_CHUNKED_BODY_STREAM ] = {.fn = s_state_fn_chunked_body_stream , .name = "CHUNKED_BODY_STREAM" },
9981066 [AWS_H1_ENCODER_STATE_CHUNKED_BODY_STREAM_LAST_CHUNK ] =
9991067 {.fn = s_state_fn_chunked_body_stream_last_chunk , .name = "LAST_CHUNK" },
0 commit comments