1818#include <aws/io/logging.h>
1919#include <aws/io/stream.h>
2020
21+ #include <inttypes.h>
22+
2123#define ENCODER_LOGF (level , encoder , text , ...) \
2224 AWS_LOGF_##level(AWS_LS_HTTP_STREAM, "id=%p: " text, encoder->logging_id, __VA_ARGS__)
2325#define ENCODER_LOG (level , encoder , text ) ENCODER_LOGF(level, encoder, "%s", text)
2628 * Scan headers to detect errors and determine anything we'll need to know later (ex: total length).
2729 */
2830static int s_scan_outgoing_headers (
31+ struct aws_h1_encoder_message * encoder_message ,
2932 const struct aws_http_message * message ,
3033 size_t * out_header_lines_len ,
31- bool * out_has_connection_close_header ,
3234 bool body_headers_ignored ,
3335 bool body_headers_forbidden ) {
3436
3537 size_t total = 0 ;
36-
3738 bool has_body_stream = aws_http_message_get_body_stream (message );
3839 bool has_body_headers = false;
3940
40- bool has_connection_close_header = false;
41-
4241 const size_t num_headers = aws_http_message_get_header_count (message );
4342 for (size_t i = 0 ; i < num_headers ; ++ i ) {
4443 struct aws_http_header header ;
@@ -49,19 +48,22 @@ static int s_scan_outgoing_headers(
4948 case AWS_HTTP_HEADER_CONNECTION : {
5049 struct aws_byte_cursor trimmed_value = aws_strutil_trim_http_whitespace (header .value );
5150 if (aws_byte_cursor_eq_c_str (& trimmed_value , "close" )) {
52- has_connection_close_header = true;
51+ encoder_message -> has_connection_close_header = true;
5352 }
5453 } break ;
5554 case AWS_HTTP_HEADER_CONTENT_LENGTH : {
56- uint64_t content_length = 0 ;
57- aws_strutil_read_unsigned_num (aws_strutil_trim_http_whitespace (header .value ), & content_length );
58- if (content_length > 0 ) {
55+ struct aws_byte_cursor trimmed_value = aws_strutil_trim_http_whitespace (header .value );
56+ if (aws_strutil_read_unsigned_num (trimmed_value , & encoder_message -> content_length )) {
57+ AWS_LOGF_ERROR (AWS_LS_HTTP_STREAM , "id=static: Invalid Content-Length" );
58+ return aws_raise_error (AWS_ERROR_HTTP_INVALID_HEADER_VALUE );
59+ }
60+ if (encoder_message -> content_length > 0 ) {
5961 has_body_headers = true;
6062 }
6163 } break ;
6264 case AWS_HTTP_HEADER_TRANSFER_ENCODING :
63- has_body_headers = true ;
64- break ;
65+ AWS_LOGF_ERROR ( AWS_LS_HTTP_STREAM , "id=static: Sending of chunked messages not yet implemented" ) ;
66+ return aws_raise_error ( AWS_ERROR_UNIMPLEMENTED ) ;
6567 default :
6668 break ;
6769 }
@@ -80,23 +82,16 @@ static int s_scan_outgoing_headers(
8082 }
8183
8284 if (body_headers_ignored ) {
83- /* no body should follow, no matter what the headers are */
84- if (has_body_stream ) {
85- return aws_raise_error (AWS_ERROR_HTTP_INVALID_BODY_STREAM );
86- }
85+ /* Don't send body, no matter what the headers are */
8786 has_body_headers = false;
87+ encoder_message -> content_length = 0 ;
8888 }
8989
9090 if (has_body_headers && !has_body_stream ) {
9191 return aws_raise_error (AWS_ERROR_HTTP_MISSING_BODY_STREAM );
9292 }
9393
94- if (!has_body_headers && has_body_stream ) {
95- return aws_raise_error (AWS_ERROR_HTTP_MISSING_BODY_HEADERS );
96- }
97-
9894 * out_header_lines_len = total ;
99- * out_has_connection_close_header = has_connection_close_header ;
10095 return AWS_OP_SUCCESS ;
10196}
10297
@@ -151,11 +146,7 @@ int aws_h1_encoder_message_init_from_request(
151146
152147 size_t header_lines_len ;
153148 err = s_scan_outgoing_headers (
154- request ,
155- & header_lines_len ,
156- & message -> has_connection_close_header ,
157- false /*body_headers_ignored*/ ,
158- false /*body_headers_forbidden*/ );
149+ message , request , & header_lines_len , false /*body_headers_ignored*/ , false /*body_headers_forbidden*/ );
159150 if (err ) {
160151 goto error ;
161152 }
@@ -241,12 +232,7 @@ int aws_h1_encoder_message_init_from_response(
241232 */
242233 body_headers_ignored |= status_int == AWS_HTTP_STATUS_304_NOT_MODIFIED ;
243234 bool body_headers_forbidden = status_int == AWS_HTTP_STATUS_204_NO_CONTENT || status_int / 100 == 1 ;
244- err = s_scan_outgoing_headers (
245- response ,
246- & header_lines_len ,
247- & message -> has_connection_close_header ,
248- body_headers_ignored ,
249- body_headers_forbidden );
235+ err = s_scan_outgoing_headers (message , response , & header_lines_len , body_headers_ignored , body_headers_forbidden );
250236 if (err ) {
251237 goto error ;
252238 }
@@ -328,7 +314,7 @@ int aws_h1_encoder_start_message(
328314 encoder -> logging_id = log_as_stream ;
329315 encoder -> message = message ;
330316 encoder -> state = AWS_H1_ENCODER_STATE_HEAD ;
331- encoder -> outgoing_head_progress = 0 ;
317+ encoder -> progress_bytes = 0 ;
332318
333319 return AWS_OP_SUCCESS ;
334320}
@@ -354,34 +340,35 @@ int aws_h1_encoder_process(struct aws_h1_encoder *encoder, struct aws_byte_buf *
354340
355341 /* Copy data from outgoing_head_buf */
356342 struct aws_byte_buf * src = & encoder -> message -> outgoing_head_buf ;
357- size_t src_progress = encoder -> outgoing_head_progress ;
343+ size_t src_progress = ( size_t ) encoder -> progress_bytes ;
358344 size_t src_remaining = src -> len - src_progress ;
359345 size_t transferring = src_remaining < dst_available ? src_remaining : dst_available ;
360346
361347 bool success = aws_byte_buf_write (dst , src -> buffer + src_progress , transferring );
362348 (void )success ;
363349 AWS_ASSERT (success );
364350
365- encoder -> outgoing_head_progress += transferring ;
351+ encoder -> progress_bytes += transferring ;
366352
367353 ENCODER_LOGF (
368354 TRACE ,
369355 encoder ,
370- "Writing to message, outgoing head progress %zu /%zu." ,
371- encoder -> outgoing_head_progress ,
356+ "Writing to message, outgoing head progress %" PRIu64 " /%zu." ,
357+ encoder -> progress_bytes ,
372358 encoder -> message -> outgoing_head_buf .len );
373359
374- if (encoder -> outgoing_head_progress == src -> len ) {
360+ if (encoder -> progress_bytes == src -> len ) {
375361 /* Don't NEED to free this buffer now, but we don't need it anymore, so why not */
376362 aws_byte_buf_clean_up (& encoder -> message -> outgoing_head_buf );
377363
364+ encoder -> progress_bytes = 0 ;
378365 encoder -> state ++ ;
379366 }
380367 }
381368
382369 if (encoder -> state == AWS_H1_ENCODER_STATE_BODY ) {
383- if (!encoder -> message -> body ) {
384- ENCODER_LOG (TRACE , encoder , "No body to send. " )
370+ if (!encoder -> message -> body || ! encoder -> message -> content_length ) {
371+ ENCODER_LOG (TRACE , encoder , "Skipping body" )
385372 encoder -> state ++ ;
386373 } else {
387374 while (true) {
@@ -408,29 +395,51 @@ int aws_h1_encoder_process(struct aws_h1_encoder *encoder, struct aws_byte_buf *
408395 return AWS_OP_ERR ;
409396 }
410397
411- ENCODER_LOGF (TRACE , encoder , "Writing %zu body bytes to message" , amount_read );
412-
413- struct aws_stream_status status ;
414- err = aws_input_stream_get_status (encoder -> message -> body , & status );
415- if (err ) {
398+ if ((amount_read > encoder -> message -> content_length ) ||
399+ (encoder -> progress_bytes > encoder -> message -> content_length - amount_read )) {
416400 ENCODER_LOGF (
417- TRACE ,
401+ ERROR ,
418402 encoder ,
419- "Failed to query body stream status, error %d (%s)" ,
420- aws_last_error (),
421- aws_error_name (aws_last_error ()));
422-
423- return AWS_OP_ERR ;
403+ "Body stream has exceeded Content-Length: %" PRIu64 ,
404+ encoder -> message -> content_length );
405+ return aws_raise_error (AWS_ERROR_HTTP_OUTGOING_STREAM_LENGTH_INCORRECT );
424406 }
425- if (status .is_end_of_stream ) {
426- ENCODER_LOG (TRACE , encoder , "Done sending body." );
427407
408+ encoder -> progress_bytes += amount_read ;
409+
410+ ENCODER_LOGF (TRACE , encoder , "Writing %zu body bytes to message" , amount_read );
411+
412+ if (encoder -> progress_bytes == encoder -> message -> content_length ) {
413+ ENCODER_LOG (TRACE , encoder , "Done sending body." );
414+ encoder -> progress_bytes = 0 ;
428415 encoder -> state ++ ;
429416 break ;
430417 }
431418
432419 /* Return if user failed to write anything. Maybe their data isn't ready yet. */
433420 if (amount_read == 0 ) {
421+ /* Ensure we're not at end-of-stream too early */
422+ struct aws_stream_status status ;
423+ err = aws_input_stream_get_status (encoder -> message -> body , & status );
424+ if (err ) {
425+ ENCODER_LOGF (
426+ TRACE ,
427+ encoder ,
428+ "Failed to query body stream status, error %d (%s)" ,
429+ aws_last_error (),
430+ aws_error_name (aws_last_error ()));
431+
432+ return AWS_OP_ERR ;
433+ }
434+ if (status .is_end_of_stream ) {
435+ ENCODER_LOGF (
436+ ERROR ,
437+ encoder ,
438+ "Reached end of body stream before Content-Length: %" PRIu64 " sent" ,
439+ encoder -> message -> content_length );
440+ return aws_raise_error (AWS_ERROR_HTTP_OUTGOING_STREAM_LENGTH_INCORRECT );
441+ }
442+
434443 ENCODER_LOG (
435444 TRACE ,
436445 encoder ,
0 commit comments