@@ -156,20 +156,84 @@ static int s_scan_outgoing_headers(
156156 return AWS_OP_SUCCESS ;
157157}
158158
159+ static int s_scan_outgoing_trailer (const struct aws_http_headers * headers , size_t * out_size ) {
160+ const size_t num_headers = aws_http_headers_count (headers );
161+ size_t total = 0 ;
162+ for (size_t i = 0 ; i < num_headers ; i ++ ) {
163+ struct aws_http_header header ;
164+ aws_http_headers_get_index (headers , i , & header );
165+ /* Validate header field-name (RFC-7230 3.2): field-name = token */
166+ if (!aws_strutil_is_http_token (header .name )) {
167+ AWS_LOGF_ERROR (AWS_LS_HTTP_STREAM , "id=static: Header name is invalid" );
168+ return aws_raise_error (AWS_ERROR_HTTP_INVALID_HEADER_NAME );
169+ }
170+
171+ /* Validate header field-value.
172+ * The value itself isn't supposed to have whitespace on either side,
173+ * but we'll trim it off before validation so we don't start needlessly
174+ * failing requests that used to work before we added validation.
175+ * This should be OK because field-value can be sent with any amount
176+ * of whitespace around it, which the other side will just ignore (RFC-7230 3.2):
177+ * header-field = field-name ":" OWS field-value OWS */
178+ struct aws_byte_cursor field_value = aws_strutil_trim_http_whitespace (header .value );
179+ if (!aws_strutil_is_http_field_value (field_value )) {
180+ AWS_LOGF_ERROR (
181+ AWS_LS_HTTP_STREAM ,
182+ "id=static: Header '" PRInSTR "' has invalid value" ,
183+ AWS_BYTE_CURSOR_PRI (header .name ));
184+ return aws_raise_error (AWS_ERROR_HTTP_INVALID_HEADER_VALUE );
185+ }
186+
187+ enum aws_http_header_name name_enum = aws_http_str_to_header_name (header .name );
188+ if (name_enum == AWS_HTTP_HEADER_TRANSFER_ENCODING || name_enum == AWS_HTTP_HEADER_CONTENT_LENGTH ||
189+ name_enum == AWS_HTTP_HEADER_HOST || name_enum == AWS_HTTP_HEADER_EXPECT ||
190+ name_enum == AWS_HTTP_HEADER_CACHE_CONTROL || name_enum == AWS_HTTP_HEADER_MAX_FORWARDS ||
191+ name_enum == AWS_HTTP_HEADER_PRAGMA || name_enum == AWS_HTTP_HEADER_RANGE ||
192+ name_enum == AWS_HTTP_HEADER_TE || name_enum == AWS_HTTP_HEADER_CONTENT_ENCODING ||
193+ name_enum == AWS_HTTP_HEADER_CONTENT_TYPE || name_enum == AWS_HTTP_HEADER_CONTENT_RANGE ||
194+ name_enum == AWS_HTTP_HEADER_TRAILER || name_enum == AWS_HTTP_HEADER_WWW_AUTHENTICATE ||
195+ name_enum == AWS_HTTP_HEADER_AUTHORIZATION || name_enum == AWS_HTTP_HEADER_PROXY_AUTHENTICATE ||
196+ name_enum == AWS_HTTP_HEADER_PROXY_AUTHORIZATION || name_enum == AWS_HTTP_HEADER_SET_COOKIE ||
197+ name_enum == AWS_HTTP_HEADER_COOKIE || name_enum == AWS_HTTP_HEADER_AGE ||
198+ name_enum == AWS_HTTP_HEADER_EXPIRES || name_enum == AWS_HTTP_HEADER_DATE ||
199+ name_enum == AWS_HTTP_HEADER_LOCATION || name_enum == AWS_HTTP_HEADER_RETRY_AFTER ||
200+ name_enum == AWS_HTTP_HEADER_VARY || name_enum == AWS_HTTP_HEADER_WARNING ) {
201+ AWS_LOGF_ERROR (
202+ AWS_LS_HTTP_STREAM ,
203+ "id=static: Trailing Header '" PRInSTR "' has invalid value" ,
204+ AWS_BYTE_CURSOR_PRI (header .name ));
205+ return aws_raise_error (AWS_ERROR_HTTP_INVALID_HEADER_FIELD );
206+ }
207+
208+ int err = 0 ;
209+ err |= aws_add_size_checked (header .name .len , total , & total );
210+ err |= aws_add_size_checked (header .value .len , total , & total );
211+ err |= aws_add_size_checked (4 , total , & total ); /* ": " + "\r\n" */
212+ if (err ) {
213+ return AWS_OP_ERR ;
214+ }
215+ }
216+ if (aws_add_size_checked (2 , total , & total )) { /* "\r\n" */
217+ return AWS_OP_ERR ;
218+ }
219+ * out_size = total ;
220+ return AWS_OP_SUCCESS ;
221+ }
222+
159223static bool s_write_crlf (struct aws_byte_buf * dst ) {
160224 AWS_PRECONDITION (aws_byte_buf_is_valid (dst ));
161225 struct aws_byte_cursor crlf_cursor = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL ("\r\n" );
162226 return aws_byte_buf_write_from_whole_cursor (dst , crlf_cursor );
163227}
164228
165- static void s_write_headers (struct aws_byte_buf * dst , const struct aws_http_message * message ) {
229+ static void s_write_headers (struct aws_byte_buf * dst , const struct aws_http_headers * headers ) {
166230
167- const size_t num_headers = aws_http_message_get_header_count ( message );
231+ const size_t num_headers = aws_http_headers_count ( headers );
168232
169233 bool wrote_all = true;
170234 for (size_t i = 0 ; i < num_headers ; ++ i ) {
171235 struct aws_http_header header ;
172- aws_http_message_get_header ( message , & header , i );
236+ aws_http_headers_get_index ( headers , i , & header );
173237
174238 /* header-line: "{name}: {value}\r\n" */
175239 wrote_all &= aws_byte_buf_write_from_whole_cursor (dst , header .name );
@@ -264,7 +328,7 @@ int aws_h1_encoder_message_init_from_request(
264328 wrote_all &= aws_byte_buf_write_from_whole_cursor (& message -> outgoing_head_buf , version );
265329 wrote_all &= s_write_crlf (& message -> outgoing_head_buf );
266330
267- s_write_headers (& message -> outgoing_head_buf , request );
331+ s_write_headers (& message -> outgoing_head_buf , aws_http_message_get_const_headers ( request ) );
268332
269333 wrote_all &= s_write_crlf (& message -> outgoing_head_buf );
270334 (void )wrote_all ;
@@ -352,7 +416,7 @@ int aws_h1_encoder_message_init_from_response(
352416 wrote_all &= aws_byte_buf_write_from_whole_cursor (& message -> outgoing_head_buf , status_text );
353417 wrote_all &= s_write_crlf (& message -> outgoing_head_buf );
354418
355- s_write_headers (& message -> outgoing_head_buf , response );
419+ s_write_headers (& message -> outgoing_head_buf , aws_http_message_get_const_headers ( response ) );
356420
357421 wrote_all &= s_write_crlf (& message -> outgoing_head_buf );
358422 (void )wrote_all ;
@@ -368,6 +432,7 @@ int aws_h1_encoder_message_init_from_response(
368432
369433void aws_h1_encoder_message_clean_up (struct aws_h1_encoder_message * message ) {
370434 aws_byte_buf_clean_up (& message -> outgoing_head_buf );
435+ aws_h1_trailer_destroy (message -> trailer );
371436 AWS_ZERO_STRUCT (* message );
372437}
373438
@@ -443,6 +508,32 @@ static void s_populate_chunk_line_buffer(
443508 AWS_ASSERT (wrote_chunk_line );
444509}
445510
511+ struct aws_h1_trailer * aws_h1_trailer_new (
512+ struct aws_allocator * allocator ,
513+ const struct aws_http_headers * trailing_headers ) {
514+ /* Allocate trailer along with storage for the trailer-line */
515+ size_t trailer_size = 0 ;
516+ if (s_scan_outgoing_trailer (trailing_headers , & trailer_size )) {
517+ return NULL ;
518+ }
519+
520+ struct aws_h1_trailer * trailer = aws_mem_calloc (allocator , 1 , sizeof (struct aws_h1_trailer ));
521+ trailer -> allocator = allocator ;
522+
523+ aws_byte_buf_init (& trailer -> trailer_data , allocator , trailer_size ); /* cannot fail */
524+ s_write_headers (& trailer -> trailer_data , trailing_headers );
525+ s_write_crlf (& trailer -> trailer_data ); /* \r\n */
526+ return trailer ;
527+ }
528+
529+ void aws_h1_trailer_destroy (struct aws_h1_trailer * trailer ) {
530+ if (trailer == NULL ) {
531+ return ;
532+ }
533+ aws_byte_buf_clean_up (& trailer -> trailer_data );
534+ aws_mem_release (trailer -> allocator , trailer );
535+ }
536+
446537struct aws_h1_chunk * aws_h1_chunk_new (struct aws_allocator * allocator , const struct aws_http1_chunk_options * options ) {
447538 /* Allocate chunk along with storage for the chunk-line */
448539 struct aws_h1_chunk * chunk ;
@@ -748,11 +839,15 @@ static int s_state_fn_chunk_end(struct aws_h1_encoder *encoder, struct aws_byte_
748839
749840/* Write out trailer after last chunk */
750841static int s_state_fn_chunk_trailer (struct aws_h1_encoder * encoder , struct aws_byte_buf * dst ) {
751- /* We don't currently have API calls that lets users add trailing headers,
752- * so just write out the final CRLF */
753- bool done = s_write_crlf (dst );
842+ bool done ;
843+ /* if a chunked trailer was set */
844+ if (encoder -> message -> trailer ) {
845+ done = s_encode_buf (encoder , dst , & encoder -> message -> trailer -> trailer_data );
846+ } else {
847+ done = s_write_crlf (dst );
848+ }
754849 if (!done ) {
755- /* Remain in this state until done writing out CRLF */
850+ /* Remain in this state until we're done writing out trailer */
756851 return AWS_OP_SUCCESS ;
757852 }
758853
0 commit comments