Line data Source code
1 : #include "source/common/http/http2/codec_impl.h"
2 :
3 : #include <algorithm>
4 : #include <cstdint>
5 : #include <memory>
6 : #include <ostream>
7 : #include <vector>
8 :
9 : #include "envoy/event/dispatcher.h"
10 : #include "envoy/http/codes.h"
11 : #include "envoy/http/header_map.h"
12 : #include "envoy/network/connection.h"
13 :
14 : #include "source/common/common/assert.h"
15 : #include "source/common/common/cleanup.h"
16 : #include "source/common/common/dump_state_utils.h"
17 : #include "source/common/common/enum_to_int.h"
18 : #include "source/common/common/fmt.h"
19 : #include "source/common/common/safe_memcpy.h"
20 : #include "source/common/common/scope_tracker.h"
21 : #include "source/common/common/utility.h"
22 : #include "source/common/http/codes.h"
23 : #include "source/common/http/exception.h"
24 : #include "source/common/http/header_utility.h"
25 : #include "source/common/http/headers.h"
26 : #include "source/common/http/http2/codec_stats.h"
27 : #include "source/common/http/utility.h"
28 : #include "source/common/runtime/runtime_features.h"
29 :
30 : #include "absl/cleanup/cleanup.h"
31 : #include "absl/container/fixed_array.h"
32 : #include "quiche/http2/adapter/callback_visitor.h"
33 : #include "quiche/http2/adapter/nghttp2_adapter.h"
34 : #include "quiche/http2/adapter/oghttp2_adapter.h"
35 :
36 : namespace Envoy {
37 : namespace Http {
38 : namespace Http2 {
39 :
40 : // Changes or additions to details should be reflected in
41 : // docs/root/configuration/http/http_conn_man/response_code_details.rst
42 : class Http2ResponseCodeDetailValues {
43 : public:
44 : // Invalid HTTP header field was received and stream is going to be
45 : // closed.
46 : const absl::string_view ng_http2_err_http_header_ = "http2.invalid.header.field";
47 : // Violation in HTTP messaging rule.
48 : const absl::string_view ng_http2_err_http_messaging_ = "http2.violation.of.messaging.rule";
49 : // none of the above
50 : const absl::string_view ng_http2_err_unknown_ = "http2.unknown.nghttp2.error";
51 : // The number of headers (or trailers) exceeded the configured limits
52 : const absl::string_view too_many_headers = "http2.too_many_headers";
53 : // Envoy detected an HTTP/2 frame flood from the server.
54 : const absl::string_view outbound_frame_flood = "http2.outbound_frames_flood";
55 : // Envoy detected an inbound HTTP/2 frame flood.
56 : const absl::string_view inbound_empty_frame_flood = "http2.inbound_empty_frames_flood";
57 : // Envoy was configured to drop requests with header keys beginning with underscores.
58 : const absl::string_view invalid_underscore = "http2.unexpected_underscore";
59 : // The peer refused the stream.
60 : const absl::string_view remote_refused = "http2.remote_refuse";
61 : // The peer reset the stream.
62 : const absl::string_view remote_reset = "http2.remote_reset";
63 :
64 128 : const absl::string_view errorDetails(int error_code) const {
65 128 : switch (error_code) {
66 61 : case NGHTTP2_ERR_HTTP_HEADER:
67 61 : return ng_http2_err_http_header_;
68 67 : case NGHTTP2_ERR_HTTP_MESSAGING:
69 67 : return ng_http2_err_http_messaging_;
70 0 : default:
71 0 : return ng_http2_err_unknown_;
72 128 : }
73 128 : }
74 : };
75 :
76 133 : int reasonToReset(StreamResetReason reason) {
77 133 : switch (reason) {
78 4 : case StreamResetReason::LocalRefusedStreamReset:
79 4 : return NGHTTP2_REFUSED_STREAM;
80 0 : case StreamResetReason::ConnectError:
81 0 : return NGHTTP2_CONNECT_ERROR;
82 129 : default:
83 129 : return NGHTTP2_NO_ERROR;
84 133 : }
85 133 : }
86 :
87 : using Http2ResponseCodeDetails = ConstSingleton<Http2ResponseCodeDetailValues>;
88 :
89 903 : ReceivedSettingsImpl::ReceivedSettingsImpl(const nghttp2_settings& settings) {
90 1420 : for (uint32_t i = 0; i < settings.niv; ++i) {
91 804 : if (settings.iv[i].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
92 287 : concurrent_stream_limit_ = settings.iv[i].value;
93 287 : break;
94 287 : }
95 804 : }
96 903 : }
97 :
98 : bool Utility::reconstituteCrumbledCookies(const HeaderString& key, const HeaderString& value,
99 24931 : HeaderString& cookies) {
100 24931 : if (key != Headers::get().Cookie.get().c_str()) {
101 6172 : return false;
102 6172 : }
103 :
104 18759 : if (!cookies.empty()) {
105 18739 : cookies.append("; ", 2);
106 18739 : }
107 :
108 18759 : const absl::string_view value_view = value.getStringView();
109 18759 : cookies.append(value_view.data(), value_view.size());
110 18759 : return true;
111 24931 : }
112 :
113 : ConnectionImpl::Http2Callbacks ConnectionImpl::http2_callbacks_;
114 :
115 : std::unique_ptr<http2::adapter::Http2Adapter>
116 : ProdNghttp2SessionFactory::create(const nghttp2_session_callbacks* callbacks,
117 : ConnectionImpl* connection,
118 305 : const http2::adapter::OgHttp2Adapter::Options& options) {
119 305 : auto visitor = std::make_unique<http2::adapter::CallbackVisitor>(
120 305 : http2::adapter::Perspective::kClient, *callbacks, connection);
121 305 : std::unique_ptr<http2::adapter::Http2Adapter> adapter =
122 305 : http2::adapter::OgHttp2Adapter::Create(*visitor, options);
123 305 : connection->setVisitor(std::move(visitor));
124 305 : return adapter;
125 305 : }
126 :
127 : std::unique_ptr<http2::adapter::Http2Adapter>
128 : ProdNghttp2SessionFactory::create(const nghttp2_session_callbacks* callbacks,
129 56 : ConnectionImpl* connection, const nghttp2_option* options) {
130 56 : auto visitor = std::make_unique<http2::adapter::CallbackVisitor>(
131 56 : http2::adapter::Perspective::kClient, *callbacks, connection);
132 56 : auto adapter = http2::adapter::NgHttp2Adapter::CreateClientAdapter(*visitor, options);
133 56 : auto stream_close_listener = [p = adapter.get()](http2::adapter::Http2StreamId stream_id) {
134 9 : p->RemoveStream(stream_id);
135 9 : };
136 56 : visitor->set_stream_close_listener(std::move(stream_close_listener));
137 56 : connection->setVisitor(std::move(visitor));
138 56 : return adapter;
139 56 : }
140 :
141 : void ProdNghttp2SessionFactory::init(ConnectionImpl* connection,
142 361 : const envoy::config::core::v3::Http2ProtocolOptions& options) {
143 361 : connection->sendSettings(options, true);
144 361 : }
145 :
146 : /**
147 : * Helper to remove const during a cast. nghttp2 takes non-const pointers for headers even though
148 : * it copies them.
149 : */
150 0 : template <typename T> static T* removeConst(const void* object) {
151 0 : return const_cast<T*>(reinterpret_cast<const T*>(object));
152 0 : }
153 :
154 : ConnectionImpl::StreamImpl::StreamImpl(ConnectionImpl& parent, uint32_t buffer_limit)
155 : : MultiplexedStreamImplBase(parent.connection_.dispatcher()), parent_(parent),
156 : pending_recv_data_(parent_.connection_.dispatcher().getWatermarkFactory().createBuffer(
157 0 : [this]() -> void { this->pendingRecvBufferLowWatermark(); },
158 0 : [this]() -> void { this->pendingRecvBufferHighWatermark(); },
159 0 : []() -> void { /* TODO(adisuissa): Handle overflow watermark */ })),
160 : pending_send_data_(parent_.connection_.dispatcher().getWatermarkFactory().createBuffer(
161 70 : [this]() -> void { this->pendingSendBufferLowWatermark(); },
162 74 : [this]() -> void { this->pendingSendBufferHighWatermark(); },
163 0 : []() -> void { /* TODO(adisuissa): Handle overflow watermark */ })),
164 : local_end_stream_sent_(false), remote_end_stream_(false), remote_rst_(false),
165 : data_deferred_(false), received_noninformational_headers_(false),
166 : pending_receive_buffer_high_watermark_called_(false),
167 : pending_send_buffer_high_watermark_called_(false), reset_due_to_messaging_error_(false),
168 : defer_processing_backedup_streams_(
169 : Runtime::runtimeFeatureEnabled(Runtime::defer_processing_backedup_streams)),
170 1974 : extend_stream_lifetime_flag_(false) {
171 1974 : parent_.stats_.streams_active_.inc();
172 1974 : if (buffer_limit > 0) {
173 1974 : setWriteBufferWatermarks(buffer_limit);
174 1974 : }
175 1974 : stream_manager_.defer_processing_segment_size_ = parent.connection_.bufferLimit();
176 1974 : }
177 :
178 1974 : void ConnectionImpl::StreamImpl::destroy() {
179 : // Cancel any pending buffered data callback for the stream.
180 1974 : process_buffered_data_callback_.reset();
181 :
182 1974 : MultiplexedStreamImplBase::destroy();
183 1974 : parent_.stats_.streams_active_.dec();
184 1974 : parent_.stats_.pending_send_bytes_.sub(pending_send_data_->length());
185 1974 : }
186 :
187 1499 : void ConnectionImpl::ServerStreamImpl::destroy() {
188 : // Only the downstream stream should clear the downstream of the
189 : // memory account.
190 : // This occurs in destroy as we want to ensure the Stream does not get
191 : // reset called on it from the account.
192 : //
193 : // There are cases where a corresponding upstream stream dtor might
194 : // be called, but the downstream stream isn't going to terminate soon
195 : // such as StreamDecoderFilterCallbacks::recreateStream().
196 1499 : if (buffer_memory_account_) {
197 0 : buffer_memory_account_->clearDownstream();
198 0 : }
199 :
200 1499 : StreamImpl::destroy();
201 1499 : }
202 :
203 0 : static void insertHeader(std::vector<nghttp2_nv>& headers, const HeaderEntry& header) {
204 0 : uint8_t flags = 0;
205 0 : if (header.key().isReference()) {
206 0 : flags |= NGHTTP2_NV_FLAG_NO_COPY_NAME;
207 0 : }
208 0 : if (header.value().isReference()) {
209 0 : flags |= NGHTTP2_NV_FLAG_NO_COPY_VALUE;
210 0 : }
211 0 : const absl::string_view header_key = header.key().getStringView();
212 0 : const absl::string_view header_value = header.value().getStringView();
213 0 : headers.push_back({removeConst<uint8_t>(header_key.data()),
214 0 : removeConst<uint8_t>(header_value.data()), header_key.size(),
215 0 : header_value.size(), flags});
216 0 : }
217 :
218 : void ConnectionImpl::StreamImpl::buildHeaders(std::vector<nghttp2_nv>& final_headers,
219 0 : const HeaderMap& headers) {
220 0 : final_headers.reserve(headers.size());
221 0 : headers.iterate([&final_headers](const HeaderEntry& header) -> HeaderMap::Iterate {
222 0 : insertHeader(final_headers, header);
223 0 : return HeaderMap::Iterate::Continue;
224 0 : });
225 0 : }
226 :
227 7088 : http2::adapter::HeaderRep getRep(const HeaderString& str) {
228 7088 : if (str.isReference()) {
229 4023 : return str.getStringView();
230 4493 : } else {
231 3065 : return std::string(str.getStringView());
232 3065 : }
233 7088 : }
234 :
235 : std::vector<http2::adapter::Header>
236 743 : ConnectionImpl::StreamImpl::buildHeaders(const HeaderMap& headers) {
237 743 : std::vector<http2::adapter::Header> out;
238 743 : out.reserve(headers.size());
239 3544 : headers.iterate([&out](const HeaderEntry& header) -> HeaderMap::Iterate {
240 3544 : out.push_back({getRep(header.key()), getRep(header.value())});
241 3544 : return HeaderMap::Iterate::Continue;
242 3544 : });
243 743 : return out;
244 743 : }
245 :
246 8 : void ConnectionImpl::ServerStreamImpl::encode1xxHeaders(const ResponseHeaderMap& headers) {
247 8 : ASSERT(HeaderUtility::isSpecial1xx(headers));
248 8 : encodeHeaders(headers, false);
249 8 : }
250 :
251 727 : void ConnectionImpl::StreamImpl::encodeHeadersBase(const HeaderMap& headers, bool end_stream) {
252 727 : local_end_stream_ = end_stream;
253 727 : submitHeaders(headers, end_stream);
254 727 : if (parent_.sendPendingFramesAndHandleError()) {
255 : // Intended to check through coverage that this error case is tested
256 0 : return;
257 0 : }
258 727 : }
259 :
260 : Status ConnectionImpl::ClientStreamImpl::encodeHeaders(const RequestHeaderMap& headers,
261 475 : bool end_stream) {
262 475 : parent_.updateActiveStreamsOnEncode(*this);
263 475 : #ifndef ENVOY_ENABLE_UHV
264 : // Headers are now validated by UHV before encoding by the codec. Two checks below are not needed
265 : // when UHV is enabled.
266 : //
267 : // Required headers must be present. This can only happen by some erroneous processing after the
268 : // downstream codecs decode.
269 475 : RETURN_IF_ERROR(HeaderUtility::checkRequiredRequestHeaders(headers));
270 : // Verify that a filter hasn't added an invalid header key or value.
271 469 : RETURN_IF_ERROR(HeaderUtility::checkValidRequestHeaders(headers));
272 : // Extended CONNECT to H/1 upgrade transformation has moved to UHV
273 : // This must exist outside of the scope of isUpgrade as the underlying memory is
274 : // needed until encodeHeadersBase has been called.
275 459 : Http::RequestHeaderMapPtr modified_headers;
276 459 : if (Http::Utility::isUpgrade(headers)) {
277 4 : modified_headers = createHeaderMap<RequestHeaderMapImpl>(headers);
278 4 : upgrade_type_ = std::string(headers.getUpgradeValue());
279 4 : Http::Utility::transformUpgradeRequestFromH1toH2(*modified_headers);
280 4 : encodeHeadersBase(*modified_headers, end_stream);
281 455 : } else if (headers.Method() && headers.Method()->value() == "CONNECT") {
282 0 : modified_headers = createHeaderMap<RequestHeaderMapImpl>(headers);
283 0 : modified_headers->removeScheme();
284 0 : modified_headers->removePath();
285 0 : modified_headers->removeProtocol();
286 0 : encodeHeadersBase(*modified_headers, end_stream);
287 455 : } else {
288 455 : encodeHeadersBase(headers, end_stream);
289 455 : }
290 : #else
291 : encodeHeadersBase(headers, end_stream);
292 : #endif
293 459 : return okStatus();
294 469 : }
295 :
296 : void ConnectionImpl::ServerStreamImpl::encodeHeaders(const ResponseHeaderMap& headers,
297 268 : bool end_stream) {
298 268 : parent_.updateActiveStreamsOnEncode(*this);
299 : // The contract is that client codecs must ensure that :status is present.
300 268 : ASSERT(headers.Status() != nullptr);
301 :
302 268 : #ifndef ENVOY_ENABLE_UHV
303 : // Extended CONNECT to H/1 upgrade transformation has moved to UHV
304 : // This must exist outside of the scope of isUpgrade as the underlying memory is
305 : // needed until encodeHeadersBase has been called.
306 268 : Http::ResponseHeaderMapPtr modified_headers;
307 268 : if (Http::Utility::isUpgrade(headers)) {
308 2 : modified_headers = createHeaderMap<ResponseHeaderMapImpl>(headers);
309 2 : Http::Utility::transformUpgradeResponseFromH1toH2(*modified_headers);
310 2 : encodeHeadersBase(*modified_headers, end_stream);
311 266 : } else {
312 266 : encodeHeadersBase(headers, end_stream);
313 266 : }
314 : #else
315 : encodeHeadersBase(headers, end_stream);
316 : #endif
317 268 : }
318 :
319 58 : void ConnectionImpl::StreamImpl::encodeTrailersBase(const HeaderMap& trailers) {
320 58 : parent_.updateActiveStreamsOnEncode(*this);
321 58 : ASSERT(!local_end_stream_);
322 58 : local_end_stream_ = true;
323 58 : if (pending_send_data_->length() > 0) {
324 : // In this case we want trailers to come after we release all pending body data that is
325 : // waiting on window updates. We need to save the trailers so that we can emit them later.
326 : // However, for empty trailers, we don't need to to save the trailers.
327 2 : ASSERT(!pending_trailers_to_encode_);
328 2 : const bool skip_encoding_empty_trailers = trailers.empty();
329 2 : if (!skip_encoding_empty_trailers) {
330 0 : pending_trailers_to_encode_ = cloneTrailers(trailers);
331 0 : onLocalEndStream();
332 0 : }
333 56 : } else {
334 56 : submitTrailers(trailers);
335 56 : if (parent_.sendPendingFramesAndHandleError()) {
336 : // Intended to check through coverage that this error case is tested
337 0 : return;
338 0 : }
339 56 : }
340 58 : }
341 :
342 8 : void ConnectionImpl::StreamImpl::encodeMetadata(const MetadataMapVector& metadata_map_vector) {
343 8 : parent_.updateActiveStreamsOnEncode(*this);
344 8 : ASSERT(parent_.allow_metadata_);
345 8 : NewMetadataEncoder& metadata_encoder = getMetadataEncoder();
346 8 : auto sources_vec = metadata_encoder.createSources(metadata_map_vector);
347 8 : for (auto& source : sources_vec) {
348 8 : parent_.adapter_->SubmitMetadata(stream_id_, 16 * 1024, std::move(source));
349 8 : }
350 :
351 8 : if (parent_.sendPendingFramesAndHandleError()) {
352 : // Intended to check through coverage that this error case is tested
353 0 : return;
354 0 : }
355 8 : }
356 :
357 16 : void ConnectionImpl::StreamImpl::processBufferedData() {
358 16 : ENVOY_CONN_LOG(debug, "Stream {} processing buffered data.", parent_.connection_, stream_id_);
359 :
360 : // Restore crash dump context when processing buffered data.
361 16 : Event::Dispatcher& dispatcher = parent_.connection_.dispatcher();
362 : // This method is only called from a callback placed directly on the
363 : // dispatcher, as such the dispatcher shouldn't have any tracked objects.
364 16 : ASSERT(dispatcher.trackedObjectStackIsEmpty());
365 16 : Envoy::ScopeTrackedObjectStack stack;
366 16 : stack.add(parent_.connection_);
367 :
368 16 : absl::Cleanup clear_current_stream_id = [this]() { parent_.current_stream_id_.reset(); };
369 : // TODO(kbaichoo): When we add support to *ConnectionImpl::getStream* for
370 : // deferred closed streams we can use their stream id here.
371 16 : if (!stream_manager_.buffered_on_stream_close_) {
372 0 : ASSERT(!parent_.current_stream_id_.has_value());
373 0 : parent_.current_stream_id_ = stream_id_;
374 0 : }
375 :
376 16 : stack.add(parent_);
377 16 : ScopeTrackerScopeState scope{&stack, dispatcher};
378 :
379 16 : if (stream_manager_.body_buffered_ && continueProcessingBufferedData()) {
380 16 : decodeData();
381 16 : }
382 :
383 16 : if (stream_manager_.trailers_buffered_ && !stream_manager_.body_buffered_ &&
384 16 : continueProcessingBufferedData()) {
385 0 : decodeTrailers();
386 0 : ASSERT(!stream_manager_.trailers_buffered_);
387 0 : }
388 :
389 : // Reset cases are handled by resetStream and directly invoke onStreamClose,
390 : // which consumes the buffered_on_stream_close_ so we don't invoke
391 : // onStreamClose twice.
392 16 : if (stream_manager_.buffered_on_stream_close_ && !stream_manager_.hasBufferedBodyOrTrailers()) {
393 16 : ASSERT(!reset_reason_.has_value());
394 16 : ENVOY_CONN_LOG(debug, "invoking onStreamClose for stream: {} via processBufferedData",
395 16 : parent_.connection_, stream_id_);
396 : // We only buffer the onStreamClose if we had no errors.
397 16 : if (Status status = parent_.onStreamClose(this, 0); !status.ok()) {
398 0 : ENVOY_CONN_LOG(debug, "error invoking onStreamClose: {}", parent_.connection_,
399 0 : status.message());
400 0 : }
401 16 : }
402 16 : }
403 :
404 37 : void ConnectionImpl::StreamImpl::grantPeerAdditionalStreamWindow() {
405 37 : parent_.adapter_->MarkDataConsumedForStream(stream_id_, unconsumed_bytes_);
406 37 : unconsumed_bytes_ = 0;
407 37 : if (parent_.sendPendingFramesAndHandleError()) {
408 : // Intended to check through coverage that this error case is tested
409 0 : return;
410 0 : }
411 37 : }
412 :
413 82 : void ConnectionImpl::StreamImpl::readDisable(bool disable) {
414 82 : ENVOY_CONN_LOG(debug, "Stream {} {}, unconsumed_bytes {} read_disable_count {}",
415 82 : parent_.connection_, stream_id_, (disable ? "disabled" : "enabled"),
416 82 : unconsumed_bytes_, read_disable_count_);
417 82 : if (disable) {
418 45 : ++read_disable_count_;
419 45 : } else {
420 37 : ASSERT(read_disable_count_ > 0);
421 37 : --read_disable_count_;
422 37 : if (!buffersOverrun()) {
423 37 : scheduleProcessingOfBufferedData(false);
424 37 : if (shouldAllowPeerAdditionalStreamWindow()) {
425 37 : grantPeerAdditionalStreamWindow();
426 37 : }
427 37 : }
428 37 : }
429 82 : }
430 :
431 37 : void ConnectionImpl::StreamImpl::scheduleProcessingOfBufferedData(bool schedule_next_iteration) {
432 37 : if (defer_processing_backedup_streams_ && stream_manager_.hasBufferedBodyOrTrailers()) {
433 16 : if (!process_buffered_data_callback_) {
434 16 : process_buffered_data_callback_ = parent_.connection_.dispatcher().createSchedulableCallback(
435 16 : [this]() { processBufferedData(); });
436 16 : }
437 :
438 : // We schedule processing to occur in another callback to avoid
439 : // reentrant and deep call stacks.
440 16 : if (schedule_next_iteration) {
441 0 : process_buffered_data_callback_->scheduleCallbackNextIteration();
442 16 : } else {
443 16 : process_buffered_data_callback_->scheduleCallbackCurrentIteration();
444 16 : }
445 16 : }
446 37 : }
447 :
448 0 : void ConnectionImpl::StreamImpl::pendingRecvBufferHighWatermark() {
449 : // If `defer_processing_backedup_streams_`, read disabling here can become
450 : // dangerous as it can prevent us from processing buffered data.
451 0 : if (!defer_processing_backedup_streams_) {
452 0 : ENVOY_CONN_LOG(debug, "recv buffer over limit ", parent_.connection_);
453 0 : ASSERT(!pending_receive_buffer_high_watermark_called_);
454 0 : pending_receive_buffer_high_watermark_called_ = true;
455 0 : readDisable(true);
456 0 : }
457 0 : }
458 :
459 0 : void ConnectionImpl::StreamImpl::pendingRecvBufferLowWatermark() {
460 : // If `defer_processing_backedup_streams_`, we don't read disable on
461 : // high watermark, so we shouldn't read disable here.
462 0 : if (defer_processing_backedup_streams_) {
463 0 : if (shouldAllowPeerAdditionalStreamWindow()) {
464 : // We should grant additional stream window here, in case the
465 : // `pending_recv_buffer_` was blocking flow control updates
466 : // from going to the peer.
467 0 : grantPeerAdditionalStreamWindow();
468 0 : }
469 0 : } else {
470 0 : ENVOY_CONN_LOG(debug, "recv buffer under limit ", parent_.connection_);
471 0 : ASSERT(pending_receive_buffer_high_watermark_called_);
472 0 : pending_receive_buffer_high_watermark_called_ = false;
473 0 : readDisable(false);
474 0 : }
475 0 : }
476 :
477 22198 : void ConnectionImpl::StreamImpl::decodeData() {
478 22198 : if (defer_processing_backedup_streams_ && buffersOverrun()) {
479 28 : ENVOY_CONN_LOG(trace, "Stream {} buffering decodeData() call.", parent_.connection_,
480 28 : stream_id_);
481 28 : stream_manager_.body_buffered_ = true;
482 28 : return;
483 28 : }
484 :
485 : // Some buffered body will be consumed. If there remains buffered body after
486 : // this call, set this to true.
487 22170 : stream_manager_.body_buffered_ = false;
488 :
489 22170 : bool already_drained_data = false;
490 : // It's possible that we are waiting to send a deferred reset, so only raise data if local
491 : // is not complete.
492 22170 : if (!deferred_reset_) {
493 : // We should decode data in chunks only if we have defer processing enabled
494 : // with a non-zero defer_processing_segment_size, and the buffer holds more
495 : // data than the defer_processing_segment_size. Otherwise, push the
496 : // entire buffer through.
497 22169 : const bool decode_data_in_chunk =
498 22169 : defer_processing_backedup_streams_ && stream_manager_.decodeAsChunks() &&
499 22169 : pending_recv_data_->length() > stream_manager_.defer_processing_segment_size_;
500 :
501 22169 : if (decode_data_in_chunk) {
502 0 : Buffer::OwnedImpl chunk_buffer;
503 : // TODO(kbaichoo): Consider implementing an approximate move for chunking.
504 0 : chunk_buffer.move(*pending_recv_data_, stream_manager_.defer_processing_segment_size_);
505 :
506 : // With the current implementation this should always be true,
507 : // though this can change with approximation.
508 0 : stream_manager_.body_buffered_ = true;
509 0 : ASSERT(pending_recv_data_->length() > 0);
510 :
511 0 : decoder().decodeData(chunk_buffer, sendEndStream());
512 0 : already_drained_data = true;
513 :
514 0 : if (!buffersOverrun()) {
515 0 : scheduleProcessingOfBufferedData(true);
516 0 : }
517 22169 : } else {
518 : // Send the entire buffer through.
519 22169 : decoder().decodeData(*pending_recv_data_, sendEndStream());
520 22169 : }
521 22169 : }
522 :
523 22171 : if (!already_drained_data) {
524 22171 : pending_recv_data_->drain(pending_recv_data_->length());
525 22171 : }
526 22170 : }
527 :
528 183 : void ConnectionImpl::ClientStreamImpl::decodeHeaders() {
529 183 : auto& headers = absl::get<ResponseHeaderMapPtr>(headers_or_trailers_);
530 183 : #ifndef ENVOY_ENABLE_UHV
531 183 : const uint64_t status = Http::Utility::getResponseStatus(*headers);
532 :
533 : // Extended CONNECT to H/1 upgrade transformation has moved to UHV
534 183 : if (!upgrade_type_.empty() && headers->Status()) {
535 2 : Http::Utility::transformUpgradeResponseFromH2toH1(*headers, upgrade_type_);
536 2 : }
537 : #else
538 : // In UHV mode the :status header at this point can be malformed, as it is validated
539 : // later on in the response_decoder_.decodeHeaders() call.
540 : // Account for this here.
541 : absl::optional<uint64_t> status_opt = Http::Utility::getResponseStatusOrNullopt(*headers);
542 : if (!status_opt.has_value()) {
543 : // In case the status is invalid or missing, the response_decoder_.decodeHeaders() will fail the
544 : // request
545 : response_decoder_.decodeHeaders(std::move(headers), sendEndStream());
546 : return;
547 : }
548 : const uint64_t status = status_opt.value();
549 : #endif
550 : // Non-informational headers are non-1xx OR 101-SwitchingProtocols, since 101 implies that further
551 : // proxying is on an upgrade path.
552 : // TODO(#29071) determine how to handle 101, since it is not supported by HTTP/2
553 183 : received_noninformational_headers_ =
554 183 : !CodeUtility::is1xx(status) || status == enumToInt(Http::Code::SwitchingProtocols);
555 :
556 183 : if (HeaderUtility::isSpecial1xx(*headers)) {
557 7 : ASSERT(!remote_end_stream_);
558 7 : response_decoder_.decode1xxHeaders(std::move(headers));
559 176 : } else {
560 176 : response_decoder_.decodeHeaders(std::move(headers), sendEndStream());
561 176 : }
562 183 : }
563 :
564 20 : bool ConnectionImpl::StreamImpl::maybeDeferDecodeTrailers() {
565 20 : ASSERT(!deferred_reset_.has_value());
566 : // Buffer trailers if we're deferring processing and not flushing all data
567 : // through and either
568 : // 1) Buffers are overrun
569 : // 2) There's buffered body which should get processed before these trailers
570 : // to avoid losing data.
571 20 : if (defer_processing_backedup_streams_ && (buffersOverrun() || stream_manager_.body_buffered_)) {
572 4 : stream_manager_.trailers_buffered_ = true;
573 4 : ENVOY_CONN_LOG(trace, "Stream {} buffering decodeTrailers() call.", parent_.connection_,
574 4 : stream_id_);
575 4 : return true;
576 4 : }
577 :
578 16 : return false;
579 20 : }
580 :
581 10 : void ConnectionImpl::ClientStreamImpl::decodeTrailers() {
582 10 : if (maybeDeferDecodeTrailers()) {
583 4 : return;
584 4 : }
585 :
586 : // Consume any buffered trailers.
587 6 : stream_manager_.trailers_buffered_ = false;
588 :
589 6 : response_decoder_.decodeTrailers(
590 6 : std::move(absl::get<ResponseTrailerMapPtr>(headers_or_trailers_)));
591 6 : }
592 :
593 312 : void ConnectionImpl::ServerStreamImpl::decodeHeaders() {
594 312 : auto& headers = absl::get<RequestHeaderMapSharedPtr>(headers_or_trailers_);
595 312 : #ifndef ENVOY_ENABLE_UHV
596 : // Extended CONNECT to H/1 upgrade transformation has moved to UHV
597 312 : if (Http::Utility::isH2UpgradeRequest(*headers)) {
598 0 : Http::Utility::transformUpgradeRequestFromH2toH1(*headers);
599 0 : }
600 312 : #endif
601 312 : request_decoder_->decodeHeaders(std::move(headers), sendEndStream());
602 312 : }
603 :
604 10 : void ConnectionImpl::ServerStreamImpl::decodeTrailers() {
605 10 : if (maybeDeferDecodeTrailers()) {
606 0 : return;
607 0 : }
608 :
609 : // Consume any buffered trailers.
610 10 : stream_manager_.trailers_buffered_ = false;
611 :
612 10 : request_decoder_->decodeTrailers(
613 10 : std::move(absl::get<RequestTrailerMapPtr>(headers_or_trailers_)));
614 10 : }
615 :
616 74 : void ConnectionImpl::StreamImpl::pendingSendBufferHighWatermark() {
617 74 : ENVOY_CONN_LOG(debug, "send buffer over limit ", parent_.connection_);
618 74 : ASSERT(!pending_send_buffer_high_watermark_called_);
619 74 : pending_send_buffer_high_watermark_called_ = true;
620 74 : runHighWatermarkCallbacks();
621 74 : }
622 :
623 70 : void ConnectionImpl::StreamImpl::pendingSendBufferLowWatermark() {
624 70 : ENVOY_CONN_LOG(debug, "send buffer under limit ", parent_.connection_);
625 70 : ASSERT(pending_send_buffer_high_watermark_called_);
626 70 : pending_send_buffer_high_watermark_called_ = false;
627 70 : runLowWatermarkCallbacks();
628 70 : }
629 :
630 24931 : void ConnectionImpl::StreamImpl::saveHeader(HeaderString&& name, HeaderString&& value) {
631 24931 : if (!Utility::reconstituteCrumbledCookies(name, value, cookies_)) {
632 6172 : headers().addViaMove(std::move(name), std::move(value));
633 6172 : }
634 24931 : }
635 :
636 56 : void ConnectionImpl::StreamImpl::submitTrailers(const HeaderMap& trailers) {
637 56 : ASSERT(local_end_stream_);
638 56 : const bool skip_encoding_empty_trailers = trailers.empty();
639 56 : if (skip_encoding_empty_trailers) {
640 40 : ENVOY_CONN_LOG(debug, "skipping submitting trailers", parent_.connection_);
641 :
642 : // Instead of submitting empty trailers, we send empty data instead.
643 40 : Buffer::OwnedImpl empty_buffer;
644 40 : encodeDataHelper(empty_buffer, /*end_stream=*/true, skip_encoding_empty_trailers);
645 40 : return;
646 40 : }
647 :
648 16 : std::vector<http2::adapter::Header> final_headers = buildHeaders(trailers);
649 16 : parent_.adapter_->SubmitTrailer(stream_id_, final_headers);
650 16 : }
651 :
652 : std::pair<int64_t, bool>
653 23432 : ConnectionImpl::StreamDataFrameSource::SelectPayloadLength(size_t max_length) {
654 23432 : if (stream_.pending_send_data_->length() == 0 && !stream_.local_end_stream_) {
655 1098 : ASSERT(!stream_.data_deferred_);
656 1098 : stream_.data_deferred_ = true;
657 1098 : return {kBlocked, false};
658 22417 : } else {
659 22334 : const size_t length = std::min<size_t>(max_length, stream_.pending_send_data_->length());
660 22334 : bool end_data = false;
661 22334 : if (stream_.local_end_stream_ && length == stream_.pending_send_data_->length()) {
662 237 : end_data = true;
663 237 : if (stream_.pending_trailers_to_encode_) {
664 0 : stream_.submitTrailers(*stream_.pending_trailers_to_encode_);
665 0 : stream_.pending_trailers_to_encode_.reset();
666 237 : } else {
667 237 : send_fin_ = true;
668 237 : }
669 237 : }
670 22334 : return {static_cast<int64_t>(length), end_data};
671 22334 : }
672 23432 : }
673 :
674 : bool ConnectionImpl::StreamDataFrameSource::Send(absl::string_view frame_header,
675 22334 : size_t payload_length) {
676 22334 : stream_.parent_.protocol_constraints_.incrementOutboundDataFrameCount();
677 :
678 22334 : Buffer::OwnedImpl output;
679 22334 : stream_.parent_.addOutboundFrameFragment(
680 22334 : output, reinterpret_cast<const uint8_t*>(frame_header.data()), frame_header.size());
681 22334 : if (!stream_.parent_.protocol_constraints_.checkOutboundFrameLimits().ok()) {
682 0 : ENVOY_CONN_LOG(debug, "error sending data frame: Too many frames in the outbound queue",
683 0 : stream_.parent_.connection_);
684 0 : stream_.setDetails(Http2ResponseCodeDetails::get().outbound_frame_flood);
685 0 : }
686 :
687 22334 : stream_.parent_.stats_.pending_send_bytes_.sub(payload_length);
688 22334 : output.move(*stream_.pending_send_data_, payload_length);
689 22334 : stream_.parent_.connection_.write(output, false);
690 22334 : return true;
691 22334 : }
692 :
693 459 : void ConnectionImpl::ClientStreamImpl::submitHeaders(const HeaderMap& headers, bool end_stream) {
694 459 : ASSERT(stream_id_ == -1);
695 459 : stream_id_ = parent_.adapter_->SubmitRequest(
696 459 : buildHeaders(headers), end_stream ? nullptr : std::make_unique<StreamDataFrameSource>(*this),
697 459 : base());
698 459 : ASSERT(stream_id_ > 0);
699 459 : }
700 :
701 268 : void ConnectionImpl::ServerStreamImpl::submitHeaders(const HeaderMap& headers, bool end_stream) {
702 268 : ASSERT(stream_id_ != -1);
703 268 : parent_.adapter_->SubmitResponse(stream_id_, buildHeaders(headers),
704 268 : end_stream ? nullptr
705 268 : : std::make_unique<StreamDataFrameSource>(*this));
706 268 : }
707 :
708 0 : void ConnectionImpl::StreamImpl::onPendingFlushTimer() {
709 0 : ENVOY_CONN_LOG(debug, "pending stream flush timeout", parent_.connection_);
710 0 : MultiplexedStreamImplBase::onPendingFlushTimer();
711 0 : parent_.stats_.tx_flush_timeout_.inc();
712 0 : ASSERT(local_end_stream_ && !local_end_stream_sent_);
713 : // This will emit a reset frame for this stream and close the stream locally.
714 : // Only the stream adapter's reset callback should run as other higher layers
715 : // think the stream is already finished.
716 0 : resetStreamWorker(StreamResetReason::LocalReset);
717 0 : if (parent_.sendPendingFramesAndHandleError()) {
718 : // Intended to check through coverage that this error case is tested
719 0 : return;
720 0 : }
721 0 : }
722 :
723 1389 : void ConnectionImpl::StreamImpl::encodeData(Buffer::Instance& data, bool end_stream) {
724 1389 : parent_.updateActiveStreamsOnEncode(*this);
725 1389 : ASSERT(!local_end_stream_);
726 1389 : encodeDataHelper(data, end_stream,
727 : /*skip_encoding_empty_trailers=*/
728 1389 : false);
729 1389 : }
730 :
731 : void ConnectionImpl::StreamImpl::encodeDataHelper(Buffer::Instance& data, bool end_stream,
732 1429 : bool skip_encoding_empty_trailers) {
733 1429 : if (skip_encoding_empty_trailers) {
734 40 : ASSERT(data.length() == 0 && end_stream);
735 40 : }
736 :
737 1429 : local_end_stream_ = end_stream;
738 1429 : parent_.stats_.pending_send_bytes_.add(data.length());
739 1429 : pending_send_data_->move(data);
740 1429 : if (data_deferred_) {
741 916 : bool success = parent_.adapter_->ResumeStream(stream_id_);
742 916 : ASSERT(success);
743 :
744 916 : data_deferred_ = false;
745 916 : }
746 :
747 1429 : if (parent_.sendPendingFramesAndHandleError()) {
748 : // Intended to check through coverage that this error case is tested
749 0 : return;
750 0 : }
751 1429 : if (local_end_stream_) {
752 267 : onLocalEndStream();
753 267 : }
754 1429 : }
755 :
756 15 : void ConnectionImpl::ServerStreamImpl::resetStream(StreamResetReason reason) {
757 : // Clear the downstream on the account since we're resetting the downstream.
758 15 : if (buffer_memory_account_) {
759 0 : buffer_memory_account_->clearDownstream();
760 0 : }
761 :
762 15 : StreamImpl::resetStream(reason);
763 15 : }
764 :
765 136 : void ConnectionImpl::StreamImpl::resetStream(StreamResetReason reason) {
766 136 : reset_reason_ = reason;
767 :
768 : // Higher layers expect calling resetStream() to immediately raise reset callbacks.
769 136 : runResetCallbacks(reason);
770 :
771 : // If we've bufferedOnStreamClose for this stream, we shouldn't propagate this
772 : // reset as nghttp2 will have forgotten about the stream.
773 136 : if (stream_manager_.buffered_on_stream_close_) {
774 0 : ENVOY_CONN_LOG(
775 0 : trace, "Stopped propagating reset to nghttp2 as we've buffered onStreamClose for stream {}",
776 0 : parent_.connection_, stream_id_);
777 : // The stream didn't originally have an NGHTTP2 error, since we buffered
778 : // its stream close.
779 0 : if (Status status = parent_.onStreamClose(this, 0); !status.ok()) {
780 0 : ENVOY_CONN_LOG(debug, "error invoking onStreamClose: {}", parent_.connection_,
781 0 : status.message());
782 0 : }
783 0 : return;
784 0 : }
785 :
786 : // If we submit a reset, nghttp2 will cancel outbound frames that have not yet been sent.
787 : // We want these frames to go out so we defer the reset until we send all of the frames that
788 : // end the local stream. However, if we're resetting the stream due to
789 : // overload, we should reset the stream as soon as possible to free used
790 : // resources.
791 136 : if (useDeferredReset() && local_end_stream_ && !local_end_stream_sent_ &&
792 136 : reason != StreamResetReason::OverloadManager) {
793 2 : ASSERT(parent_.getStreamUnchecked(stream_id_) != nullptr);
794 2 : parent_.pending_deferred_reset_streams_.emplace(stream_id_, this);
795 2 : deferred_reset_ = reason;
796 2 : ENVOY_CONN_LOG(trace, "deferred reset stream", parent_.connection_);
797 136 : } else {
798 134 : resetStreamWorker(reason);
799 134 : }
800 :
801 : // We must still call sendPendingFrames() in both the deferred and not deferred path. This forces
802 : // the cleanup logic to run which will reset the stream in all cases if all data frames could not
803 : // be sent.
804 136 : if (parent_.sendPendingFramesAndHandleError()) {
805 : // Intended to check through coverage that this error case is tested
806 0 : return;
807 0 : }
808 136 : }
809 :
810 134 : void ConnectionImpl::StreamImpl::resetStreamWorker(StreamResetReason reason) {
811 134 : if (stream_id_ == -1) {
812 : // Handle the case where client streams are reset before headers are created.
813 1 : return;
814 1 : }
815 133 : if (codec_callbacks_) {
816 0 : codec_callbacks_->onCodecLowLevelReset();
817 0 : }
818 133 : parent_.adapter_->SubmitRst(stream_id_,
819 133 : static_cast<http2::adapter::Http2ErrorCode>(reasonToReset(reason)));
820 133 : }
821 :
822 8 : NewMetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoder() {
823 8 : if (metadata_encoder_ == nullptr) {
824 6 : metadata_encoder_ = std::make_unique<NewMetadataEncoder>();
825 6 : }
826 8 : return *metadata_encoder_;
827 8 : }
828 :
829 50 : MetadataDecoder& ConnectionImpl::StreamImpl::getMetadataDecoder() {
830 50 : if (metadata_decoder_ == nullptr) {
831 6 : auto cb = [this](MetadataMapPtr&& metadata_map_ptr) {
832 6 : this->onMetadataDecoded(std::move(metadata_map_ptr));
833 6 : };
834 4 : metadata_decoder_ = std::make_unique<MetadataDecoder>(cb);
835 4 : }
836 50 : return *metadata_decoder_;
837 50 : }
838 :
839 6 : void ConnectionImpl::StreamImpl::onMetadataDecoded(MetadataMapPtr&& metadata_map_ptr) {
840 : // Empty metadata maps should not be decoded.
841 6 : if (metadata_map_ptr->empty()) {
842 0 : ENVOY_CONN_LOG(debug, "decode metadata called with empty map, skipping", parent_.connection_);
843 0 : parent_.stats_.metadata_empty_frames_.inc();
844 6 : } else {
845 6 : decoder().decodeMetadata(std::move(metadata_map_ptr));
846 6 : }
847 6 : }
848 :
849 360 : void ConnectionImpl::StreamImpl::setAccount(Buffer::BufferMemoryAccountSharedPtr account) {
850 360 : buffer_memory_account_ = account;
851 360 : pending_recv_data_->bindAccount(buffer_memory_account_);
852 360 : pending_send_data_->bindAccount(buffer_memory_account_);
853 360 : }
854 :
855 : ConnectionImpl::ConnectionImpl(Network::Connection& connection, CodecStats& stats,
856 : Random::RandomGenerator& random_generator,
857 : const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
858 : const uint32_t max_headers_kb, const uint32_t max_headers_count)
859 : : stats_(stats), connection_(connection), max_headers_kb_(max_headers_kb),
860 : max_headers_count_(max_headers_count),
861 : per_stream_buffer_limit_(http2_options.initial_stream_window_size().value()),
862 : stream_error_on_invalid_http_messaging_(
863 : http2_options.override_stream_error_on_invalid_http_message().value()),
864 : protocol_constraints_(stats, http2_options), dispatching_(false), raised_goaway_(false),
865 : random_(random_generator),
866 2860 : last_received_data_time_(connection_.dispatcher().timeSource().monotonicTime()) {
867 2860 : if (http2_options.has_use_oghttp2_codec()) {
868 0 : use_oghttp2_library_ = http2_options.use_oghttp2_codec().value();
869 2860 : } else {
870 2860 : use_oghttp2_library_ =
871 2860 : Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_use_oghttp2");
872 2860 : }
873 2860 : if (http2_options.has_connection_keepalive()) {
874 0 : keepalive_interval_ = std::chrono::milliseconds(
875 0 : PROTOBUF_GET_MS_OR_DEFAULT(http2_options.connection_keepalive(), interval, 0));
876 0 : keepalive_timeout_ = std::chrono::milliseconds(
877 0 : PROTOBUF_GET_MS_REQUIRED(http2_options.connection_keepalive(), timeout));
878 0 : keepalive_interval_jitter_percent_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(
879 0 : http2_options.connection_keepalive(), interval_jitter, 15.0);
880 :
881 0 : if (keepalive_interval_.count() > 0) {
882 0 : keepalive_send_timer_ = connection.dispatcher().createTimer([this]() { sendKeepalive(); });
883 0 : }
884 0 : keepalive_timeout_timer_ =
885 0 : connection.dispatcher().createTimer([this]() { onKeepaliveResponseTimeout(); });
886 :
887 : // This call schedules the initial interval, with jitter.
888 0 : onKeepaliveResponse();
889 0 : }
890 2860 : }
891 :
892 2860 : ConnectionImpl::~ConnectionImpl() {
893 2881 : for (const auto& stream : active_streams_) {
894 1613 : stream->destroy();
895 1613 : }
896 2860 : }
897 :
898 0 : void ConnectionImpl::sendKeepalive() {
899 0 : ASSERT(keepalive_timeout_timer_);
900 0 : if (keepalive_timeout_timer_->enabled()) {
901 0 : ENVOY_CONN_LOG(trace, "Skipping PING: already awaiting PING ACK", connection_);
902 0 : return;
903 0 : }
904 :
905 : // Include the current time as the payload to help with debugging.
906 0 : SystemTime now = connection_.dispatcher().timeSource().systemTime();
907 0 : uint64_t ms_since_epoch =
908 0 : std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
909 0 : ENVOY_CONN_LOG(trace, "Sending keepalive PING {}", connection_, ms_since_epoch);
910 :
911 0 : adapter_->SubmitPing(ms_since_epoch);
912 :
913 0 : if (sendPendingFramesAndHandleError()) {
914 : // Intended to check through coverage that this error case is tested
915 0 : return;
916 0 : }
917 0 : keepalive_timeout_timer_->enableTimer(keepalive_timeout_);
918 0 : }
919 :
920 0 : void ConnectionImpl::onKeepaliveResponse() {
921 : // Check the timers for nullptr in case the peer sent an unsolicited PING ACK.
922 0 : if (keepalive_timeout_timer_ != nullptr) {
923 0 : keepalive_timeout_timer_->disableTimer();
924 0 : }
925 0 : if (keepalive_send_timer_ != nullptr && keepalive_interval_.count()) {
926 0 : uint64_t interval_ms = keepalive_interval_.count();
927 0 : const uint64_t jitter_percent_mod = keepalive_interval_jitter_percent_ * interval_ms / 100;
928 0 : if (jitter_percent_mod > 0) {
929 0 : interval_ms += random_.random() % jitter_percent_mod;
930 0 : }
931 0 : keepalive_send_timer_->enableTimer(std::chrono::milliseconds(interval_ms));
932 0 : }
933 0 : }
934 :
935 0 : void ConnectionImpl::onKeepaliveResponseTimeout() {
936 0 : ENVOY_CONN_LOG_EVENT(debug, "h2_ping_timeout", "Closing connection due to keepalive timeout",
937 0 : connection_);
938 0 : stats_.keepalive_timeout_.inc();
939 0 : connection_.close(Network::ConnectionCloseType::NoFlush,
940 0 : StreamInfo::LocalCloseReasons::get().Http2PingTimeout);
941 0 : }
942 :
943 0 : bool ConnectionImpl::slowContainsStreamId(int32_t stream_id) const {
944 0 : for (const auto& stream : active_streams_) {
945 0 : if (stream->stream_id_ == stream_id) {
946 0 : return true;
947 0 : }
948 0 : }
949 :
950 0 : return false;
951 0 : }
952 :
953 45212 : Http::Status ConnectionImpl::dispatch(Buffer::Instance& data) {
954 45212 : ScopeTrackerScopeState scope(this, connection_.dispatcher());
955 45212 : ENVOY_CONN_LOG(trace, "dispatching {} bytes", connection_, data.length());
956 : // Make sure that dispatching_ is set to false after dispatching, even when
957 : // ConnectionImpl::dispatch returns early or throws an exception (consider removing if there is a
958 : // single return after exception removal (#10878)).
959 45212 : Cleanup cleanup([this]() {
960 45212 : dispatching_ = false;
961 45212 : current_slice_ = nullptr;
962 45212 : current_stream_id_.reset();
963 45212 : });
964 45212 : last_received_data_time_ = connection_.dispatcher().timeSource().monotonicTime();
965 62379 : for (const Buffer::RawSlice& slice : data.getRawSlices()) {
966 62379 : current_slice_ = &slice;
967 62379 : dispatching_ = true;
968 62379 : ssize_t rc;
969 62379 : rc = adapter_->ProcessBytes(absl::string_view(static_cast<char*>(slice.mem_), slice.len_));
970 62379 : if (!codec_callback_status_.ok()) {
971 5 : return codec_callback_status_;
972 5 : }
973 : // This error is returned when nghttp2 library detected a frame flood by one of its
974 : // internal mechanisms. Most flood protection is done by Envoy's codec and this error
975 : // should never be returned. However it is handled here in case nghttp2 has some flood
976 : // protections that Envoy's codec does not have.
977 62374 : if (rc == NGHTTP2_ERR_FLOODED) {
978 0 : return bufferFloodError(
979 0 : "Flooding was detected in this HTTP/2 session, and it must be closed");
980 0 : }
981 62374 : if (rc != static_cast<ssize_t>(slice.len_)) {
982 226 : return codecProtocolError(nghttp2_strerror(rc));
983 226 : }
984 :
985 62148 : current_slice_ = nullptr;
986 62148 : dispatching_ = false;
987 62148 : current_stream_id_.reset();
988 62148 : }
989 :
990 44981 : ENVOY_CONN_LOG(trace, "dispatched {} bytes", connection_, data.length());
991 44981 : data.drain(data.length());
992 :
993 : // Decoding incoming frames can generate outbound frames so flush pending.
994 44981 : return sendPendingFrames();
995 45212 : }
996 :
997 0 : const ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) const {
998 : // Delegate to the non-const version.
999 0 : return const_cast<ConnectionImpl*>(this)->getStream(stream_id);
1000 0 : }
1001 :
1002 38904 : ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) {
1003 38904 : StreamImpl* stream = getStreamUnchecked(stream_id);
1004 38904 : SLOW_ASSERT(stream != nullptr || !slowContainsStreamId(stream_id));
1005 38904 : return stream;
1006 38904 : }
1007 :
1008 0 : const ConnectionImpl::StreamImpl* ConnectionImpl::getStreamUnchecked(int32_t stream_id) const {
1009 : // Delegate to the non-const version.
1010 0 : return const_cast<ConnectionImpl*>(this)->getStreamUnchecked(stream_id);
1011 0 : }
1012 :
1013 158583 : ConnectionImpl::StreamImpl* ConnectionImpl::getStreamUnchecked(int32_t stream_id) {
1014 158583 : return static_cast<StreamImpl*>(adapter_->GetStreamUserData(stream_id));
1015 158583 : }
1016 :
1017 38874 : int ConnectionImpl::onData(int32_t stream_id, const uint8_t* data, size_t len) {
1018 38874 : ASSERT(connection_.state() == Network::Connection::State::Open);
1019 38874 : StreamImpl* stream = getStream(stream_id);
1020 : // If this results in buffering too much data, the watermark buffer will call
1021 : // pendingRecvBufferHighWatermark, resulting in ++read_disable_count_
1022 38874 : stream->pending_recv_data_->add(data, len);
1023 : // Update the window to the peer unless some consumer of this stream's data has hit a flow control
1024 : // limit and disabled reads on this stream
1025 38876 : if (stream->shouldAllowPeerAdditionalStreamWindow()) {
1026 38856 : adapter_->MarkDataConsumedForStream(stream_id, len);
1027 38362 : } else {
1028 20 : stream->unconsumed_bytes_ += len;
1029 20 : }
1030 38874 : return 0;
1031 38874 : }
1032 :
1033 94 : void ConnectionImpl::goAway() {
1034 94 : adapter_->SubmitGoAway(adapter_->GetHighestReceivedStreamId(),
1035 94 : http2::adapter::Http2ErrorCode::HTTP2_NO_ERROR, "");
1036 94 : stats_.goaway_sent_.inc();
1037 94 : if (sendPendingFramesAndHandleError()) {
1038 : // Intended to check through coverage that this error case is tested
1039 0 : return;
1040 0 : }
1041 94 : }
1042 :
1043 0 : void ConnectionImpl::shutdownNotice() {
1044 0 : adapter_->SubmitShutdownNotice();
1045 :
1046 0 : if (sendPendingFramesAndHandleError()) {
1047 : // Intended to check through coverage that this error case is tested
1048 0 : return;
1049 0 : }
1050 0 : }
1051 :
1052 0 : Status ConnectionImpl::protocolErrorForTest() {
1053 0 : adapter_->SubmitGoAway(adapter_->GetHighestReceivedStreamId(),
1054 0 : http2::adapter::Http2ErrorCode::PROTOCOL_ERROR, "");
1055 :
1056 0 : return sendPendingFrames();
1057 0 : }
1058 :
1059 : Status ConnectionImpl::onBeforeFrameReceived(int32_t stream_id, size_t length, uint8_t type,
1060 45183 : uint8_t flags) {
1061 45183 : ENVOY_CONN_LOG(trace, "about to recv frame type={}, flags={}, stream_id={}", connection_,
1062 45183 : static_cast<uint64_t>(type), static_cast<uint64_t>(flags), stream_id);
1063 45183 : ASSERT(connection_.state() == Network::Connection::State::Open);
1064 :
1065 45183 : current_stream_id_ = stream_id;
1066 : // Track all the frames without padding here, since this is the only callback we receive
1067 : // for some of them (e.g. CONTINUATION frame, frames sent on closed streams, etc.).
1068 : // HEADERS frame is tracked in onBeginHeaders(), DATA frame is tracked in onFrameReceived().
1069 45183 : auto status = okStatus();
1070 45183 : if (type != NGHTTP2_HEADERS && type != NGHTTP2_DATA) {
1071 20974 : status = trackInboundFrames(stream_id, length, type, flags, 0);
1072 20974 : }
1073 :
1074 45183 : return status;
1075 45183 : }
1076 :
1077 : ABSL_MUST_USE_RESULT
1078 15 : enum GoAwayErrorCode ngHttp2ErrorCodeToErrorCode(uint32_t code) noexcept {
1079 15 : switch (code) {
1080 10 : case NGHTTP2_NO_ERROR:
1081 10 : return GoAwayErrorCode::NoError;
1082 5 : default:
1083 5 : return GoAwayErrorCode::Other;
1084 15 : }
1085 15 : }
1086 :
1087 42685 : Status ConnectionImpl::onFrameReceived(const nghttp2_frame* frame) {
1088 42685 : ENVOY_CONN_LOG(trace, "recv frame type={}", connection_, static_cast<uint64_t>(frame->hd.type));
1089 42685 : ASSERT(connection_.state() == Network::Connection::State::Open);
1090 :
1091 : // onFrameReceived() is called with a complete HEADERS frame assembled from all the HEADERS
1092 : // and CONTINUATION frames, but we track them separately: HEADERS frames in onBeginHeaders()
1093 : // and CONTINUATION frames in onBeforeFrameReceived().
1094 42685 : ASSERT(frame->hd.type != NGHTTP2_CONTINUATION);
1095 :
1096 42685 : if ((frame->hd.type == NGHTTP2_PING) && (frame->ping.hd.flags & NGHTTP2_FLAG_ACK)) {
1097 : // The ``opaque_data`` should be exactly what was sent in the ping, which is
1098 : // was the current time when the ping was sent. This can be useful while debugging
1099 : // to match the ping and ack.
1100 0 : uint64_t data;
1101 0 : safeMemcpy(&data, &(frame->ping.opaque_data));
1102 0 : ENVOY_CONN_LOG(trace, "recv PING ACK {}", connection_, data);
1103 :
1104 0 : onKeepaliveResponse();
1105 0 : return okStatus();
1106 0 : }
1107 :
1108 : // In slow networks, HOL blocking can prevent the ping response from coming in a reasonable
1109 : // amount of time. To avoid HOL blocking influence, if we receive *any* frame extend the
1110 : // timeout for another timeout period. This will still timeout the connection if there is no
1111 : // activity, but if there is frame activity we assume the connection is still healthy and the
1112 : // PING ACK may be delayed behind other frames.
1113 42685 : if (keepalive_timeout_timer_ != nullptr && keepalive_timeout_timer_->enabled()) {
1114 0 : keepalive_timeout_timer_->enableTimer(keepalive_timeout_);
1115 0 : }
1116 :
1117 42685 : if (frame->hd.type == NGHTTP2_DATA) {
1118 22182 : RETURN_IF_ERROR(trackInboundFrames(frame->hd.stream_id, frame->hd.length, frame->hd.type,
1119 22182 : frame->hd.flags, frame->data.padlen));
1120 22182 : }
1121 :
1122 : // Only raise GOAWAY once, since we don't currently expose stream information. Shutdown
1123 : // notifications are the same as a normal GOAWAY.
1124 : // TODO: handle multiple GOAWAY frames.
1125 42685 : if (frame->hd.type == NGHTTP2_GOAWAY && !raised_goaway_) {
1126 15 : ASSERT(frame->hd.stream_id == 0);
1127 15 : raised_goaway_ = true;
1128 15 : callbacks().onGoAway(ngHttp2ErrorCodeToErrorCode(frame->goaway.error_code));
1129 15 : return okStatus();
1130 15 : }
1131 :
1132 42670 : if (frame->hd.type == NGHTTP2_SETTINGS && frame->hd.flags == NGHTTP2_FLAG_NONE) {
1133 2872 : onSettings(frame->settings);
1134 2872 : }
1135 :
1136 42670 : StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id);
1137 42670 : if (!stream) {
1138 12615 : return okStatus();
1139 12615 : }
1140 :
1141 : // Track bytes sent and received.
1142 30057 : if (frame->hd.type != METADATA_FRAME_TYPE) {
1143 30016 : stream->bytes_meter_->addWireBytesReceived(frame->hd.length + H2_FRAME_HEADER_SIZE);
1144 30016 : }
1145 30055 : if (frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_CONTINUATION) {
1146 515 : stream->bytes_meter_->addHeaderBytesReceived(frame->hd.length + H2_FRAME_HEADER_SIZE);
1147 515 : }
1148 :
1149 30055 : switch (frame->hd.type) {
1150 515 : case NGHTTP2_HEADERS: {
1151 515 : stream->remote_end_stream_ = frame->hd.flags & NGHTTP2_FLAG_END_STREAM;
1152 515 : if (!stream->cookies_.empty()) {
1153 18 : HeaderString key(Headers::get().Cookie);
1154 18 : stream->headers().addViaMove(std::move(key), std::move(stream->cookies_));
1155 18 : }
1156 :
1157 515 : switch (frame->headers.cat) {
1158 179 : case NGHTTP2_HCAT_RESPONSE:
1159 491 : case NGHTTP2_HCAT_REQUEST: {
1160 491 : stream->decodeHeaders();
1161 491 : break;
1162 179 : }
1163 :
1164 24 : case NGHTTP2_HCAT_HEADERS: {
1165 : // It's possible that we are waiting to send a deferred reset, so only raise headers/trailers
1166 : // if local is not complete.
1167 24 : if (!stream->deferred_reset_) {
1168 24 : if (adapter_->IsServerSession() || stream->received_noninformational_headers_) {
1169 20 : ASSERT(stream->remote_end_stream_);
1170 20 : stream->decodeTrailers();
1171 20 : } else {
1172 : // We're a client session and still waiting for non-informational headers.
1173 4 : stream->decodeHeaders();
1174 4 : }
1175 24 : }
1176 24 : break;
1177 179 : }
1178 :
1179 0 : default:
1180 : // We do not currently support push.
1181 0 : ENVOY_BUG(false, "push not supported");
1182 515 : }
1183 :
1184 515 : break;
1185 515 : }
1186 22356 : case NGHTTP2_DATA: {
1187 22180 : stream->remote_end_stream_ = frame->hd.flags & NGHTTP2_FLAG_END_STREAM;
1188 22180 : stream->decodeData();
1189 22180 : break;
1190 515 : }
1191 5 : case NGHTTP2_RST_STREAM: {
1192 5 : ENVOY_CONN_LOG(trace, "remote reset: {}", connection_, frame->rst_stream.error_code);
1193 5 : stream->remote_rst_ = true;
1194 5 : stats_.rx_reset_.inc();
1195 5 : break;
1196 515 : }
1197 30055 : }
1198 :
1199 30058 : return okStatus();
1200 30055 : }
1201 :
1202 : int ConnectionImpl::onFrameSend(int32_t stream_id, size_t length, uint8_t type, uint8_t flags,
1203 47039 : uint32_t error_code) {
1204 : // The codec library does not cleanly give us a way to determine whether we received invalid
1205 : // data from our peer. Sometimes it raises the invalid frame callback, and sometimes it does not.
1206 : // In all cases however it will attempt to send a GOAWAY frame with an error status. If we see
1207 : // an outgoing frame of this type, we will return an error code so that we can abort execution.
1208 47039 : ENVOY_CONN_LOG(trace, "sent frame type={}, stream_id={}, length={}", connection_,
1209 47039 : static_cast<uint64_t>(type), stream_id, length);
1210 47039 : StreamImpl* stream = getStreamUnchecked(stream_id);
1211 47039 : if (stream != nullptr) {
1212 30343 : if (type != METADATA_FRAME_TYPE) {
1213 30335 : stream->bytes_meter_->addWireBytesSent(length + H2_FRAME_HEADER_SIZE);
1214 30335 : }
1215 30343 : if (type == NGHTTP2_HEADERS || type == NGHTTP2_CONTINUATION) {
1216 671 : stream->bytes_meter_->addHeaderBytesSent(length + H2_FRAME_HEADER_SIZE);
1217 671 : }
1218 30343 : }
1219 47039 : switch (type) {
1220 1982 : case NGHTTP2_GOAWAY: {
1221 1982 : ENVOY_CONN_LOG(debug, "sent goaway code={}", connection_, error_code);
1222 1982 : if (error_code != NGHTTP2_NO_ERROR) {
1223 : // TODO(mattklein123): Returning this error code abandons standard nghttp2 frame accounting.
1224 : // As such, it is not reliable to call sendPendingFrames() again after this and we assume
1225 : // that the connection is going to get torn down immediately. One byproduct of this is that
1226 : // we need to cancel all pending flush stream timeouts since they can race with connection
1227 : // teardown. As part of the work to remove exceptions we should aim to clean up all of this
1228 : // error handling logic and only handle this type of case at the end of dispatch.
1229 1888 : for (auto& stream : active_streams_) {
1230 1164 : stream->disarmStreamIdleTimer();
1231 1164 : }
1232 1888 : return NGHTTP2_ERR_CALLBACK_FAILURE;
1233 1888 : }
1234 94 : break;
1235 1982 : }
1236 :
1237 149 : case NGHTTP2_RST_STREAM: {
1238 84 : ENVOY_CONN_LOG(debug, "sent reset code={}", connection_, error_code);
1239 84 : stats_.tx_reset_.inc();
1240 84 : break;
1241 1982 : }
1242 :
1243 671 : case NGHTTP2_HEADERS:
1244 23005 : case NGHTTP2_DATA: {
1245 : // This should be the case since we're sending these frames. It's possible
1246 : // that codec fuzzers would incorrectly send frames for non-existent streams
1247 : // which is why this is not an assert.
1248 23005 : if (stream != nullptr) {
1249 23005 : const bool end_stream_sent = flags & NGHTTP2_FLAG_END_STREAM;
1250 23005 : stream->local_end_stream_sent_ = end_stream_sent;
1251 23005 : if (end_stream_sent) {
1252 467 : stream->onEndStreamEncoded();
1253 467 : }
1254 23005 : }
1255 23005 : break;
1256 671 : }
1257 47039 : }
1258 :
1259 45151 : return 0;
1260 47039 : }
1261 :
1262 16 : int ConnectionImpl::onError(absl::string_view error) {
1263 16 : ENVOY_CONN_LOG(debug, "invalid http2: {}", connection_, error);
1264 16 : return 0;
1265 16 : }
1266 :
1267 182 : int ConnectionImpl::onInvalidFrame(int32_t stream_id, int error_code) {
1268 182 : ENVOY_CONN_LOG(debug, "invalid frame: {} on stream {}", connection_, nghttp2_strerror(error_code),
1269 182 : stream_id);
1270 :
1271 : // Set details of error_code in the stream whenever we have one.
1272 182 : StreamImpl* stream = getStreamUnchecked(stream_id);
1273 182 : if (stream != nullptr) {
1274 128 : stream->setDetails(Http2ResponseCodeDetails::get().errorDetails(error_code));
1275 128 : }
1276 :
1277 182 : switch (error_code) {
1278 0 : case NGHTTP2_ERR_REFUSED_STREAM:
1279 :
1280 0 : stats_.stream_refused_errors_.inc();
1281 0 : return 0;
1282 :
1283 61 : case NGHTTP2_ERR_HTTP_HEADER:
1284 128 : case NGHTTP2_ERR_HTTP_MESSAGING:
1285 128 : stats_.rx_messaging_error_.inc();
1286 128 : if (stream_error_on_invalid_http_messaging_) {
1287 : // The stream is about to be closed due to an invalid header or messaging. Don't kill the
1288 : // entire connection if one stream has bad headers or messaging.
1289 0 : if (stream != nullptr) {
1290 : // See comment below in onStreamClose() for why we do this.
1291 0 : stream->reset_due_to_messaging_error_ = true;
1292 0 : }
1293 0 : return 0;
1294 0 : }
1295 128 : break;
1296 :
1297 128 : case NGHTTP2_ERR_FLOW_CONTROL:
1298 54 : case NGHTTP2_ERR_PROTO:
1299 54 : case NGHTTP2_ERR_STREAM_CLOSED:
1300 : // Known error conditions that should trigger connection close.
1301 54 : break;
1302 :
1303 0 : default:
1304 : // Unknown error conditions. Trigger ENVOY_BUG and connection close.
1305 0 : ENVOY_BUG(false, absl::StrCat("Unexpected error_code: ", error_code));
1306 0 : break;
1307 182 : }
1308 :
1309 : // Cause dispatch to return with an error code.
1310 182 : return NGHTTP2_ERR_CALLBACK_FAILURE;
1311 182 : }
1312 :
1313 : int ConnectionImpl::onBeforeFrameSend(int32_t /*stream_id*/, size_t /*length*/, uint8_t type,
1314 24705 : uint8_t flags) {
1315 24705 : ENVOY_CONN_LOG(trace, "about to send frame type={}, flags={}", connection_,
1316 24705 : static_cast<uint64_t>(type), static_cast<uint64_t>(flags));
1317 24705 : ASSERT(!is_outbound_flood_monitored_control_frame_);
1318 : // Flag flood monitored outbound control frames.
1319 24705 : is_outbound_flood_monitored_control_frame_ =
1320 24705 : ((type == NGHTTP2_PING || type == NGHTTP2_SETTINGS) && flags & NGHTTP2_FLAG_ACK) ||
1321 24705 : type == NGHTTP2_RST_STREAM;
1322 24705 : return 0;
1323 24705 : }
1324 :
1325 : void ConnectionImpl::addOutboundFrameFragment(Buffer::OwnedImpl& output, const uint8_t* data,
1326 47392 : size_t length) {
1327 : // Reset the outbound frame type (set in the onBeforeFrameSend callback) since the
1328 : // onBeforeFrameSend callback is not called for DATA frames.
1329 47392 : bool is_outbound_flood_monitored_control_frame = false;
1330 47392 : std::swap(is_outbound_flood_monitored_control_frame, is_outbound_flood_monitored_control_frame_);
1331 47392 : auto releasor =
1332 47392 : protocol_constraints_.incrementOutboundFrameCount(is_outbound_flood_monitored_control_frame);
1333 47392 : output.add(data, length);
1334 47392 : output.addDrainTracker(releasor);
1335 47392 : }
1336 :
1337 25058 : ssize_t ConnectionImpl::onSend(const uint8_t* data, size_t length) {
1338 25058 : ENVOY_CONN_LOG(trace, "send data: bytes={}", connection_, length);
1339 25058 : Buffer::OwnedImpl buffer;
1340 25058 : addOutboundFrameFragment(buffer, data, length);
1341 :
1342 : // While the buffer is transient the fragment it contains will be moved into the
1343 : // write_buffer_ of the underlying connection_ by the write method below.
1344 : // This creates lifetime dependency between the write_buffer_ of the underlying connection
1345 : // and the codec object. Specifically the write_buffer_ MUST be either fully drained or
1346 : // deleted before the codec object is deleted. This is presently guaranteed by the
1347 : // destruction order of the Network::ConnectionImpl object where write_buffer_ is
1348 : // destroyed before the filter_manager_ which owns the codec through Http::ConnectionManagerImpl.
1349 25058 : connection_.write(buffer, false);
1350 25058 : return length;
1351 25058 : }
1352 :
1353 393 : Status ConnectionImpl::onStreamClose(StreamImpl* stream, uint32_t error_code) {
1354 393 : if (stream) {
1355 381 : const int32_t stream_id = stream->stream_id_;
1356 :
1357 : // Consume buffered on stream_close.
1358 381 : if (stream->stream_manager_.buffered_on_stream_close_) {
1359 16 : stream->stream_manager_.buffered_on_stream_close_ = false;
1360 16 : stats_.deferred_stream_close_.dec();
1361 16 : }
1362 :
1363 381 : ENVOY_CONN_LOG(debug, "stream {} closed: {}", connection_, stream_id, error_code);
1364 :
1365 : // Even if we have received both the remote_end_stream and the
1366 : // local_end_stream (e.g. we have all the data for the response), if we've
1367 : // received a remote reset we should reset the stream.
1368 : // We only do so currently for server side streams by checking for
1369 : // extend_stream_lifetime_flag_ as its observers all unregisters stream
1370 : // callbacks.
1371 381 : bool should_reset_stream = !stream->remote_end_stream_ || !stream->local_end_stream_;
1372 381 : if (stream->extend_stream_lifetime_flag_) {
1373 153 : should_reset_stream = should_reset_stream || stream->remote_rst_;
1374 153 : }
1375 :
1376 381 : if (should_reset_stream) {
1377 72 : StreamResetReason reason;
1378 72 : if (stream->reset_due_to_messaging_error_) {
1379 : // Unfortunately, the nghttp2 API makes it incredibly difficult to clearly understand
1380 : // the flow of resets. I.e., did the reset originate locally? Was it remote? Here,
1381 : // we attempt to track cases in which we sent a reset locally due to an invalid frame
1382 : // received from the remote. We only do that in two cases currently (HTTP messaging layer
1383 : // errors from https://tools.ietf.org/html/rfc7540#section-8 which nghttp2 is very strict
1384 : // about). In other cases we treat invalid frames as a protocol error and just kill
1385 : // the connection.
1386 :
1387 : // Get ClientConnectionImpl or ServerConnectionImpl specific stream reset reason,
1388 : // depending whether the connection is upstream or downstream.
1389 0 : reason = getMessagingErrorResetReason();
1390 72 : } else {
1391 72 : if (error_code == NGHTTP2_REFUSED_STREAM) {
1392 6 : reason = StreamResetReason::RemoteRefusedStreamReset;
1393 6 : stream->setDetails(Http2ResponseCodeDetails::get().remote_refused);
1394 66 : } else {
1395 66 : if (error_code == NGHTTP2_CONNECT_ERROR) {
1396 0 : reason = StreamResetReason::ConnectError;
1397 66 : } else {
1398 66 : reason = StreamResetReason::RemoteReset;
1399 66 : }
1400 66 : stream->setDetails(Http2ResponseCodeDetails::get().remote_reset);
1401 66 : }
1402 72 : }
1403 :
1404 72 : stream->runResetCallbacks(reason);
1405 :
1406 319 : } else if (stream->defer_processing_backedup_streams_ && !stream->reset_reason_.has_value() &&
1407 309 : stream->stream_manager_.hasBufferedBodyOrTrailers()) {
1408 20 : ASSERT(error_code == NGHTTP2_NO_ERROR);
1409 20 : ENVOY_CONN_LOG(debug, "buffered onStreamClose for stream: {}", connection_, stream_id);
1410 : // Buffer the call, rely on the stream->process_buffered_data_callback_
1411 : // to end up invoking.
1412 20 : stream->stream_manager_.buffered_on_stream_close_ = true;
1413 20 : stats_.deferred_stream_close_.inc();
1414 20 : return okStatus();
1415 20 : }
1416 :
1417 361 : stream->destroy();
1418 361 : current_stream_id_.reset();
1419 : // TODO(antoniovicente) Test coverage for onCloseStream before deferred reset handling happens.
1420 361 : pending_deferred_reset_streams_.erase(stream->stream_id_);
1421 :
1422 361 : connection_.dispatcher().deferredDelete(stream->removeFromList(active_streams_));
1423 : // Any unconsumed data must be consumed before the stream is deleted.
1424 : // nghttp2 does not appear to track this internally, and any stream deleted
1425 : // with outstanding window will contribute to a slow connection-window leak.
1426 361 : ENVOY_CONN_LOG(debug, "Recouping {} bytes of flow control window for stream {}.", connection_,
1427 361 : stream->unconsumed_bytes_, stream_id);
1428 361 : adapter_->MarkDataConsumedForStream(stream_id, stream->unconsumed_bytes_);
1429 361 : stream->unconsumed_bytes_ = 0;
1430 361 : adapter_->SetStreamUserData(stream->stream_id_, nullptr);
1431 361 : }
1432 :
1433 373 : return okStatus();
1434 393 : }
1435 :
1436 377 : Status ConnectionImpl::onStreamClose(int32_t stream_id, uint32_t error_code) {
1437 377 : return onStreamClose(getStreamUnchecked(stream_id), error_code);
1438 377 : }
1439 :
1440 318 : int ConnectionImpl::onMetadataReceived(int32_t stream_id, const uint8_t* data, size_t len) {
1441 318 : ENVOY_CONN_LOG(trace, "recv {} bytes METADATA", connection_, len);
1442 :
1443 318 : StreamImpl* stream = getStreamUnchecked(stream_id);
1444 318 : if (!stream || stream->remote_end_stream_) {
1445 274 : if (!stream) {
1446 159 : ENVOY_CONN_LOG(debug, "no stream for stream_id {} while receiving METADATA", connection_,
1447 159 : stream_id);
1448 159 : }
1449 274 : return 0;
1450 274 : }
1451 :
1452 44 : bool success = stream->getMetadataDecoder().receiveMetadata(data, len);
1453 44 : return success ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
1454 318 : }
1455 :
1456 76 : int ConnectionImpl::onMetadataFrameComplete(int32_t stream_id, bool end_metadata) {
1457 76 : ENVOY_CONN_LOG(trace, "recv METADATA frame on stream {}, end_metadata: {}", connection_,
1458 76 : stream_id, end_metadata);
1459 :
1460 76 : StreamImpl* stream = getStreamUnchecked(stream_id);
1461 76 : if (!stream || stream->remote_end_stream_) {
1462 70 : if (!stream) {
1463 35 : ENVOY_CONN_LOG(debug, "no stream for stream_id {} while completing METADATA", connection_,
1464 35 : stream_id);
1465 35 : }
1466 70 : return 0;
1467 70 : }
1468 :
1469 6 : bool result = stream->getMetadataDecoder().onMetadataFrameComplete(end_metadata);
1470 6 : return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
1471 76 : }
1472 :
1473 : int ConnectionImpl::saveHeader(const nghttp2_frame* frame, HeaderString&& name,
1474 24931 : HeaderString&& value) {
1475 24931 : StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id);
1476 24931 : if (!stream) {
1477 : // We have seen 1 or 2 crashes where we get a headers callback but there is no associated
1478 : // stream data. I honestly am not sure how this can happen. However, from reading the nghttp2
1479 : // code it looks possible that inflate_header_block() can safely inflate headers for an already
1480 : // closed stream, but will still call the headers callback. Since that seems possible, we should
1481 : // ignore this case here.
1482 : // TODO(mattklein123): Figure out a test case that can hit this.
1483 0 : stats_.headers_cb_no_stream_.inc();
1484 0 : return 0;
1485 0 : }
1486 :
1487 : // TODO(10646): Switch to use HeaderUtility::checkHeaderNameForUnderscores().
1488 24931 : auto should_return = checkHeaderNameForUnderscores(name.getStringView());
1489 24931 : if (should_return) {
1490 0 : stream->setDetails(Http2ResponseCodeDetails::get().invalid_underscore);
1491 0 : name.clear();
1492 0 : value.clear();
1493 0 : return should_return.value();
1494 0 : }
1495 :
1496 24931 : stream->saveHeader(std::move(name), std::move(value));
1497 :
1498 24931 : if (stream->headers().byteSize() > max_headers_kb_ * 1024 ||
1499 24931 : stream->headers().size() > max_headers_count_) {
1500 6 : stream->setDetails(Http2ResponseCodeDetails::get().too_many_headers);
1501 6 : stats_.header_overflow_.inc();
1502 : // This will cause the library to reset/close the stream.
1503 6 : return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1504 24925 : } else {
1505 24925 : return 0;
1506 24925 : }
1507 24931 : }
1508 :
1509 47467 : Status ConnectionImpl::sendPendingFrames() {
1510 47467 : if (dispatching_ || connection_.state() == Network::Connection::State::Closed) {
1511 635 : return okStatus();
1512 635 : }
1513 :
1514 46832 : const int rc = adapter_->Send();
1515 46832 : if (rc != 0) {
1516 1888 : ASSERT(rc == NGHTTP2_ERR_CALLBACK_FAILURE);
1517 1888 : return codecProtocolError(nghttp2_strerror(rc));
1518 1888 : }
1519 :
1520 : // See ConnectionImpl::StreamImpl::resetStream() for why we do this. This is an uncommon event,
1521 : // so iterating through every stream to find the ones that have a deferred reset is not a big
1522 : // deal. Furthermore, queueing a reset frame does not actually invoke the close stream callback.
1523 : // This is only done when the reset frame is sent. Thus, it's safe to work directly with the
1524 : // stream map.
1525 : // NOTE: The way we handle deferred reset is essentially best effort. If we intend to do a
1526 : // deferred reset, we try to finish the stream, including writing any pending data frames.
1527 : // If we cannot do this (potentially due to not enough window), we just reset the stream.
1528 : // In general this behavior occurs only when we are trying to send immediate error messages
1529 : // to short circuit requests. In the best effort case, we complete the stream before
1530 : // resetting. In other cases, we just do the reset now which will blow away pending data
1531 : // frames and release any memory associated with the stream.
1532 44944 : if (!pending_deferred_reset_streams_.empty()) {
1533 0 : while (!pending_deferred_reset_streams_.empty()) {
1534 0 : auto it = pending_deferred_reset_streams_.begin();
1535 0 : auto* stream = it->second;
1536 : // Sanity check: the stream's id matches the map key.
1537 0 : ASSERT(it->first == stream->stream_id_);
1538 0 : pending_deferred_reset_streams_.erase(it);
1539 0 : ASSERT(stream->deferred_reset_);
1540 0 : stream->resetStreamWorker(stream->deferred_reset_.value());
1541 0 : }
1542 0 : RETURN_IF_ERROR(sendPendingFrames());
1543 0 : }
1544 :
1545 : // After all pending frames have been written into the outbound buffer check if any of
1546 : // protocol constraints had been violated.
1547 44944 : Status status = protocol_constraints_.checkOutboundFrameLimits();
1548 44944 : if (!status.ok()) {
1549 0 : ENVOY_CONN_LOG(debug, "error sending frames: Too many frames in the outbound queue.",
1550 0 : connection_);
1551 0 : }
1552 44944 : return status;
1553 44944 : }
1554 :
1555 2487 : bool ConnectionImpl::sendPendingFramesAndHandleError() {
1556 2487 : if (!sendPendingFrames().ok()) {
1557 0 : scheduleProtocolConstraintViolationCallback();
1558 0 : return true;
1559 0 : }
1560 2487 : return false;
1561 2487 : }
1562 :
1563 : void ConnectionImpl::sendSettingsHelper(
1564 2860 : const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) {
1565 2860 : absl::InlinedVector<http2::adapter::Http2Setting, 10> settings;
1566 2860 : auto insertParameter = [&settings](const http2::adapter::Http2Setting& entry) mutable -> bool {
1567 : // Consider using a set as an intermediate data structure, rather than this ad-hoc
1568 : // deduplication.
1569 0 : const auto it = std::find_if(
1570 0 : settings.cbegin(), settings.cend(),
1571 0 : [&entry](const http2::adapter::Http2Setting& existing) { return entry.id == existing.id; });
1572 0 : if (it != settings.end()) {
1573 0 : return false;
1574 0 : }
1575 0 : settings.push_back(entry);
1576 0 : return true;
1577 0 : };
1578 :
1579 : // Universally disable receiving push promise frames as we don't currently
1580 : // support them. nghttp2 will fail the connection if the other side still
1581 : // sends them.
1582 : // TODO(mattklein123): Remove this when we correctly proxy push promise.
1583 : // NOTE: This is a special case with respect to custom parameter overrides in
1584 : // that server push is not supported and therefore not end user configurable.
1585 2860 : if (disable_push) {
1586 361 : settings.push_back({static_cast<int32_t>(http2::adapter::ENABLE_PUSH), disable_push ? 0U : 1U});
1587 361 : }
1588 :
1589 2860 : for (const auto& it : http2_options.custom_settings_parameters()) {
1590 0 : ASSERT(it.identifier().value() <= std::numeric_limits<uint16_t>::max());
1591 0 : const bool result =
1592 0 : insertParameter({static_cast<http2::adapter::Http2SettingsId>(it.identifier().value()),
1593 0 : it.value().value()});
1594 0 : ASSERT(result);
1595 0 : ENVOY_CONN_LOG(debug, "adding custom settings parameter with id {:#x} to {}", connection_,
1596 0 : it.identifier().value(), it.value().value());
1597 0 : }
1598 :
1599 : // Insert named parameters.
1600 2860 : settings.insert(
1601 2860 : settings.end(),
1602 2860 : {{http2::adapter::HEADER_TABLE_SIZE, http2_options.hpack_table_size().value()},
1603 2860 : {http2::adapter::ENABLE_CONNECT_PROTOCOL, http2_options.allow_connect()},
1604 2860 : {http2::adapter::MAX_CONCURRENT_STREAMS, http2_options.max_concurrent_streams().value()},
1605 2860 : {http2::adapter::INITIAL_WINDOW_SIZE, http2_options.initial_stream_window_size().value()}});
1606 2860 : adapter_->SubmitSettings(settings);
1607 2860 : }
1608 :
1609 : void ConnectionImpl::sendSettings(
1610 2860 : const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) {
1611 2860 : sendSettingsHelper(http2_options, disable_push);
1612 :
1613 2860 : const uint32_t initial_connection_window_size =
1614 2860 : http2_options.initial_connection_window_size().value();
1615 : // Increase connection window size up to our default size.
1616 2860 : if (initial_connection_window_size != NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE) {
1617 2636 : ENVOY_CONN_LOG(debug, "updating connection-level initial window size to {}", connection_,
1618 2636 : initial_connection_window_size);
1619 2636 : adapter_->SubmitWindowUpdate(0, initial_connection_window_size -
1620 2636 : NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE);
1621 2636 : }
1622 2860 : }
1623 :
1624 90018 : int ConnectionImpl::setAndCheckCodecCallbackStatus(Status&& status) {
1625 : // Keep the error status that caused the original failure. Subsequent
1626 : // error statuses are silently discarded.
1627 90018 : codec_callback_status_.Update(std::move(status));
1628 90018 : if (codec_callback_status_.ok() && connection_.state() != Network::Connection::State::Open) {
1629 5 : codec_callback_status_ = codecProtocolError("Connection was closed while dispatching frames");
1630 5 : }
1631 :
1632 90018 : return codec_callback_status_.ok() ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
1633 90018 : }
1634 :
1635 0 : void ConnectionImpl::scheduleProtocolConstraintViolationCallback() {
1636 0 : if (!protocol_constraint_violation_callback_) {
1637 0 : protocol_constraint_violation_callback_ = connection_.dispatcher().createSchedulableCallback(
1638 0 : [this]() { onProtocolConstraintViolation(); });
1639 0 : protocol_constraint_violation_callback_->scheduleCallbackCurrentIteration();
1640 0 : }
1641 0 : }
1642 :
1643 0 : void ConnectionImpl::onProtocolConstraintViolation() {
1644 : // Flooded outbound queue implies that peer is not reading and it does not
1645 : // make sense to try to flush pending bytes.
1646 0 : connection_.close(Envoy::Network::ConnectionCloseType::NoFlush,
1647 0 : StreamInfo::LocalCloseReasons::get().Http2ConnectionProtocolViolation);
1648 0 : }
1649 :
1650 0 : void ConnectionImpl::onUnderlyingConnectionBelowWriteBufferLowWatermark() {
1651 0 : if (Runtime::runtimeFeatureEnabled(Runtime::defer_processing_backedup_streams)) {
1652 : // Notify the streams based on least recently encoding to the connection.
1653 0 : for (auto it = active_streams_.rbegin(); it != active_streams_.rend(); ++it) {
1654 0 : (*it)->runLowWatermarkCallbacks();
1655 0 : }
1656 0 : } else {
1657 0 : for (auto& stream : active_streams_) {
1658 0 : stream->runLowWatermarkCallbacks();
1659 0 : }
1660 0 : }
1661 0 : }
1662 :
1663 69 : ConnectionImpl::Http2Callbacks::Http2Callbacks() {
1664 69 : nghttp2_session_callbacks_new(&callbacks_);
1665 69 : nghttp2_session_callbacks_set_send_callback(
1666 69 : callbacks_,
1667 25116 : [](nghttp2_session*, const uint8_t* data, size_t length, int, void* user_data) -> ssize_t {
1668 25058 : return static_cast<ConnectionImpl*>(user_data)->onSend(data, length);
1669 25058 : });
1670 :
1671 69 : nghttp2_session_callbacks_set_on_begin_headers_callback(
1672 1822 : callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int {
1673 1764 : auto status = static_cast<ConnectionImpl*>(user_data)->onBeginHeaders(frame);
1674 1764 : return static_cast<ConnectionImpl*>(user_data)->setAndCheckCodecCallbackStatus(
1675 1764 : std::move(status));
1676 1764 : });
1677 :
1678 69 : nghttp2_session_callbacks_set_on_header_callback(
1679 69 : callbacks_,
1680 69 : [](nghttp2_session*, const nghttp2_frame* frame, const uint8_t* raw_name, size_t name_length,
1681 24990 : const uint8_t* raw_value, size_t value_length, uint8_t, void* user_data) -> int {
1682 : // TODO PERF: Can reference count here to avoid copies.
1683 24931 : HeaderString name;
1684 24931 : name.setCopy(reinterpret_cast<const char*>(raw_name), name_length);
1685 24931 : HeaderString value;
1686 24931 : value.setCopy(reinterpret_cast<const char*>(raw_value), value_length);
1687 24931 : return static_cast<ConnectionImpl*>(user_data)->onHeader(frame, std::move(name),
1688 24931 : std::move(value));
1689 24931 : });
1690 :
1691 69 : nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
1692 69 : callbacks_,
1693 69 : [](nghttp2_session*, uint8_t, int32_t stream_id, const uint8_t* data, size_t len,
1694 38939 : void* user_data) -> int {
1695 38874 : return static_cast<ConnectionImpl*>(user_data)->onData(stream_id, data, len);
1696 38874 : });
1697 :
1698 69 : nghttp2_session_callbacks_set_on_begin_frame_callback(
1699 45244 : callbacks_, [](nghttp2_session*, const nghttp2_frame_hd* hd, void* user_data) -> int {
1700 45186 : auto status = static_cast<ConnectionImpl*>(user_data)->onBeforeFrameReceived(
1701 45186 : hd->stream_id, hd->length, hd->type, hd->flags);
1702 45186 : return static_cast<ConnectionImpl*>(user_data)->setAndCheckCodecCallbackStatus(
1703 45186 : std::move(status));
1704 45186 : });
1705 :
1706 69 : nghttp2_session_callbacks_set_on_frame_recv_callback(
1707 42743 : callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int {
1708 42685 : auto status = static_cast<ConnectionImpl*>(user_data)->onFrameReceived(frame);
1709 42685 : return static_cast<ConnectionImpl*>(user_data)->setAndCheckCodecCallbackStatus(
1710 42685 : std::move(status));
1711 42685 : });
1712 :
1713 69 : nghttp2_session_callbacks_set_on_stream_close_callback(
1714 69 : callbacks_,
1715 438 : [](nghttp2_session*, int32_t stream_id, uint32_t error_code, void* user_data) -> int {
1716 377 : auto status = static_cast<ConnectionImpl*>(user_data)->onStreamClose(stream_id, error_code);
1717 377 : return static_cast<ConnectionImpl*>(user_data)->setAndCheckCodecCallbackStatus(
1718 377 : std::move(status));
1719 377 : });
1720 :
1721 69 : nghttp2_session_callbacks_set_on_frame_send_callback(
1722 47097 : callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int {
1723 47039 : uint32_t error_code = 0;
1724 47039 : switch (frame->hd.type) {
1725 1982 : case NGHTTP2_GOAWAY:
1726 1982 : error_code = frame->goaway.error_code;
1727 1982 : break;
1728 84 : case NGHTTP2_RST_STREAM:
1729 84 : error_code = frame->rst_stream.error_code;
1730 84 : break;
1731 47039 : }
1732 47039 : return static_cast<ConnectionImpl*>(user_data)->onFrameSend(
1733 47039 : frame->hd.stream_id, frame->hd.length, frame->hd.type, frame->hd.flags, error_code);
1734 47039 : });
1735 :
1736 69 : nghttp2_session_callbacks_set_before_frame_send_callback(
1737 24763 : callbacks_, [](nghttp2_session*, const nghttp2_frame* frame, void* user_data) -> int {
1738 24705 : return static_cast<ConnectionImpl*>(user_data)->onBeforeFrameSend(
1739 24705 : frame->hd.stream_id, frame->hd.length, frame->hd.type, frame->hd.flags);
1740 24705 : });
1741 :
1742 69 : nghttp2_session_callbacks_set_on_frame_not_send_callback(
1743 69 : callbacks_, [](nghttp2_session*, const nghttp2_frame*, int, void*) -> int {
1744 : // We used to always return failure here but it looks now this can get called if the other
1745 : // side sends GOAWAY and we are trying to send a SETTINGS ACK. Just ignore this for now.
1746 0 : return 0;
1747 0 : });
1748 :
1749 69 : nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
1750 69 : callbacks_,
1751 245 : [](nghttp2_session*, const nghttp2_frame* frame, int error_code, void* user_data) -> int {
1752 182 : return static_cast<ConnectionImpl*>(user_data)->onInvalidFrame(frame->hd.stream_id,
1753 182 : error_code);
1754 182 : });
1755 :
1756 69 : nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
1757 69 : callbacks_,
1758 69 : [](nghttp2_session*, const nghttp2_frame_hd* hd, const uint8_t* data, size_t len,
1759 382 : void* user_data) -> int {
1760 318 : ASSERT(hd->length >= len);
1761 318 : return static_cast<ConnectionImpl*>(user_data)->onMetadataReceived(hd->stream_id, data,
1762 318 : len);
1763 318 : });
1764 :
1765 69 : nghttp2_session_callbacks_set_unpack_extension_callback(
1766 140 : callbacks_, [](nghttp2_session*, void**, const nghttp2_frame_hd* hd, void* user_data) -> int {
1767 76 : return static_cast<ConnectionImpl*>(user_data)->onMetadataFrameComplete(
1768 76 : hd->stream_id, hd->flags == END_METADATA_FLAG);
1769 76 : });
1770 :
1771 69 : nghttp2_session_callbacks_set_error_callback2(
1772 83 : callbacks_, [](nghttp2_session*, int, const char* msg, size_t len, void* user_data) -> int {
1773 16 : return static_cast<ConnectionImpl*>(user_data)->onError(absl::string_view(msg, len));
1774 16 : });
1775 69 : }
1776 :
1777 0 : ConnectionImpl::Http2Callbacks::~Http2Callbacks() { nghttp2_session_callbacks_del(callbacks_); }
1778 :
1779 : ConnectionImpl::Http2Options::Http2Options(
1780 2860 : const envoy::config::core::v3::Http2ProtocolOptions& http2_options, uint32_t max_headers_kb) {
1781 2860 : og_options_.perspective = http2::adapter::Perspective::kServer;
1782 2860 : og_options_.max_hpack_encoding_table_capacity = http2_options.hpack_table_size().value();
1783 2860 : og_options_.max_header_list_bytes = max_headers_kb * 1024;
1784 2860 : og_options_.max_header_field_size = max_headers_kb * 1024;
1785 2860 : og_options_.allow_extended_connect = http2_options.allow_connect();
1786 2860 : og_options_.allow_different_host_and_authority = true;
1787 :
1788 : #ifdef ENVOY_ENABLE_UHV
1789 : // UHV - disable header validations in oghttp2
1790 : og_options_.validate_http_headers = false;
1791 : #endif
1792 :
1793 2860 : nghttp2_option_new(&options_);
1794 : // Currently we do not do anything with stream priority. Setting the following option prevents
1795 : // nghttp2 from keeping around closed streams for use during stream priority dependency graph
1796 : // calculations. This saves a tremendous amount of memory in cases where there are a large
1797 : // number of kept alive HTTP/2 connections.
1798 2860 : nghttp2_option_set_no_closed_streams(options_, 1);
1799 2860 : nghttp2_option_set_no_auto_window_update(options_, 1);
1800 :
1801 : // RFC9113 invalidates trailing whitespace in header values but this is a new validation which
1802 : // can break existing deployments.
1803 : // Disable this validation for now.
1804 2860 : nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(options_, 1);
1805 :
1806 : // The max send header block length is configured to an arbitrarily high number so as to never
1807 : // trigger the check within nghttp2, as we check request headers length in
1808 : // codec_impl::saveHeader.
1809 2860 : nghttp2_option_set_max_send_header_block_length(options_, 0x2000000);
1810 :
1811 2860 : if (http2_options.hpack_table_size().value() != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
1812 224 : nghttp2_option_set_max_deflate_dynamic_table_size(options_,
1813 224 : http2_options.hpack_table_size().value());
1814 224 : }
1815 :
1816 2860 : if (http2_options.allow_metadata()) {
1817 812 : nghttp2_option_set_user_recv_extension_type(options_, METADATA_FRAME_TYPE);
1818 2787 : } else {
1819 2048 : ENVOY_LOG(trace, "Codec does not have Metadata frame support.");
1820 2048 : }
1821 :
1822 : // nghttp2 v1.39.2 lowered the internal flood protection limit from 10K to 1K of ACK frames.
1823 : // This new limit may cause the internal nghttp2 mitigation to trigger more often (as it
1824 : // requires just 9K of incoming bytes for smallest 9 byte SETTINGS frame), bypassing the same
1825 : // mitigation and its associated behavior in the envoy HTTP/2 codec. Since envoy does not rely
1826 : // on this mitigation, set back to the old 10K number to avoid any changes in the HTTP/2 codec
1827 : // behavior.
1828 2860 : nghttp2_option_set_max_outbound_ack(options_, 10000);
1829 2860 : }
1830 :
1831 2860 : ConnectionImpl::Http2Options::~Http2Options() { nghttp2_option_del(options_); }
1832 :
1833 : ConnectionImpl::ClientHttp2Options::ClientHttp2Options(
1834 : const envoy::config::core::v3::Http2ProtocolOptions& http2_options, uint32_t max_headers_kb)
1835 361 : : Http2Options(http2_options, max_headers_kb) {
1836 361 : og_options_.perspective = http2::adapter::Perspective::kClient;
1837 361 : og_options_.remote_max_concurrent_streams =
1838 361 : ::Envoy::Http2::Utility::OptionsLimits::DEFAULT_MAX_CONCURRENT_STREAMS;
1839 : // Temporarily disable initial max streams limit/protection, since we might want to create
1840 : // more than 100 streams before receiving the HTTP/2 SETTINGS frame from the server.
1841 : //
1842 : // TODO(PiotrSikora): remove this once multiple upstream connections or queuing are implemented.
1843 361 : nghttp2_option_set_peer_max_concurrent_streams(
1844 361 : options_, ::Envoy::Http2::Utility::OptionsLimits::DEFAULT_MAX_CONCURRENT_STREAMS);
1845 361 : }
1846 :
1847 0 : void ConnectionImpl::dumpState(std::ostream& os, int indent_level) const {
1848 0 : const char* spaces = spacesForLevel(indent_level);
1849 0 : os << spaces << "Http2::ConnectionImpl " << this << DUMP_MEMBER(max_headers_kb_)
1850 0 : << DUMP_MEMBER(max_headers_count_) << DUMP_MEMBER(per_stream_buffer_limit_)
1851 0 : << DUMP_MEMBER(allow_metadata_) << DUMP_MEMBER(stream_error_on_invalid_http_messaging_)
1852 0 : << DUMP_MEMBER(is_outbound_flood_monitored_control_frame_) << DUMP_MEMBER(dispatching_)
1853 0 : << DUMP_MEMBER(raised_goaway_) << DUMP_MEMBER(pending_deferred_reset_streams_.size()) << '\n';
1854 :
1855 : // Dump the protocol constraints
1856 0 : DUMP_DETAILS(&protocol_constraints_);
1857 :
1858 : // Dump either a targeted stream or several of the active streams.
1859 0 : dumpStreams(os, indent_level);
1860 :
1861 : // Dump the active slice
1862 0 : if (current_slice_ == nullptr) {
1863 : // No current slice, use macro for consistent formatting.
1864 0 : os << spaces << "current_slice_: null\n";
1865 0 : } else {
1866 0 : auto slice_view =
1867 0 : absl::string_view(static_cast<const char*>(current_slice_->mem_), current_slice_->len_);
1868 :
1869 0 : os << spaces << "current slice length: " << slice_view.length() << " contents: \"";
1870 0 : StringUtil::escapeToOstream(os, slice_view);
1871 0 : os << "\"\n";
1872 0 : }
1873 0 : }
1874 :
1875 0 : void ConnectionImpl::dumpStreams(std::ostream& os, int indent_level) const {
1876 0 : const char* spaces = spacesForLevel(indent_level);
1877 :
1878 : // Try to dump details for the current stream.
1879 : // If none, dump a subset of our active streams.
1880 0 : os << spaces << "Number of active streams: " << active_streams_.size()
1881 0 : << DUMP_OPTIONAL_MEMBER(current_stream_id_);
1882 :
1883 0 : if (current_stream_id_.has_value()) {
1884 0 : os << " Dumping current stream:\n";
1885 0 : const ConnectionImpl::StreamImpl* stream = getStream(current_stream_id_.value());
1886 0 : DUMP_DETAILS(stream);
1887 0 : } else {
1888 0 : os << " Dumping " << std::min<size_t>(25, active_streams_.size()) << " Active Streams:\n";
1889 0 : size_t count = 0;
1890 0 : for (auto& stream : active_streams_) {
1891 0 : DUMP_DETAILS(stream);
1892 0 : if (++count >= 25) {
1893 0 : break;
1894 0 : }
1895 0 : }
1896 0 : }
1897 0 : }
1898 :
1899 0 : void ClientConnectionImpl::dumpStreams(std::ostream& os, int indent_level) const {
1900 0 : ConnectionImpl::dumpStreams(os, indent_level);
1901 :
1902 0 : if (!current_stream_id_.has_value()) {
1903 0 : return;
1904 0 : }
1905 :
1906 : // Try to dump the downstream request information, corresponding to the
1907 : // stream we were processing.
1908 0 : const char* spaces = spacesForLevel(indent_level);
1909 0 : os << spaces << "Dumping corresponding downstream request for upstream stream "
1910 0 : << current_stream_id_.value() << ":\n";
1911 :
1912 0 : const ClientStreamImpl* client_stream =
1913 0 : static_cast<const ClientStreamImpl*>(getStreamUnchecked(current_stream_id_.value()));
1914 0 : if (client_stream) {
1915 0 : client_stream->response_decoder_.dumpState(os, indent_level + 1);
1916 0 : } else {
1917 0 : os << spaces
1918 0 : << " Failed to get the upstream stream with stream id: " << current_stream_id_.value()
1919 0 : << " Unable to dump downstream request.\n";
1920 0 : }
1921 0 : }
1922 :
1923 0 : void ConnectionImpl::StreamImpl::dumpState(std::ostream& os, int indent_level) const {
1924 0 : const char* spaces = spacesForLevel(indent_level);
1925 0 : os << spaces << "ConnectionImpl::StreamImpl " << this << DUMP_MEMBER(stream_id_)
1926 0 : << DUMP_MEMBER(unconsumed_bytes_) << DUMP_MEMBER(read_disable_count_)
1927 0 : << DUMP_MEMBER(local_end_stream_) << DUMP_MEMBER(local_end_stream_sent_)
1928 0 : << DUMP_MEMBER(remote_end_stream_) << DUMP_MEMBER(data_deferred_)
1929 0 : << DUMP_MEMBER(received_noninformational_headers_)
1930 0 : << DUMP_MEMBER(pending_receive_buffer_high_watermark_called_)
1931 0 : << DUMP_MEMBER(pending_send_buffer_high_watermark_called_)
1932 0 : << DUMP_MEMBER(reset_due_to_messaging_error_)
1933 0 : << DUMP_MEMBER_AS(cookies_, cookies_.getStringView());
1934 :
1935 0 : DUMP_DETAILS(pending_trailers_to_encode_);
1936 0 : }
1937 :
1938 0 : void ConnectionImpl::ClientStreamImpl::dumpState(std::ostream& os, int indent_level) const {
1939 0 : const char* spaces = spacesForLevel(indent_level);
1940 0 : StreamImpl::dumpState(os, indent_level);
1941 :
1942 : // Dump header map
1943 0 : if (absl::holds_alternative<ResponseHeaderMapPtr>(headers_or_trailers_)) {
1944 0 : DUMP_DETAILS(absl::get<ResponseHeaderMapPtr>(headers_or_trailers_));
1945 0 : } else {
1946 0 : DUMP_DETAILS(absl::get<ResponseTrailerMapPtr>(headers_or_trailers_));
1947 0 : }
1948 0 : }
1949 :
1950 0 : void ConnectionImpl::ServerStreamImpl::dumpState(std::ostream& os, int indent_level) const {
1951 0 : const char* spaces = spacesForLevel(indent_level);
1952 0 : StreamImpl::dumpState(os, indent_level);
1953 :
1954 : // Dump header map
1955 0 : if (absl::holds_alternative<RequestHeaderMapSharedPtr>(headers_or_trailers_)) {
1956 0 : DUMP_DETAILS(absl::get<RequestHeaderMapSharedPtr>(headers_or_trailers_));
1957 0 : } else {
1958 0 : DUMP_DETAILS(absl::get<RequestTrailerMapPtr>(headers_or_trailers_));
1959 0 : }
1960 0 : }
1961 :
1962 : ClientConnectionImpl::ClientConnectionImpl(
1963 : Network::Connection& connection, Http::ConnectionCallbacks& callbacks, CodecStats& stats,
1964 : Random::RandomGenerator& random_generator,
1965 : const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
1966 : const uint32_t max_response_headers_kb, const uint32_t max_response_headers_count,
1967 : Http2SessionFactory& http2_session_factory)
1968 : : ConnectionImpl(connection, stats, random_generator, http2_options, max_response_headers_kb,
1969 : max_response_headers_count),
1970 361 : callbacks_(callbacks) {
1971 361 : ClientHttp2Options client_http2_options(http2_options, max_response_headers_kb);
1972 361 : if (use_oghttp2_library_) {
1973 305 : adapter_ = http2_session_factory.create(http2_callbacks_.callbacks(), base(),
1974 305 : client_http2_options.ogOptions());
1975 305 : } else {
1976 56 : adapter_ = http2_session_factory.create(http2_callbacks_.callbacks(), base(),
1977 56 : client_http2_options.options());
1978 56 : }
1979 361 : http2_session_factory.init(base(), http2_options);
1980 361 : allow_metadata_ = http2_options.allow_metadata();
1981 361 : idle_session_requires_ping_interval_ = std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(
1982 361 : http2_options.connection_keepalive(), connection_idle_interval, 0));
1983 361 : }
1984 :
1985 475 : RequestEncoder& ClientConnectionImpl::newStream(ResponseDecoder& decoder) {
1986 : // If the connection has been idle long enough to trigger a ping, send one
1987 : // ahead of creating the stream.
1988 475 : if (idle_session_requires_ping_interval_.count() != 0 &&
1989 475 : (connection_.dispatcher().timeSource().monotonicTime() - lastReceivedDataTime() >
1990 0 : idle_session_requires_ping_interval_)) {
1991 0 : sendKeepalive();
1992 0 : }
1993 :
1994 475 : ClientStreamImplPtr stream(new ClientStreamImpl(*this, per_stream_buffer_limit_, decoder));
1995 : // If the connection is currently above the high watermark, make sure to inform the new stream.
1996 : // The connection can not pass this on automatically as it has no awareness that a new stream is
1997 : // created.
1998 475 : if (connection_.aboveHighWatermark()) {
1999 0 : stream->runHighWatermarkCallbacks();
2000 0 : }
2001 475 : ClientStreamImpl& stream_ref = *stream;
2002 475 : LinkedList::moveIntoList(std::move(stream), active_streams_);
2003 475 : protocol_constraints_.incrementOpenedStreamCount();
2004 475 : return stream_ref;
2005 475 : }
2006 :
2007 249 : Status ClientConnectionImpl::onBeginHeaders(const nghttp2_frame* frame) {
2008 : // The client code explicitly does not currently support push promise.
2009 249 : RELEASE_ASSERT(frame->hd.type == NGHTTP2_HEADERS, "");
2010 249 : RELEASE_ASSERT(frame->headers.cat == NGHTTP2_HCAT_RESPONSE ||
2011 249 : frame->headers.cat == NGHTTP2_HCAT_HEADERS,
2012 249 : "");
2013 249 : RETURN_IF_ERROR(trackInboundFrames(frame->hd.stream_id, frame->hd.length, frame->hd.type,
2014 249 : frame->hd.flags, frame->headers.padlen));
2015 249 : if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
2016 14 : StreamImpl* stream = getStream(frame->hd.stream_id);
2017 14 : stream->allocTrailers();
2018 14 : }
2019 :
2020 249 : return okStatus();
2021 249 : }
2022 :
2023 : int ClientConnectionImpl::onHeader(const nghttp2_frame* frame, HeaderString&& name,
2024 20847 : HeaderString&& value) {
2025 : // The client code explicitly does not currently support push promise.
2026 20847 : ASSERT(frame->hd.type == NGHTTP2_HEADERS);
2027 20847 : ASSERT(frame->headers.cat == NGHTTP2_HCAT_RESPONSE || frame->headers.cat == NGHTTP2_HCAT_HEADERS);
2028 20847 : ASSERT(connection_.state() == Network::Connection::State::Open);
2029 20847 : return saveHeader(frame, std::move(name), std::move(value));
2030 20847 : }
2031 :
2032 : // TODO(yanavlasov): move to the base class once the runtime flag is removed.
2033 : Status ClientConnectionImpl::trackInboundFrames(int32_t stream_id, size_t length, uint8_t type,
2034 16056 : uint8_t flags, uint32_t padding_length) {
2035 16056 : Status result;
2036 16056 : ENVOY_CONN_LOG(trace, "track inbound frame type={} flags={} length={} padding_length={}",
2037 16056 : connection_, static_cast<uint64_t>(type), static_cast<uint64_t>(flags),
2038 16056 : static_cast<uint64_t>(length), padding_length);
2039 :
2040 16056 : result = protocol_constraints_.trackInboundFrames(length, type, flags, padding_length);
2041 16056 : if (!result.ok()) {
2042 0 : ENVOY_CONN_LOG(trace, "error reading frame: {} received in this HTTP/2 session.", connection_,
2043 0 : result.message());
2044 0 : if (isInboundFramesWithEmptyPayloadError(result)) {
2045 0 : ConnectionImpl::StreamImpl* stream = getStreamUnchecked(stream_id);
2046 0 : if (stream) {
2047 0 : stream->setDetails(Http2ResponseCodeDetails::get().inbound_empty_frame_flood);
2048 0 : }
2049 : // Above if is defensive, because the stream has just been created and therefore always
2050 : // exists.
2051 0 : }
2052 0 : }
2053 16056 : return result;
2054 16056 : }
2055 :
2056 0 : StreamResetReason ClientConnectionImpl::getMessagingErrorResetReason() const {
2057 0 : connection_.streamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamProtocolError);
2058 :
2059 0 : return StreamResetReason::ProtocolError;
2060 0 : }
2061 :
2062 : ServerConnectionImpl::ServerConnectionImpl(
2063 : Network::Connection& connection, Http::ServerConnectionCallbacks& callbacks, CodecStats& stats,
2064 : Random::RandomGenerator& random_generator,
2065 : const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
2066 : const uint32_t max_request_headers_kb, const uint32_t max_request_headers_count,
2067 : envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
2068 : headers_with_underscores_action,
2069 : Server::OverloadManager& overload_manager)
2070 : : ConnectionImpl(connection, stats, random_generator, http2_options, max_request_headers_kb,
2071 : max_request_headers_count),
2072 : callbacks_(callbacks), headers_with_underscores_action_(headers_with_underscores_action),
2073 : should_send_go_away_on_dispatch_(overload_manager.getLoadShedPoint(
2074 2499 : "envoy.load_shed_points.http2_server_go_away_on_dispatch")) {
2075 2499 : ENVOY_LOG_ONCE_IF(trace, should_send_go_away_on_dispatch_ == nullptr,
2076 2499 : "LoadShedPoint envoy.load_shed_points.http2_server_go_away_on_dispatch is not "
2077 2499 : "found. Is it configured?");
2078 2499 : Http2Options h2_options(http2_options, max_request_headers_kb);
2079 :
2080 2499 : auto visitor = std::make_unique<http2::adapter::CallbackVisitor>(
2081 2499 : http2::adapter::Perspective::kServer, *http2_callbacks_.callbacks(), base());
2082 2499 : if (use_oghttp2_library_) {
2083 2276 : visitor_ = std::move(visitor);
2084 2276 : adapter_ = http2::adapter::OgHttp2Adapter::Create(*visitor_, h2_options.ogOptions());
2085 2276 : } else {
2086 223 : auto adapter =
2087 223 : http2::adapter::NgHttp2Adapter::CreateServerAdapter(*visitor, h2_options.options());
2088 223 : auto stream_close_listener = [p = adapter.get()](http2::adapter::Http2StreamId stream_id) {
2089 9 : p->RemoveStream(stream_id);
2090 9 : };
2091 223 : visitor->set_stream_close_listener(std::move(stream_close_listener));
2092 223 : visitor_ = std::move(visitor);
2093 223 : adapter_ = std::move(adapter);
2094 223 : }
2095 2499 : sendSettings(http2_options, false);
2096 2499 : allow_metadata_ = http2_options.allow_metadata();
2097 2499 : }
2098 :
2099 1515 : Status ServerConnectionImpl::onBeginHeaders(const nghttp2_frame* frame) {
2100 : // For a server connection, we should never get push promise frames.
2101 1515 : ASSERT(frame->hd.type == NGHTTP2_HEADERS);
2102 1515 : ASSERT(connection_.state() == Network::Connection::State::Open);
2103 1515 : RETURN_IF_ERROR(trackInboundFrames(frame->hd.stream_id, frame->hd.length, frame->hd.type,
2104 1515 : frame->hd.flags, frame->headers.padlen));
2105 :
2106 1515 : if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
2107 16 : stats_.trailers_.inc();
2108 16 : ASSERT(frame->headers.cat == NGHTTP2_HCAT_HEADERS);
2109 :
2110 16 : StreamImpl* stream = getStream(frame->hd.stream_id);
2111 16 : stream->allocTrailers();
2112 16 : return okStatus();
2113 16 : }
2114 :
2115 1499 : ServerStreamImplPtr stream(new ServerStreamImpl(*this, per_stream_buffer_limit_));
2116 1499 : if (connection_.aboveHighWatermark()) {
2117 0 : stream->runHighWatermarkCallbacks();
2118 0 : }
2119 1499 : stream->setRequestDecoder(callbacks_.newStream(*stream));
2120 1499 : stream->stream_id_ = frame->hd.stream_id;
2121 1499 : LinkedList::moveIntoList(std::move(stream), active_streams_);
2122 1499 : adapter_->SetStreamUserData(frame->hd.stream_id, active_streams_.front().get());
2123 1499 : protocol_constraints_.incrementOpenedStreamCount();
2124 1499 : return okStatus();
2125 1515 : }
2126 :
2127 : int ServerConnectionImpl::onHeader(const nghttp2_frame* frame, HeaderString&& name,
2128 4084 : HeaderString&& value) {
2129 : // For a server connection, we should never get push promise frames.
2130 4084 : ASSERT(frame->hd.type == NGHTTP2_HEADERS);
2131 4084 : ASSERT(frame->headers.cat == NGHTTP2_HCAT_REQUEST || frame->headers.cat == NGHTTP2_HCAT_HEADERS);
2132 4084 : if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_discard_host_header")) {
2133 4084 : StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id);
2134 4084 : if (stream && name == static_cast<absl::string_view>(Http::Headers::get().HostLegacy)) {
2135 : // Check if there is already the :authority header
2136 1 : const auto result = stream->headers().get(Http::Headers::get().Host);
2137 1 : if (!result.empty()) {
2138 : // Discard the host header value
2139 0 : return 0;
2140 0 : }
2141 : // Otherwise use host value as :authority
2142 1 : }
2143 4084 : }
2144 4084 : return saveHeader(frame, std::move(name), std::move(value));
2145 4084 : }
2146 :
2147 : Status ServerConnectionImpl::trackInboundFrames(int32_t stream_id, size_t length, uint8_t type,
2148 28865 : uint8_t flags, uint32_t padding_length) {
2149 28865 : ENVOY_CONN_LOG(trace, "track inbound frame type={} flags={} length={} padding_length={}",
2150 28865 : connection_, static_cast<uint64_t>(type), static_cast<uint64_t>(flags),
2151 28865 : static_cast<uint64_t>(length), padding_length);
2152 :
2153 28865 : auto result = protocol_constraints_.trackInboundFrames(length, type, flags, padding_length);
2154 28865 : if (!result.ok()) {
2155 0 : ENVOY_CONN_LOG(trace, "error reading frame: {} received in this HTTP/2 session.", connection_,
2156 0 : result.message());
2157 0 : if (isInboundFramesWithEmptyPayloadError(result)) {
2158 0 : ConnectionImpl::StreamImpl* stream = getStreamUnchecked(stream_id);
2159 0 : if (stream) {
2160 0 : stream->setDetails(Http2ResponseCodeDetails::get().inbound_empty_frame_flood);
2161 0 : }
2162 : // Above if is defensive, because the stream has just been created and therefore always
2163 : // exists.
2164 0 : }
2165 0 : }
2166 28865 : return result;
2167 28865 : }
2168 :
2169 29693 : Http::Status ServerConnectionImpl::dispatch(Buffer::Instance& data) {
2170 : // Make sure downstream outbound queue was not flooded by the upstream frames.
2171 29693 : RETURN_IF_ERROR(protocol_constraints_.checkOutboundFrameLimits());
2172 29693 : if (should_send_go_away_on_dispatch_ != nullptr && !sent_go_away_on_dispatch_ &&
2173 29693 : should_send_go_away_on_dispatch_->shouldShedLoad()) {
2174 0 : ConnectionImpl::goAway();
2175 0 : sent_go_away_on_dispatch_ = true;
2176 0 : }
2177 29693 : return ConnectionImpl::dispatch(data);
2178 29693 : }
2179 :
2180 : absl::optional<int> ServerConnectionImpl::checkHeaderNameForUnderscores(
2181 4084 : [[maybe_unused]] absl::string_view header_name) {
2182 4084 : #ifndef ENVOY_ENABLE_UHV
2183 : // This check has been moved to UHV
2184 4084 : if (headers_with_underscores_action_ != envoy::config::core::v3::HttpProtocolOptions::ALLOW &&
2185 4084 : Http::HeaderUtility::headerNameContainsUnderscore(header_name)) {
2186 0 : if (headers_with_underscores_action_ ==
2187 0 : envoy::config::core::v3::HttpProtocolOptions::DROP_HEADER) {
2188 0 : ENVOY_CONN_LOG(debug, "Dropping header with invalid characters in its name: {}", connection_,
2189 0 : header_name);
2190 0 : stats_.incDroppedHeadersWithUnderscores();
2191 0 : return 0;
2192 0 : }
2193 0 : ENVOY_CONN_LOG(debug, "Rejecting request due to header name with underscores: {}", connection_,
2194 0 : header_name);
2195 0 : stats_.incRequestsRejectedWithUnderscoresInHeaders();
2196 0 : return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
2197 0 : }
2198 : #else
2199 : // Workaround for gcc not understanding [[maybe_unused]] for class members.
2200 : (void)headers_with_underscores_action_;
2201 : #endif
2202 4084 : return absl::nullopt;
2203 4084 : }
2204 :
2205 : } // namespace Http2
2206 : } // namespace Http
2207 : } // namespace Envoy
|