@@ -438,6 +438,8 @@ on_begin_headers_callback(
438438 request_headers->_stream_id = stream_id;
439439 break ;
440440 }
441+ case NGHTTP2_HCAT_HEADERS:
442+ // Final response headers after a 1xx response or trailer headers.
441443 case NGHTTP2_HCAT_RESPONSE: {
442444 auto stream_map_iter = session_data->_stream_map .find (stream_id);
443445 if (stream_map_iter == session_data->_stream_map .end ()) {
@@ -454,11 +456,7 @@ on_begin_headers_callback(
454456 response_headers->_contains_pseudo_headers_in_fields_array = true ;
455457 break ;
456458 }
457- case NGHTTP2_HCAT_HEADERS:
458- // Headers that are not request or response headers. Trailer headers, for
459- // example, falls into this category.
460- break ;
461- default :
459+ case NGHTTP2_HCAT_PUSH_RESPONSE:
462460 errata.note (S_ERROR, " Got HTTP/2 headers for an unimplemented category: {}" , headers_category);
463461 break ;
464462 }
@@ -510,31 +508,31 @@ on_header_callback(
510508 break ;
511509 }
512510
511+ case NGHTTP2_HCAT_HEADERS: {
512+ // This is either the response headers after a 1xx response or
513+ // trailer headers.
514+ if (!stream_state->_wait_for_response_after_100_continue ) {
515+ // Must be trailer headers.
516+ auto &response_headers = stream_state->_response_from_server ;
517+ response_headers->_trailer_fields_rules ->add_field (name_view, value_view);
518+ break ;
519+ }
520+ // If we reach here, we received the response headers after a 1xx response.
521+ // This is handled along with the NGHTTP2_HCAT_RESPONSE case.
522+ [[fallthrough]];
523+ }
513524 case NGHTTP2_HCAT_RESPONSE: {
514525 auto &response_headers = stream_state->_response_from_server ;
515526 if (name_view == " :status" ) {
516527 response_headers->_status = swoc::svtou (value_view);
517528 response_headers->_status_string = std::string (value_view);
518529 }
519530 response_headers->_fields_rules ->add_field (name_view, value_view);
520- // See if we are expecting a 100 response.
521- if (stream_state->_wait_for_continue ) {
522- if (name_view == " :status" && value_view == " 100" ) {
523- // We got our 100 Continue. No need to wait for it anymore.
524- stream_state->_wait_for_continue = false ;
525- }
526- }
527- break ;
528- }
529- case NGHTTP2_HCAT_HEADERS: {
530- auto &response_headers = stream_state->_response_from_server ;
531- response_headers->_trailer_fields_rules ->add_field (name_view, value_view);
532- break ;
533- }
531+ } break ;
534532 case NGHTTP2_HCAT_PUSH_RESPONSE:
535533 errata.note (
536534 S_ERROR,
537- " Got HTTP/2 an header for an unimplemented category: {}" ,
535+ " Got an HTTP/2 header for an unimplemented category: {}" ,
538536 headers_category);
539537 return 0 ;
540538 }
@@ -850,7 +848,8 @@ on_frame_recv_cb(nghttp2_session * /* session */, nghttp2_frame const *frame, vo
850848 stream_state._key ,
851849 stream_id,
852850 request_from_client);
853- } else if (headers_category == NGHTTP2_HCAT_RESPONSE) {
851+ } else if (
852+ headers_category == NGHTTP2_HCAT_RESPONSE || headers_category == NGHTTP2_HCAT_HEADERS) {
854853 auto &response_from_wire = *stream_state._response_from_server ;
855854 response_from_wire.derive_key ();
856855 if (stream_state._key .empty ()) {
@@ -873,43 +872,60 @@ on_frame_recv_cb(nghttp2_session * /* session */, nghttp2_frame const *frame, vo
873872 // the fields of both the request and response.
874873 response_from_wire.set_key (stream_state._key );
875874 }
875+ auto const &key = stream_state._key ;
876876 if (auto spot{
877877 response_from_wire._fields_rules ->_fields .find (HttpHeader::FIELD_CONTENT_LENGTH)};
878878 spot != response_from_wire._fields_rules ->_fields .end ())
879879 {
880880 size_t expected_size = swoc::svtou (spot->second );
881881 stream_state._body_received .reserve (expected_size);
882882 }
883- errata.note (
884- S_DIAG,
885- " Received an HTTP/2 response for key {} with stream id {}:\n {}" ,
886- stream_state._key ,
887- stream_id,
888- response_from_wire);
889- auto const &key = stream_state._key ;
890- auto const &specified_response = stream_state._specified_response ;
891- if (specified_response &&
892- response_from_wire.verify_headers (key, *specified_response->_fields_rules ))
893- {
883+ if (headers_category == NGHTTP2_HCAT_RESPONSE) {
894884 errata.note (
895- S_ERROR,
896- R"( HTTP/2 response headers did not match expected response headers.)" );
897- session_data->set_non_zero_exit_status ();
898- }
899- if (specified_response && specified_response->_status != 0 &&
900- response_from_wire._status != specified_response->_status &&
901- (response_from_wire._status != 200 || specified_response->_status != 304 ) &&
902- (response_from_wire._status != 304 || specified_response->_status != 200 ))
903- {
885+ S_DIAG,
886+ " Received an HTTP/2 response for key {} with stream id {}:\n {}" ,
887+ key,
888+ stream_id,
889+ response_from_wire);
890+ } else {
904891 errata.note (
905- S_ERROR,
906- R"( HTTP/2 Status Violation: expected {} got {}, key: {})" ,
907- specified_response->_status ,
908- response_from_wire._status ,
909- key);
892+ S_DIAG,
893+ " Received HTTP/2 response trailers for key {} with stream id {}:\n {}" ,
894+ key,
895+ stream_id,
896+ *response_from_wire._trailer_fields_rules );
897+ }
898+ if (response_from_wire._status == 100 ) {
899+ // Prepare for the final response after the 100 Continue.
900+ stream_state._response_from_server = std::make_shared<HttpHeader>();
901+ stream_state._response_from_server ->_stream_id = stream_id;
902+ stream_state._wait_for_continue = false ;
903+ stream_state._wait_for_response_after_100_continue = true ;
904+ errata.note (" Received 100 Continue for key {}, now awaiting response headers" , key);
905+ } else {
906+ stream_state._wait_for_response_after_100_continue = false ;
907+ auto const &specified_response = stream_state._specified_response ;
908+ if (specified_response &&
909+ response_from_wire.verify_headers (key, *specified_response->_fields_rules ))
910+ {
911+ errata.note (
912+ S_ERROR,
913+ R"( HTTP/2 response headers did not match expected response headers.)" );
914+ session_data->set_non_zero_exit_status ();
915+ }
916+ if (specified_response && specified_response->_status != 0 &&
917+ response_from_wire._status != specified_response->_status &&
918+ (response_from_wire._status != 200 || specified_response->_status != 304 ) &&
919+ (response_from_wire._status != 304 || specified_response->_status != 200 ))
920+ {
921+ errata.note (
922+ S_ERROR,
923+ R"( HTTP/2 Status Violation: expected {} got {}, key: {})" ,
924+ specified_response->_status ,
925+ response_from_wire._status ,
926+ key);
927+ }
910928 }
911- } else if (headers_category == NGHTTP2_HCAT_HEADERS) {
912- errata.note (S_DIAG, " Received an HTTP/2 trailer for stream id {}:\n " , stream_id);
913929 }
914930 }
915931 if (flags & NGHTTP2_FLAG_END_STREAM) {
@@ -1543,6 +1559,9 @@ H2Session::write(HttpHeader const &hdr)
15431559 }
15441560
15451561 stream_state->_key = hdr.get_key ();
1562+ // A user shouldn't set Expect: 100-continue with a request with no body,
1563+ // but we support it anyway.
1564+ stream_state->_wait_for_continue = hdr.is_request_with_expect_100_continue ();
15461565 if (hdr._content_length > 0 &&
15471566 (hdr.is_request () || !HttpHeader::STATUS_NO_CONTENT[hdr._status ])) {
15481567 TextView content;
@@ -1561,7 +1580,6 @@ H2Session::write(HttpHeader const &hdr)
15611580 stream_state->_body_to_send = content.data ();
15621581 stream_state->_send_body_length = content.size ();
15631582 stream_state->_send_body_offset = 0 ;
1564- stream_state->_wait_for_continue = hdr.is_request_with_expect_100_continue ();
15651583 stream_state->_last_data_frame = true ;
15661584 if (hdr.is_response ()) {
15671585 // Pack the trailer headers.
0 commit comments