| 2411 | } |
| 2412 | |
| 2413 | void |
| 2414 | Http2ConnectionState::send_headers_frame(Http2Stream *stream) |
| 2415 | { |
| 2416 | uint32_t header_blocks_size = 0; |
| 2417 | int payload_length = 0; |
| 2418 | uint8_t flags = 0x00; |
| 2419 | |
| 2420 | // For outbound streams, set the ID if it has not yet already been set |
| 2421 | // Need to defer setting the stream ID to avoid another later created stream |
| 2422 | // sending out first. This may cause the peer to issue a stream or connection |
| 2423 | // error (new stream less that the greatest we have seen so far) |
| 2424 | this->set_stream_id(stream); |
| 2425 | |
| 2426 | // Keep this debug below set_stream_id so that the id is correct. |
| 2427 | Http2StreamDebug(session, stream->get_id(), "Send HEADERS frame"); |
| 2428 | |
| 2429 | HTTPHdr *send_hdr = stream->get_send_header(); |
| 2430 | if (stream->expect_send_trailer()) { |
| 2431 | // Which is a no-op conversion |
| 2432 | } else { |
| 2433 | http2_convert_header_from_1_1_to_2(send_hdr); |
| 2434 | } |
| 2435 | |
| 2436 | uint32_t buf_len = send_hdr->length_get() * 2; // Make it double just in case |
| 2437 | ts::LocalBuffer local_buffer(buf_len); |
| 2438 | uint8_t *buf = local_buffer.data(); |
| 2439 | |
| 2440 | stream->mark_milestone(Http2StreamMilestone::START_ENCODE_HEADERS); |
| 2441 | Http2ErrorCode result = http2_encode_header_blocks(send_hdr, buf, buf_len, &header_blocks_size, *(this->peer_hpack_handle), |
| 2442 | peer_settings.get(HTTP2_SETTINGS_HEADER_TABLE_SIZE)); |
| 2443 | if (result != Http2ErrorCode::HTTP2_ERROR_NO_ERROR) { |
| 2444 | return; |
| 2445 | } |
| 2446 | |
| 2447 | // Send a HEADERS frame |
| 2448 | if (header_blocks_size <= static_cast<uint32_t>(BUFFER_SIZE_FOR_INDEX(buffer_size_index[HTTP2_FRAME_TYPE_HEADERS]))) { |
| 2449 | payload_length = header_blocks_size; |
| 2450 | flags |= HTTP2_FLAGS_HEADERS_END_HEADERS; |
| 2451 | if (stream->is_outbound_connection()) { // Will be sending a request_header |
| 2452 | int method = send_hdr->method_get_wksidx(); |
| 2453 | |
| 2454 | // Set END_STREAM on request headers for POST, etc. methods combined with |
| 2455 | // an explicit length 0. Some origins RST on request headers with |
| 2456 | // explicit zero length and no end stream flag, causing the request to |
| 2457 | // fail. We emulate chromium behaviour here prevent such RSTs. Transfer-encoding |
| 2458 | // implies there’s a body, regardless of whether it is chunked or not. |
| 2459 | bool content_method = method == HTTP_WKSIDX_POST || method == HTTP_WKSIDX_PUSH || method == HTTP_WKSIDX_PUT; |
| 2460 | bool is_transfer_encoded = send_hdr->presence(MIME_PRESENCE_TRANSFER_ENCODING); |
| 2461 | bool has_content_header = send_hdr->presence(MIME_PRESENCE_CONTENT_LENGTH); |
| 2462 | bool explicit_zero_length = has_content_header && send_hdr->get_content_length() == 0; |
| 2463 | int64_t content_length = has_content_header ? send_hdr->get_content_length() : 0L; |
| 2464 | bool is_chunked = |
| 2465 | is_transfer_encoded && send_hdr->value_get(MIME_FIELD_TRANSFER_ENCODING) == std::string_view(HTTP_VALUE_CHUNKED); |
| 2466 | |
| 2467 | bool expect_content_stream = |
| 2468 | is_transfer_encoded || // transfer encoded content length is unknown |
| 2469 | (!content_method && has_content_header && !explicit_zero_length) || // nonzero content with GET,etc |
| 2470 | (content_method && !explicit_zero_length) || // content-length >0 or empty with POST etc |
no test coverage detected