/proc/self/cwd/source/common/http/http1/codec_impl.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "source/common/http/http1/codec_impl.h" |
2 | | |
3 | | #include <cstdint> |
4 | | #include <memory> |
5 | | #include <string> |
6 | | |
7 | | #include "envoy/buffer/buffer.h" |
8 | | #include "envoy/common/optref.h" |
9 | | #include "envoy/http/codec.h" |
10 | | #include "envoy/http/header_map.h" |
11 | | #include "envoy/network/connection.h" |
12 | | |
13 | | #include "source/common/common/cleanup.h" |
14 | | #include "source/common/common/dump_state_utils.h" |
15 | | #include "source/common/common/enum_to_int.h" |
16 | | #include "source/common/common/scope_tracker.h" |
17 | | #include "source/common/common/statusor.h" |
18 | | #include "source/common/common/utility.h" |
19 | | #include "source/common/grpc/common.h" |
20 | | #include "source/common/http/exception.h" |
21 | | #include "source/common/http/header_utility.h" |
22 | | #include "source/common/http/headers.h" |
23 | | #include "source/common/http/http1/balsa_parser.h" |
24 | | #include "source/common/http/http1/header_formatter.h" |
25 | | #include "source/common/http/http1/legacy_parser_impl.h" |
26 | | #include "source/common/http/utility.h" |
27 | | #include "source/common/runtime/runtime_features.h" |
28 | | |
29 | | #include "absl/container/fixed_array.h" |
30 | | #include "absl/strings/ascii.h" |
31 | | |
32 | | namespace Envoy { |
33 | | namespace Http { |
34 | | namespace Http1 { |
35 | | namespace { |
36 | | |
37 | | // Changes or additions to details should be reflected in |
38 | | // docs/root/configuration/http/http_conn_man/response_code_details.rst |
39 | | struct Http1ResponseCodeDetailValues { |
40 | | const absl::string_view TooManyHeaders = "http1.too_many_headers"; |
41 | | const absl::string_view HeadersTooLarge = "http1.headers_too_large"; |
42 | | const absl::string_view HttpCodecError = "http1.codec_error"; |
43 | | const absl::string_view InvalidCharacters = "http1.invalid_characters"; |
44 | | const absl::string_view ConnectionHeaderSanitization = "http1.connection_header_rejected"; |
45 | | const absl::string_view InvalidUrl = "http1.invalid_url"; |
46 | | const absl::string_view InvalidTransferEncoding = "http1.invalid_transfer_encoding"; |
47 | | const absl::string_view BodyDisallowed = "http1.body_disallowed"; |
48 | | const absl::string_view TransferEncodingNotAllowed = "http1.transfer_encoding_not_allowed"; |
49 | | const absl::string_view ContentLengthNotAllowed = "http1.content_length_not_allowed"; |
50 | | const absl::string_view InvalidUnderscore = "http1.unexpected_underscore"; |
51 | | const absl::string_view ChunkedContentLength = "http1.content_length_and_chunked_not_allowed"; |
52 | | const absl::string_view HttpsInPlaintext = "http1.https_url_on_plaintext_connection"; |
53 | | const absl::string_view InvalidScheme = "http1.invalid_scheme"; |
54 | | }; |
55 | | |
56 | | struct Http1HeaderTypesValues { |
57 | | const absl::string_view Headers = "headers"; |
58 | | const absl::string_view Trailers = "trailers"; |
59 | | }; |
60 | | |
61 | | // Pipelining is generally not well supported on the internet and has a series of dangerous |
62 | | // overflow bugs. As such Envoy disabled it. |
63 | | static constexpr uint32_t kMaxOutboundResponses = 2; |
64 | | |
65 | | using Http1ResponseCodeDetails = ConstSingleton<Http1ResponseCodeDetailValues>; |
66 | | using Http1HeaderTypes = ConstSingleton<Http1HeaderTypesValues>; |
67 | | |
68 | 252 | const StringUtil::CaseUnorderedSet& caseUnorderdSetContainingUpgradeAndHttp2Settings() { |
69 | 252 | CONSTRUCT_ON_FIRST_USE(StringUtil::CaseUnorderedSet, |
70 | 252 | Http::Headers::get().ConnectionValues.Upgrade, |
71 | 252 | Http::Headers::get().ConnectionValues.Http2Settings); |
72 | 252 | } |
73 | | |
74 | 56.4k | HeaderKeyFormatterConstPtr encodeOnlyFormatterFromSettings(const Http::Http1Settings& settings) { |
75 | 56.4k | if (settings.header_key_format_ == Http1Settings::HeaderKeyFormat::ProperCase) { |
76 | 67 | return std::make_unique<ProperCaseHeaderKeyFormatter>(); |
77 | 67 | } |
78 | | |
79 | 56.3k | return nullptr; |
80 | 56.4k | } |
81 | | |
82 | 42.3k | StatefulHeaderKeyFormatterPtr statefulFormatterFromSettings(const Http::Http1Settings& settings) { |
83 | 42.3k | if (settings.header_key_format_ == Http1Settings::HeaderKeyFormat::StatefulFormatter) { |
84 | 0 | return settings.stateful_header_key_formatter_->create(); |
85 | 0 | } |
86 | 42.3k | return nullptr; |
87 | 42.3k | } |
88 | | |
89 | | constexpr size_t CRLF_SIZE = 2; |
90 | | |
91 | | } // namespace |
92 | | |
93 | | static constexpr absl::string_view CRLF = "\r\n"; |
94 | | // Last chunk as defined here https://tools.ietf.org/html/rfc7230#section-4.1 |
95 | | static constexpr absl::string_view LAST_CHUNK = "0\r\n"; |
96 | | |
97 | | static constexpr absl::string_view SPACE = " "; |
98 | | static constexpr absl::string_view COLON_SPACE = ": "; |
99 | | |
100 | | StreamEncoderImpl::StreamEncoderImpl(ConnectionImpl& connection, |
101 | | StreamInfo::BytesMeterSharedPtr&& bytes_meter) |
102 | | : connection_(connection), disable_chunk_encoding_(false), chunk_encoding_(true), |
103 | | connect_request_(false), is_tcp_tunneling_(false), is_response_to_head_request_(false), |
104 | 39.5k | is_response_to_connect_request_(false), bytes_meter_(std::move(bytes_meter)) { |
105 | 39.5k | if (!bytes_meter_) { |
106 | 11.6k | bytes_meter_ = std::make_shared<StreamInfo::BytesMeter>(); |
107 | 11.6k | } |
108 | 39.5k | if (connection_.connection().aboveHighWatermark()) { |
109 | 0 | runHighWatermarkCallbacks(); |
110 | 0 | } |
111 | 39.5k | } Envoy::Http::Http1::StreamEncoderImpl::StreamEncoderImpl(Envoy::Http::Http1::ConnectionImpl&, std::__1::shared_ptr<Envoy::StreamInfo::BytesMeter>&&) Line | Count | Source | 104 | 39.5k | is_response_to_connect_request_(false), bytes_meter_(std::move(bytes_meter)) { | 105 | 39.5k | if (!bytes_meter_) { | 106 | 11.6k | bytes_meter_ = std::make_shared<StreamInfo::BytesMeter>(); | 107 | 11.6k | } | 108 | 39.5k | if (connection_.connection().aboveHighWatermark()) { | 109 | 0 | runHighWatermarkCallbacks(); | 110 | 0 | } | 111 | 39.5k | } |
Unexecuted instantiation: Envoy::Http::Http1::StreamEncoderImpl::StreamEncoderImpl(Envoy::Http::Http1::ConnectionImpl&, std::__1::shared_ptr<Envoy::StreamInfo::BytesMeter>&&) |
112 | | |
113 | 94.5k | void StreamEncoderImpl::encodeHeader(absl::string_view key, absl::string_view value) { |
114 | 94.5k | ASSERT(!key.empty()); |
115 | | |
116 | 94.5k | const uint64_t header_size = connection_.buffer().addFragments({key, COLON_SPACE, value, CRLF}); |
117 | | |
118 | 94.5k | bytes_meter_->addHeaderBytesSent(header_size); |
119 | 94.5k | } |
120 | | |
121 | | void StreamEncoderImpl::encodeFormattedHeader(absl::string_view key, absl::string_view value, |
122 | 94.5k | HeaderKeyFormatterOptConstRef formatter) { |
123 | 94.5k | if (formatter.has_value()) { |
124 | 479 | encodeHeader(formatter->format(key), value); |
125 | 94.1k | } else { |
126 | 94.1k | encodeHeader(key, value); |
127 | 94.1k | } |
128 | 94.5k | } |
129 | | |
130 | 7.62k | void ResponseEncoderImpl::encode1xxHeaders(const ResponseHeaderMap& headers) { |
131 | 7.62k | ASSERT(HeaderUtility::isSpecial1xx(headers)); |
132 | 7.62k | encodeHeaders(headers, false); |
133 | 7.62k | if (Runtime::runtimeFeatureEnabled( |
134 | 7.62k | "envoy.reloadable_features.http1_allow_codec_error_response_after_1xx_headers")) { |
135 | | // Don't consider 100-continue responses as the actual response. |
136 | 7.62k | started_response_ = false; |
137 | 7.62k | } |
138 | 7.62k | } |
139 | | |
140 | | void StreamEncoderImpl::encodeHeadersBase(const RequestOrResponseHeaderMap& headers, |
141 | | absl::optional<uint64_t> status, bool end_stream, |
142 | 26.1k | bool bodiless_request) { |
143 | 26.1k | HeaderKeyFormatterOptConstRef formatter(headers.formatter()); |
144 | 26.1k | if (!formatter.has_value()) { |
145 | 26.1k | formatter = connection_.formatter(); |
146 | 26.1k | } |
147 | | |
148 | 26.1k | const Http::HeaderValues& header_values = Http::Headers::get(); |
149 | 26.1k | bool saw_content_length = false; |
150 | 26.1k | headers.iterate( |
151 | 121k | [this, &header_values, formatter](const HeaderEntry& header) -> HeaderMap::Iterate { |
152 | 121k | absl::string_view key_to_use = header.key().getStringView(); |
153 | 121k | uint32_t key_size_to_use = header.key().size(); |
154 | | // Translate :authority -> host so that upper layers do not need to deal with this. |
155 | 121k | if (key_size_to_use > 1 && key_to_use[0] == ':' && key_to_use[1] == 'a') { |
156 | 11.8k | key_to_use = absl::string_view(header_values.HostLegacy.get()); |
157 | 11.8k | key_size_to_use = header_values.HostLegacy.get().size(); |
158 | 11.8k | } |
159 | | |
160 | | // Skip all headers starting with ':' that make it here. |
161 | 121k | if (key_to_use[0] == ':') { |
162 | 41.1k | return HeaderMap::Iterate::Continue; |
163 | 41.1k | } |
164 | | |
165 | 80.8k | encodeFormattedHeader(key_to_use, header.value().getStringView(), formatter); |
166 | | |
167 | 80.8k | return HeaderMap::Iterate::Continue; |
168 | 121k | }); |
169 | | |
170 | 26.1k | if (headers.ContentLength()) { |
171 | 1.74k | saw_content_length = true; |
172 | 1.74k | } |
173 | | |
174 | 26.1k | ASSERT(!headers.TransferEncoding()); |
175 | | |
176 | | // Assume we are chunk encoding unless we are passed a content length or this is a header only |
177 | | // response. Upper layers generally should strip transfer-encoding since it only applies to |
178 | | // HTTP/1.1. The codec will infer it based on the type of response. |
179 | | // for streaming (e.g. SSE stream sent to hystrix dashboard), we do not want |
180 | | // chunk transfer encoding but we don't have a content-length so disable_chunk_encoding_ is |
181 | | // consulted before enabling chunk encoding. |
182 | | // |
183 | | // Note that for HEAD requests Envoy does best-effort guessing when there is no |
184 | | // content-length. If a client makes a HEAD request for an upstream resource |
185 | | // with no bytes but the upstream response doesn't include "Content-length: 0", |
186 | | // Envoy will incorrectly assume a subsequent response to GET will be chunk encoded. |
187 | 26.1k | if (saw_content_length || disable_chunk_encoding_) { |
188 | 2.01k | chunk_encoding_ = false; |
189 | 24.1k | } else { |
190 | 24.1k | if (status && (*status < 200 || *status == 204)) { |
191 | | // For 1xx and 204 responses, do not send the chunked encoding header or enable chunked |
192 | | // encoding: https://tools.ietf.org/html/rfc7230#section-3.3.1 |
193 | 7.61k | chunk_encoding_ = false; |
194 | 16.5k | } else if (status && *status == 304) { |
195 | | // For 304 response, since it should never have a body, we should not need to chunk_encode at |
196 | | // all. |
197 | 3 | chunk_encoding_ = false; |
198 | 16.5k | } else if (end_stream && !is_response_to_head_request_) { |
199 | | // If this is a headers-only stream, append an explicit "Content-Length: 0" unless it's a |
200 | | // response to a HEAD request. |
201 | | // For 204s and 1xx where content length is disallowed, don't append the content length but |
202 | | // also don't chunk encode. |
203 | | // Also do not add content length for requests which should not have a |
204 | | // body, per https://tools.ietf.org/html/rfc7230#section-3.3.2 |
205 | 6.21k | if (!status || (*status >= 200 && *status != 204)) { |
206 | 6.21k | if (!bodiless_request) { |
207 | 3.44k | encodeFormattedHeader(header_values.ContentLength.get(), "0", formatter); |
208 | 3.44k | } |
209 | 6.21k | } |
210 | 6.21k | chunk_encoding_ = false; |
211 | 10.3k | } else if (connection_.protocol() == Protocol::Http10) { |
212 | 15 | chunk_encoding_ = false; |
213 | 10.3k | } else { |
214 | | // For responses to connect requests, do not send the chunked encoding header: |
215 | | // https://tools.ietf.org/html/rfc7231#section-4.3.6. |
216 | 10.3k | if (!is_response_to_connect_request_) { |
217 | 10.3k | encodeFormattedHeader(header_values.TransferEncoding.get(), |
218 | 10.3k | header_values.TransferEncodingValues.Chunked, formatter); |
219 | 10.3k | } |
220 | | // We do not apply chunk encoding for HTTP upgrades, including CONNECT style upgrades. |
221 | | // If there is a body in a response on the upgrade path, the chunks will be |
222 | | // passed through via maybeDirectDispatch so we need to avoid appending |
223 | | // extra chunk boundaries. |
224 | | // |
225 | | // When sending a response to a HEAD request Envoy may send an informational |
226 | | // "Transfer-Encoding: chunked" header, but should not send a chunk encoded body. |
227 | 10.3k | chunk_encoding_ = !Utility::isUpgrade(headers) && !is_response_to_head_request_ && |
228 | 10.3k | !is_response_to_connect_request_; |
229 | 10.3k | } |
230 | 24.1k | } |
231 | | |
232 | 26.1k | connection_.buffer().add(CRLF); |
233 | | |
234 | 26.1k | if (end_stream) { |
235 | 6.29k | endEncode(); |
236 | 19.8k | } else { |
237 | 19.8k | flushOutput(); |
238 | 19.8k | } |
239 | 26.1k | } |
240 | | |
241 | 8.69k | void StreamEncoderImpl::encodeData(Buffer::Instance& data, bool end_stream) { |
242 | | // end_stream may be indicated with a zero length data buffer. If that is the case, so not |
243 | | // actually write the zero length buffer out. |
244 | 8.69k | if (data.length() > 0) { |
245 | 8.30k | if (chunk_encoding_) { |
246 | 5.64k | std::string chunk_header = absl::StrCat(absl::Hex(data.length()), CRLF); |
247 | 5.64k | connection_.buffer().add(std::move(chunk_header)); |
248 | 5.64k | } |
249 | | |
250 | 8.30k | connection_.buffer().move(data); |
251 | | |
252 | 8.30k | if (chunk_encoding_) { |
253 | 5.64k | connection_.buffer().add(CRLF); |
254 | 5.64k | } |
255 | 8.30k | } |
256 | | |
257 | 8.69k | if (end_stream) { |
258 | 3.74k | endEncode(); |
259 | 4.95k | } else { |
260 | 4.95k | flushOutput(); |
261 | 4.95k | } |
262 | 8.69k | } |
263 | | |
264 | 37.3k | void StreamEncoderImpl::flushOutput(bool end_encode) { |
265 | 37.3k | auto encoded_bytes = connection_.flushOutput(end_encode); |
266 | 37.3k | bytes_meter_->addWireBytesSent(encoded_bytes); |
267 | 37.3k | } |
268 | | |
269 | 2.46k | void StreamEncoderImpl::encodeTrailersBase(const HeaderMap& trailers) { |
270 | 2.46k | if (!connection_.enableTrailers()) { |
271 | 2.46k | return endEncode(); |
272 | 2.46k | } |
273 | | // Trailers only matter if it is a chunk transfer encoding |
274 | | // https://tools.ietf.org/html/rfc7230#section-4.4 |
275 | 0 | if (chunk_encoding_) { |
276 | | // Finalize the body |
277 | 0 | connection_.buffer().add(LAST_CHUNK); |
278 | | |
279 | | // TODO(mattklein123): Wire up the formatter if someone actually asks for this (very unlikely). |
280 | 0 | trailers.iterate([this](const HeaderEntry& header) -> HeaderMap::Iterate { |
281 | 0 | encodeFormattedHeader(header.key().getStringView(), header.value().getStringView(), |
282 | 0 | HeaderKeyFormatterOptConstRef()); |
283 | 0 | return HeaderMap::Iterate::Continue; |
284 | 0 | }); |
285 | |
|
286 | 0 | connection_.buffer().add(CRLF); |
287 | 0 | } |
288 | |
|
289 | 0 | flushOutput(); |
290 | 0 | notifyEncodeComplete(); |
291 | 0 | } |
292 | | |
293 | 182 | void StreamEncoderImpl::encodeMetadata(const MetadataMapVector&) { |
294 | 182 | connection_.stats().metadata_not_supported_error_.inc(); |
295 | 182 | } |
296 | | |
297 | 12.5k | void StreamEncoderImpl::endEncode() { |
298 | 12.5k | if (chunk_encoding_) { |
299 | 5.26k | connection_.buffer().addFragments({LAST_CHUNK, CRLF}); |
300 | 5.26k | } |
301 | | |
302 | 12.5k | flushOutput(true); |
303 | 12.5k | notifyEncodeComplete(); |
304 | | // With CONNECT or TCP tunneling, half-closing the connection is used to signal end stream so |
305 | | // don't delay that signal. |
306 | 12.5k | if (connect_request_ || is_tcp_tunneling_) { |
307 | 13 | connection_.connection().close( |
308 | 13 | Network::ConnectionCloseType::FlushWrite, |
309 | 13 | StreamInfo::LocalCloseReasons::get().CloseForConnectRequestOrTcpTunneling); |
310 | 13 | } |
311 | 12.5k | } |
312 | | |
313 | 12.5k | void StreamEncoderImpl::notifyEncodeComplete() { |
314 | 12.5k | if (codec_callbacks_) { |
315 | 3.58k | codec_callbacks_->onCodecEncodeComplete(); |
316 | 3.58k | } |
317 | 12.5k | connection_.onEncodeComplete(); |
318 | 12.5k | } |
319 | | |
320 | 6.25k | void ServerConnectionImpl::maybeAddSentinelBufferFragment(Buffer::Instance& output_buffer) { |
321 | | // It's messy and complicated to try to tag the final write of an HTTP response for response |
322 | | // tracking for flood protection. Instead, write an empty buffer fragment after the response, |
323 | | // to allow for tracking. |
324 | | // When the response is written out, the fragment will be deleted and the counter will be updated |
325 | | // by ServerConnectionImpl::releaseOutboundResponse() |
326 | 6.25k | auto fragment = |
327 | 6.25k | Buffer::OwnedBufferFragmentImpl::create(absl::string_view("", 0), response_buffer_releasor_); |
328 | 6.25k | output_buffer.addBufferFragment(*fragment.release()); |
329 | 6.25k | ASSERT(outbound_responses_ < kMaxOutboundResponses); |
330 | 6.25k | outbound_responses_++; |
331 | 6.25k | } |
332 | | |
333 | 27.9k | Status ServerConnectionImpl::doFloodProtectionChecks() const { |
334 | 27.9k | ASSERT(dispatching_); |
335 | | // Before processing another request, make sure that we are below the response flood protection |
336 | | // threshold. |
337 | 27.9k | if (outbound_responses_ >= kMaxOutboundResponses) { |
338 | 15 | ENVOY_CONN_LOG(trace, "error accepting request: too many pending responses queued", |
339 | 15 | connection_); |
340 | 15 | stats_.response_flood_.inc(); |
341 | 15 | return bufferFloodError("Too many responses queued."); |
342 | 15 | } |
343 | 27.8k | return okStatus(); |
344 | 27.9k | } |
345 | | |
346 | 37.3k | uint64_t ConnectionImpl::flushOutput(bool end_encode) { |
347 | 37.3k | if (end_encode) { |
348 | | // If this is an HTTP response in ServerConnectionImpl, track outbound responses for flood |
349 | | // protection |
350 | 12.5k | maybeAddSentinelBufferFragment(*output_buffer_); |
351 | 12.5k | } |
352 | 37.3k | const uint64_t bytes_encoded = output_buffer_->length(); |
353 | 37.3k | connection().write(*output_buffer_, false); |
354 | 37.3k | ASSERT(0UL == output_buffer_->length()); |
355 | 37.3k | return bytes_encoded; |
356 | 37.3k | } |
357 | | |
358 | | CodecEventCallbacks* |
359 | 7.75k | StreamEncoderImpl::registerCodecEventCallbacks(CodecEventCallbacks* codec_callbacks) { |
360 | 7.75k | std::swap(codec_callbacks, codec_callbacks_); |
361 | 7.75k | return codec_callbacks; |
362 | 7.75k | } |
363 | | |
364 | 1.84k | void StreamEncoderImpl::resetStream(StreamResetReason reason) { |
365 | 1.84k | connection_.onResetStreamBase(reason); |
366 | 1.84k | } |
367 | | |
368 | 1.32k | void ResponseEncoderImpl::resetStream(StreamResetReason reason) { |
369 | | // Clear the downstream on the account since we're resetting the downstream. |
370 | 1.32k | if (buffer_memory_account_) { |
371 | 0 | buffer_memory_account_->clearDownstream(); |
372 | 0 | } |
373 | | |
374 | | // For H1, we use idleTimeouts to cancel streams unless there was an |
375 | | // explicit protocol error prior to sending a response to the downstream |
376 | | // in which case we send a local reply. |
377 | | // TODO(kbaichoo): If we want snappier resets of H1 streams we can |
378 | | // 1) Send local reply if no response data sent yet |
379 | | // 2) Invoke the idle timeout sooner to close underlying connection |
380 | 1.32k | StreamEncoderImpl::resetStream(reason); |
381 | 1.32k | } |
382 | | |
383 | 8.63k | void StreamEncoderImpl::readDisable(bool disable) { |
384 | 8.63k | if (disable) { |
385 | 4.31k | ++read_disable_calls_; |
386 | 4.31k | } else { |
387 | 4.31k | ASSERT(read_disable_calls_ != 0); |
388 | 4.31k | if (read_disable_calls_ != 0) { |
389 | 4.31k | --read_disable_calls_; |
390 | 4.31k | } |
391 | 4.31k | } |
392 | 8.63k | connection_.readDisable(disable); |
393 | 8.63k | } |
394 | | |
395 | 3.87k | uint32_t StreamEncoderImpl::bufferLimit() const { return connection_.bufferLimit(); } |
396 | | |
397 | 44 | const Network::ConnectionInfoProvider& StreamEncoderImpl::connectionInfoProvider() { |
398 | 44 | return connection_.connection().connectionInfoProvider(); |
399 | 44 | } |
400 | | |
401 | | static constexpr absl::string_view RESPONSE_PREFIX = "HTTP/1.1 "; |
402 | | static constexpr absl::string_view HTTP_10_RESPONSE_PREFIX = "HTTP/1.0 "; |
403 | | |
404 | 14.7k | void ResponseEncoderImpl::encodeHeaders(const ResponseHeaderMap& headers, bool end_stream) { |
405 | 14.7k | started_response_ = true; |
406 | | |
407 | | // The contract is that client codecs must ensure that :status is present and valid. |
408 | 14.7k | ASSERT(headers.Status() != nullptr); |
409 | 14.7k | uint64_t numeric_status = Utility::getResponseStatus(headers); |
410 | | |
411 | 14.7k | absl::string_view response_prefix; |
412 | 14.7k | if (connection_.protocol() == Protocol::Http10 && connection_.supportsHttp10()) { |
413 | 182 | response_prefix = HTTP_10_RESPONSE_PREFIX; |
414 | 14.5k | } else { |
415 | 14.5k | response_prefix = RESPONSE_PREFIX; |
416 | 14.5k | } |
417 | | |
418 | 14.7k | StatefulHeaderKeyFormatterOptConstRef formatter(headers.formatter()); |
419 | | |
420 | 14.7k | absl::string_view reason_phrase; |
421 | 14.7k | if (formatter.has_value() && !formatter->getReasonPhrase().empty()) { |
422 | 0 | reason_phrase = formatter->getReasonPhrase(); |
423 | 14.7k | } else { |
424 | 14.7k | const char* status_string = CodeUtility::toString(static_cast<Code>(numeric_status)); |
425 | 14.7k | uint32_t status_string_len = strlen(status_string); |
426 | 14.7k | reason_phrase = {status_string, status_string_len}; |
427 | 14.7k | } |
428 | | |
429 | 14.7k | connection_.buffer().addFragments( |
430 | 14.7k | {response_prefix, absl::StrCat(numeric_status), SPACE, reason_phrase, CRLF}); |
431 | | |
432 | 14.7k | if (numeric_status >= 300) { |
433 | | // Don't do special CONNECT logic if the CONNECT was rejected. |
434 | 1.34k | is_response_to_connect_request_ = false; |
435 | 1.34k | } |
436 | | |
437 | 14.7k | encodeHeadersBase(headers, absl::make_optional<uint64_t>(numeric_status), end_stream, false); |
438 | 14.7k | } |
439 | | |
440 | | static constexpr absl::string_view REQUEST_POSTFIX = " HTTP/1.1\r\n"; |
441 | | |
442 | 11.6k | Status RequestEncoderImpl::encodeHeaders(const RequestHeaderMap& headers, bool end_stream) { |
443 | 11.6k | #ifndef ENVOY_ENABLE_UHV |
444 | | // Headers are now validated by UHV before encoding by the codec. Two checks below are not needed |
445 | | // when UHV is enabled. |
446 | | // |
447 | | // Required headers must be present. This can only happen by some erroneous processing after the |
448 | | // downstream codecs decode. |
449 | 11.6k | RETURN_IF_ERROR(HeaderUtility::checkRequiredRequestHeaders(headers)); |
450 | | // Verify that a filter hasn't added an invalid header key or value. |
451 | 11.6k | RETURN_IF_ERROR(HeaderUtility::checkValidRequestHeaders(headers)); |
452 | 11.4k | #endif |
453 | | |
454 | 11.4k | const HeaderEntry* method = headers.Method(); |
455 | 11.4k | const HeaderEntry* path = headers.Path(); |
456 | 11.4k | const HeaderEntry* host = headers.Host(); |
457 | 11.4k | bool is_connect = HeaderUtility::isConnect(headers); |
458 | 11.4k | const Http::HeaderValues& header_values = Http::Headers::get(); |
459 | | |
460 | 11.4k | if (method->value() == header_values.MethodValues.Head) { |
461 | 141 | head_request_ = true; |
462 | 11.3k | } else if (method->value() == header_values.MethodValues.Connect) { |
463 | 157 | disableChunkEncoding(); |
464 | 157 | connection_.connection().enableHalfClose(true); |
465 | 157 | connect_request_ = true; |
466 | 157 | } |
467 | 11.4k | if (Utility::isUpgrade(headers)) { |
468 | 117 | upgrade_request_ = true; |
469 | | // If the flag is flipped from true to false all outstanding upgrade requests that are waiting |
470 | | // for upstream connections will become invalid, as Envoy will add chunk encoding to the |
471 | | // protocol stream. This will likely cause the server to disconnect, since it will be unable to |
472 | | // parse the protocol. |
473 | 117 | disableChunkEncoding(); |
474 | 117 | } |
475 | | |
476 | 11.4k | if (connection_.sendFullyQualifiedUrl() && !is_connect) { |
477 | 0 | const HeaderEntry* scheme = headers.Scheme(); |
478 | 0 | if (!scheme) { |
479 | 0 | return absl::InvalidArgumentError( |
480 | 0 | absl::StrCat("missing required header: ", Envoy::Http::Headers::get().Scheme.get())); |
481 | 0 | } |
482 | 0 | if (!host) { |
483 | 0 | return absl::InvalidArgumentError( |
484 | 0 | absl::StrCat("missing required header: ", Envoy::Http::Headers::get().Host.get())); |
485 | 0 | } |
486 | 0 | ASSERT(path); |
487 | 0 | ASSERT(host); |
488 | | |
489 | 0 | std::string url = absl::StrCat(scheme->value().getStringView(), "://", |
490 | 0 | host->value().getStringView(), path->value().getStringView()); |
491 | 0 | ENVOY_CONN_LOG(trace, "Sending fully qualified URL: {}", connection_.connection(), url); |
492 | 0 | connection_.buffer().addFragments( |
493 | 0 | {method->value().getStringView(), SPACE, url, REQUEST_POSTFIX}); |
494 | 11.4k | } else { |
495 | 11.4k | absl::string_view host_or_path_view; |
496 | 11.4k | if (is_connect) { |
497 | 157 | host_or_path_view = host->value().getStringView(); |
498 | 11.3k | } else { |
499 | 11.3k | host_or_path_view = path->value().getStringView(); |
500 | 11.3k | } |
501 | | |
502 | 11.4k | connection_.buffer().addFragments( |
503 | 11.4k | {method->value().getStringView(), SPACE, host_or_path_view, REQUEST_POSTFIX}); |
504 | 11.4k | } |
505 | | |
506 | 11.4k | encodeHeadersBase(headers, absl::nullopt, end_stream, |
507 | 11.4k | HeaderUtility::requestShouldHaveNoBody(headers)); |
508 | 11.4k | return okStatus(); |
509 | 11.4k | } |
510 | | |
511 | 572k | CallbackResult ConnectionImpl::setAndCheckCallbackStatus(Status&& status) { |
512 | 572k | ASSERT(codec_status_.ok()); |
513 | 572k | codec_status_ = std::move(status); |
514 | 572k | return codec_status_.ok() ? CallbackResult::Success : CallbackResult::Error; |
515 | 572k | } |
516 | | |
517 | | CallbackResult |
518 | 48.2k | ConnectionImpl::setAndCheckCallbackStatusOr(Envoy::StatusOr<CallbackResult>&& statusor) { |
519 | 48.2k | ASSERT(codec_status_.ok()); |
520 | 48.2k | if (statusor.ok()) { |
521 | 42.4k | return statusor.value(); |
522 | 42.4k | } else { |
523 | 5.82k | codec_status_ = std::move(statusor.status()); |
524 | 5.82k | return CallbackResult::Error; |
525 | 5.82k | } |
526 | 48.2k | } |
527 | | |
528 | | ConnectionImpl::ConnectionImpl(Network::Connection& connection, CodecStats& stats, |
529 | | const Http1Settings& settings, MessageType type, |
530 | | uint32_t max_headers_kb, const uint32_t max_headers_count) |
531 | | : connection_(connection), stats_(stats), codec_settings_(settings), |
532 | | encode_only_header_key_formatter_(encodeOnlyFormatterFromSettings(settings)), |
533 | | processing_trailers_(false), handling_upgrade_(false), reset_stream_called_(false), |
534 | | deferred_end_stream_headers_(false), dispatching_(false), max_headers_kb_(max_headers_kb), |
535 | 56.4k | max_headers_count_(max_headers_count) { |
536 | 56.4k | if (codec_settings_.use_balsa_parser_) { |
537 | 19.3k | parser_ = std::make_unique<BalsaParser>(type, this, max_headers_kb_ * 1024, enableTrailers(), |
538 | 19.3k | codec_settings_.allow_custom_methods_); |
539 | 37.1k | } else { |
540 | 37.1k | parser_ = std::make_unique<LegacyHttpParserImpl>(type, this); |
541 | 37.1k | } |
542 | 56.4k | } |
543 | | |
544 | 239k | Status ConnectionImpl::completeCurrentHeader() { |
545 | 239k | ASSERT(dispatching_); |
546 | 239k | ENVOY_CONN_LOG(trace, "completed header: key={} value={}", connection_, |
547 | 239k | current_header_field_.getStringView(), current_header_value_.getStringView()); |
548 | 239k | auto& headers_or_trailers = headersOrTrailers(); |
549 | | |
550 | | // Account for ":" and "\r\n" bytes between the header key value pair. |
551 | 239k | getBytesMeter().addHeaderBytesReceived(CRLF_SIZE + 1); |
552 | | |
553 | | // TODO(10646): Switch to use HeaderUtility::checkHeaderNameForUnderscores(). |
554 | 239k | RETURN_IF_ERROR(checkHeaderNameForUnderscores()); |
555 | 239k | if (!current_header_field_.empty()) { |
556 | | // Strip trailing whitespace of the current header value if any. Leading whitespace was trimmed |
557 | | // in ConnectionImpl::onHeaderValue. http_parser does not strip leading or trailing whitespace |
558 | | // as the spec requires: https://tools.ietf.org/html/rfc7230#section-3.2.4 |
559 | 216k | current_header_value_.rtrim(); |
560 | | |
561 | | // If there is a stateful formatter installed, remember the original header key before |
562 | | // converting to lower case. |
563 | 216k | auto formatter = headers_or_trailers.formatter(); |
564 | 216k | if (formatter.has_value()) { |
565 | 0 | formatter->processKey(current_header_field_.getStringView()); |
566 | 0 | } |
567 | 4.51M | current_header_field_.inlineTransform([](char c) { return absl::ascii_tolower(c); }); |
568 | | |
569 | 216k | headers_or_trailers.addViaMove(std::move(current_header_field_), |
570 | 216k | std::move(current_header_value_)); |
571 | 216k | } |
572 | | |
573 | | // Check if the number of headers exceeds the limit. |
574 | 239k | if (headers_or_trailers.size() > max_headers_count_) { |
575 | 97 | error_code_ = Http::Code::RequestHeaderFieldsTooLarge; |
576 | 97 | RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().TooManyHeaders)); |
577 | 97 | const absl::string_view header_type = |
578 | 97 | processing_trailers_ ? Http1HeaderTypes::get().Trailers : Http1HeaderTypes::get().Headers; |
579 | 97 | return codecProtocolError( |
580 | 97 | absl::StrCat("http/1.1 protocol error: ", header_type, " count exceeds limit")); |
581 | 97 | } |
582 | | |
583 | 239k | header_parsing_state_ = HeaderParsingState::Field; |
584 | 239k | ASSERT(current_header_field_.empty()); |
585 | 239k | ASSERT(current_header_value_.empty()); |
586 | 239k | return okStatus(); |
587 | 239k | } |
588 | | |
589 | 42.3k | Status ConnectionImpl::onMessageBeginImpl() { |
590 | 42.3k | ENVOY_CONN_LOG(trace, "message begin", connection_); |
591 | | // Make sure that if HTTP/1.0 and HTTP/1.1 requests share a connection Envoy correctly sets |
592 | | // protocol for each request. Envoy defaults to 1.1 but sets the protocol to 1.0 where applicable |
593 | | // in onHeadersCompleteBase |
594 | 42.3k | protocol_ = Protocol::Http11; |
595 | 42.3k | processing_trailers_ = false; |
596 | 42.3k | header_parsing_state_ = HeaderParsingState::Field; |
597 | 42.3k | allocHeaders(statefulFormatterFromSettings(codec_settings_)); |
598 | 42.3k | return onMessageBeginBase(); |
599 | 42.3k | } |
600 | | |
601 | 518k | uint32_t ConnectionImpl::getHeadersSize() { |
602 | 518k | return current_header_field_.size() + current_header_value_.size() + |
603 | 518k | headersOrTrailers().byteSize(); |
604 | 518k | } |
605 | | |
606 | 518k | Status ConnectionImpl::checkMaxHeadersSize() { |
607 | 518k | const uint32_t total = getHeadersSize(); |
608 | 518k | if (total > (max_headers_kb_ * 1024)) { |
609 | 163 | const absl::string_view header_type = |
610 | 163 | processing_trailers_ ? Http1HeaderTypes::get().Trailers : Http1HeaderTypes::get().Headers; |
611 | 163 | error_code_ = Http::Code::RequestHeaderFieldsTooLarge; |
612 | 163 | RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().HeadersTooLarge)); |
613 | 163 | return codecProtocolError( |
614 | 163 | absl::StrCat("http/1.1 protocol error: ", header_type, " size exceeds limit")); |
615 | 163 | } |
616 | 518k | return okStatus(); |
617 | 518k | } |
618 | | |
619 | 152k | bool ConnectionImpl::maybeDirectDispatch(Buffer::Instance& data) { |
620 | 152k | if (!handling_upgrade_) { |
621 | | // Only direct dispatch for Upgrade requests. |
622 | 152k | return false; |
623 | 152k | } |
624 | | |
625 | 527 | ENVOY_CONN_LOG(trace, "direct-dispatched {} bytes", connection_, data.length()); |
626 | 527 | onBody(data); |
627 | 527 | data.drain(data.length()); |
628 | 527 | return true; |
629 | 152k | } |
630 | | |
631 | 89.1k | void ConnectionImpl::onDispatch(const Buffer::Instance& data) { |
632 | 89.1k | getBytesMeter().addWireBytesReceived(data.length()); |
633 | 89.1k | } |
634 | | |
635 | 30.8k | Http::Status ClientConnectionImpl::dispatch(Buffer::Instance& data) { |
636 | 30.8k | Http::Status status = ConnectionImpl::dispatch(data); |
637 | 30.8k | if (status.ok() && data.length() > 0) { |
638 | | // The HTTP/1.1 codec pauses dispatch after a single response is complete. Extraneous data |
639 | | // after a response is complete indicates an error. |
640 | 9 | return codecProtocolError("http/1.1 protocol error: extraneous data after response complete"); |
641 | 9 | } |
642 | 30.8k | return status; |
643 | 30.8k | } |
644 | | |
645 | 89.1k | Http::Status ConnectionImpl::dispatch(Buffer::Instance& data) { |
646 | | // Add self to the Dispatcher's tracked object stack. |
647 | 89.1k | ScopeTrackerScopeState scope(this, connection_.dispatcher()); |
648 | 89.1k | ENVOY_CONN_LOG(trace, "parsing {} bytes", connection_, data.length()); |
649 | | // Make sure that dispatching_ is set to false after dispatching, even when |
650 | | // http_parser exits early with an error code. |
651 | 89.1k | Cleanup cleanup([this]() { dispatching_ = false; }); |
652 | 89.1k | ASSERT(!dispatching_); |
653 | 89.1k | ASSERT(codec_status_.ok()); |
654 | 89.1k | ASSERT(buffered_body_.length() == 0); |
655 | | |
656 | 89.1k | dispatching_ = true; |
657 | 89.1k | onDispatch(data); |
658 | 89.1k | if (maybeDirectDispatch(data)) { |
659 | 341 | return Http::okStatus(); |
660 | 341 | } |
661 | | |
662 | | // Always resume before dispatch. |
663 | 88.8k | parser_->resume(); |
664 | | |
665 | 88.8k | ssize_t total_parsed = 0; |
666 | 88.8k | if (data.length() > 0) { |
667 | 78.2k | current_dispatching_buffer_ = &data; |
668 | 125k | while (data.length() > 0) { |
669 | 85.1k | auto slice = data.frontSlice(); |
670 | 85.1k | dispatching_slice_already_drained_ = false; |
671 | 85.1k | auto statusor_parsed = dispatchSlice(static_cast<const char*>(slice.mem_), slice.len_); |
672 | 85.1k | if (!statusor_parsed.ok()) { |
673 | 25.2k | return statusor_parsed.status(); |
674 | 25.2k | } |
675 | 59.8k | if (!dispatching_slice_already_drained_) { |
676 | 57.4k | ASSERT(statusor_parsed.value() <= slice.len_); |
677 | 57.4k | data.drain(statusor_parsed.value()); |
678 | 57.4k | } |
679 | | |
680 | 59.8k | total_parsed += statusor_parsed.value(); |
681 | 59.8k | if (parser_->getStatus() != ParserStatus::Ok) { |
682 | | // Parse errors trigger an exception in dispatchSlice so we are guaranteed to be paused at |
683 | | // this point. |
684 | 12.6k | ASSERT(parser_->getStatus() == ParserStatus::Paused); |
685 | 12.6k | break; |
686 | 12.6k | } |
687 | 59.8k | } |
688 | 52.9k | current_dispatching_buffer_ = nullptr; |
689 | 52.9k | dispatchBufferedBody(); |
690 | 52.9k | } else { |
691 | 10.5k | auto result = dispatchSlice(nullptr, 0); |
692 | 10.5k | if (!result.ok()) { |
693 | 66 | return result.status(); |
694 | 66 | } |
695 | 10.5k | } |
696 | 63.4k | ASSERT(buffered_body_.length() == 0); |
697 | | |
698 | 63.4k | ENVOY_CONN_LOG(trace, "parsed {} bytes", connection_, total_parsed); |
699 | | |
700 | | // If an upgrade has been handled and there is body data or early upgrade |
701 | | // payload to send on, send it on. |
702 | 63.4k | maybeDirectDispatch(data); |
703 | 63.4k | return Http::okStatus(); |
704 | 63.4k | } |
705 | | |
706 | 95.6k | Envoy::StatusOr<size_t> ConnectionImpl::dispatchSlice(const char* slice, size_t len) { |
707 | 95.6k | ASSERT(codec_status_.ok() && dispatching_); |
708 | 95.6k | const size_t nread = parser_->execute(slice, len); |
709 | 95.6k | if (!codec_status_.ok()) { |
710 | 6.09k | return codec_status_; |
711 | 6.09k | } |
712 | | |
713 | 89.5k | const ParserStatus status = parser_->getStatus(); |
714 | 89.5k | if (status != ParserStatus::Ok && status != ParserStatus::Paused) { |
715 | 19.2k | absl::string_view error = Http1ResponseCodeDetails::get().HttpCodecError; |
716 | 19.2k | if (codec_settings_.use_balsa_parser_) { |
717 | 6.91k | if (parser_->errorMessage() == "headers size exceeds limit" || |
718 | 6.91k | parser_->errorMessage() == "trailers size exceeds limit") { |
719 | 99 | error_code_ = Http::Code::RequestHeaderFieldsTooLarge; |
720 | 99 | error = Http1ResponseCodeDetails::get().HeadersTooLarge; |
721 | 6.81k | } else if (parser_->errorMessage() == "header value contains invalid chars") { |
722 | 349 | error = Http1ResponseCodeDetails::get().InvalidCharacters; |
723 | 349 | } |
724 | 6.91k | } |
725 | 19.2k | RETURN_IF_ERROR(sendProtocolError(error)); |
726 | | // Avoid overwriting the codec_status_ set in the callbacks. |
727 | 19.2k | ASSERT(codec_status_.ok()); |
728 | 19.2k | codec_status_ = |
729 | 19.2k | codecProtocolError(absl::StrCat("http/1.1 protocol error: ", parser_->errorMessage())); |
730 | 19.2k | return codec_status_; |
731 | 19.2k | } |
732 | | |
733 | 70.3k | return nread; |
734 | 89.5k | } |
735 | | |
736 | 41.4k | CallbackResult ConnectionImpl::onMessageBegin() { |
737 | 41.4k | return setAndCheckCallbackStatus(onMessageBeginImpl()); |
738 | 41.4k | } |
739 | | |
740 | 21.8k | CallbackResult ConnectionImpl::onUrl(const char* data, size_t length) { |
741 | 21.8k | return setAndCheckCallbackStatus(onUrlBase(data, length)); |
742 | 21.8k | } |
743 | | |
744 | 12.1k | CallbackResult ConnectionImpl::onStatus(const char* data, size_t length) { |
745 | 12.1k | return setAndCheckCallbackStatus(onStatusBase(data, length)); |
746 | 12.1k | } |
747 | | |
748 | 236k | CallbackResult ConnectionImpl::onHeaderField(const char* data, size_t length) { |
749 | 236k | return setAndCheckCallbackStatus(onHeaderFieldImpl(data, length)); |
750 | 236k | } |
751 | | |
752 | 260k | CallbackResult ConnectionImpl::onHeaderValue(const char* data, size_t length) { |
753 | 260k | return setAndCheckCallbackStatus(onHeaderValueImpl(data, length)); |
754 | 260k | } |
755 | | |
756 | 29.8k | CallbackResult ConnectionImpl::onHeadersComplete() { |
757 | 29.8k | return setAndCheckCallbackStatusOr(onHeadersCompleteImpl()); |
758 | 29.8k | } |
759 | | |
760 | 31.3k | void ConnectionImpl::bufferBody(const char* data, size_t length) { |
761 | 31.3k | auto slice = current_dispatching_buffer_->frontSlice(); |
762 | 31.3k | if (data == slice.mem_ && length == slice.len_) { |
763 | 2.40k | buffered_body_.move(*current_dispatching_buffer_, length); |
764 | 2.40k | dispatching_slice_already_drained_ = true; |
765 | 28.9k | } else { |
766 | 28.9k | buffered_body_.add(data, length); |
767 | 28.9k | } |
768 | 31.3k | } |
769 | | |
770 | 18.3k | CallbackResult ConnectionImpl::onMessageComplete() { |
771 | 18.3k | return setAndCheckCallbackStatusOr(onMessageCompleteImpl()); |
772 | 18.3k | } |
773 | | |
774 | 31.7k | void ConnectionImpl::onChunkHeader(bool is_final_chunk) { |
775 | 31.7k | if (is_final_chunk) { |
776 | | // Dispatch body before parsing trailers, so body ends up dispatched even if an error is found |
777 | | // while processing trailers. |
778 | 5.41k | dispatchBufferedBody(); |
779 | 5.41k | } |
780 | 31.7k | } |
781 | | |
782 | 236k | Status ConnectionImpl::onHeaderFieldImpl(const char* data, size_t length) { |
783 | 236k | ASSERT(dispatching_); |
784 | | |
785 | 236k | getBytesMeter().addHeaderBytesReceived(length); |
786 | | |
787 | | // We previously already finished up the headers, these headers are |
788 | | // now trailers. |
789 | 236k | if (header_parsing_state_ == HeaderParsingState::Done) { |
790 | 202 | if (!enableTrailers()) { |
791 | | // Ignore trailers. |
792 | 89 | return okStatus(); |
793 | 89 | } |
794 | 113 | processing_trailers_ = true; |
795 | 113 | header_parsing_state_ = HeaderParsingState::Field; |
796 | 113 | allocTrailers(); |
797 | 113 | } |
798 | 235k | if (header_parsing_state_ == HeaderParsingState::Value) { |
799 | 209k | RETURN_IF_ERROR(completeCurrentHeader()); |
800 | 209k | } |
801 | | |
802 | 235k | current_header_field_.append(data, length); |
803 | | |
804 | 235k | return checkMaxHeadersSize(); |
805 | 235k | } |
806 | | |
807 | 260k | Status ConnectionImpl::onHeaderValueImpl(const char* data, size_t length) { |
808 | 260k | ASSERT(dispatching_); |
809 | | |
810 | 260k | getBytesMeter().addHeaderBytesReceived(length); |
811 | | |
812 | 260k | if (header_parsing_state_ == HeaderParsingState::Done && !enableTrailers()) { |
813 | | // Ignore trailers. |
814 | 101 | return okStatus(); |
815 | 101 | } |
816 | | |
817 | 260k | absl::string_view header_value{data, length}; |
818 | 260k | if (!Http::HeaderUtility::headerValueIsValid(header_value)) { |
819 | 56 | ENVOY_CONN_LOG(debug, "invalid header value: {}", connection_, header_value); |
820 | 56 | error_code_ = Http::Code::BadRequest; |
821 | 56 | RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().InvalidCharacters)); |
822 | 56 | return codecProtocolError("http/1.1 protocol error: header value contains invalid chars"); |
823 | 56 | } |
824 | | |
825 | 260k | header_parsing_state_ = HeaderParsingState::Value; |
826 | 260k | if (current_header_value_.empty()) { |
827 | | // Strip leading whitespace if the current header value input contains the first bytes of the |
828 | | // encoded header value. Trailing whitespace is stripped once the full header value is known in |
829 | | // ConnectionImpl::completeCurrentHeader. http_parser does not strip leading or trailing |
830 | | // whitespace as the spec requires: https://tools.ietf.org/html/rfc7230#section-3.2.4 . |
831 | 234k | header_value = StringUtil::ltrim(header_value); |
832 | 234k | } |
833 | 260k | current_header_value_.append(header_value.data(), header_value.length()); |
834 | | |
835 | 260k | return checkMaxHeadersSize(); |
836 | 260k | } |
837 | | |
838 | 29.8k | StatusOr<CallbackResult> ConnectionImpl::onHeadersCompleteImpl() { |
839 | 29.8k | ASSERT(!processing_trailers_); |
840 | 29.8k | ASSERT(dispatching_); |
841 | 29.8k | ENVOY_CONN_LOG(trace, "onHeadersCompleteImpl", connection_); |
842 | 29.8k | RETURN_IF_ERROR(completeCurrentHeader()); |
843 | | |
844 | 29.8k | if (!parser_->isHttp11()) { |
845 | | // This is not necessarily true, but it's good enough since higher layers only care if this is |
846 | | // HTTP/1.1 or not. |
847 | 8.47k | protocol_ = Protocol::Http10; |
848 | 8.47k | } |
849 | 29.8k | RequestOrResponseHeaderMap& request_or_response_headers = requestOrResponseHeaders(); |
850 | 29.8k | const Http::HeaderValues& header_values = Http::Headers::get(); |
851 | 29.8k | if (Utility::isUpgrade(request_or_response_headers) && upgradeAllowed()) { |
852 | | // Ignore h2c upgrade requests until we support them. |
853 | | // See https://github.com/envoyproxy/envoy/issues/7161 for details. |
854 | 381 | if (absl::EqualsIgnoreCase(request_or_response_headers.getUpgradeValue(), |
855 | 381 | header_values.UpgradeValues.H2c)) { |
856 | 252 | ENVOY_CONN_LOG(trace, "removing unsupported h2c upgrade headers.", connection_); |
857 | 252 | request_or_response_headers.removeUpgrade(); |
858 | 252 | if (request_or_response_headers.Connection()) { |
859 | 252 | const auto& tokens_to_remove = caseUnorderdSetContainingUpgradeAndHttp2Settings(); |
860 | 252 | std::string new_value = StringUtil::removeTokens( |
861 | 252 | request_or_response_headers.getConnectionValue(), ",", tokens_to_remove, ","); |
862 | 252 | if (new_value.empty()) { |
863 | 7 | request_or_response_headers.removeConnection(); |
864 | 245 | } else { |
865 | 245 | request_or_response_headers.setConnection(new_value); |
866 | 245 | } |
867 | 252 | } |
868 | 252 | request_or_response_headers.remove(header_values.Http2Settings); |
869 | 252 | } else { |
870 | 129 | ENVOY_CONN_LOG(trace, "codec entering upgrade mode.", connection_); |
871 | 129 | handling_upgrade_ = true; |
872 | 129 | } |
873 | 381 | } |
874 | 29.8k | if (parser_->methodName() == header_values.MethodValues.Connect) { |
875 | 1.24k | if (request_or_response_headers.ContentLength()) { |
876 | 577 | if (request_or_response_headers.getContentLengthValue() == "0") { |
877 | 488 | request_or_response_headers.removeContentLength(); |
878 | 488 | } else { |
879 | | // Per https://tools.ietf.org/html/rfc7231#section-4.3.6 a payload with a |
880 | | // CONNECT request has no defined semantics, and may be rejected. |
881 | 89 | error_code_ = Http::Code::BadRequest; |
882 | 89 | RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().BodyDisallowed)); |
883 | 89 | return codecProtocolError("http/1.1 protocol error: unsupported content length"); |
884 | 89 | } |
885 | 577 | } |
886 | 1.15k | ENVOY_CONN_LOG(trace, "codec entering upgrade mode for CONNECT request.", connection_); |
887 | 1.15k | handling_upgrade_ = true; |
888 | 1.15k | } |
889 | | |
890 | | // https://tools.ietf.org/html/rfc7230#section-3.3.3 |
891 | | // If a message is received with both a Transfer-Encoding and a |
892 | | // Content-Length header field, the Transfer-Encoding overrides the |
893 | | // Content-Length. Such a message might indicate an attempt to |
894 | | // perform request smuggling (Section 9.5) or response splitting |
895 | | // (Section 9.4) and ought to be handled as an error. A sender MUST |
896 | | // remove the received Content-Length field prior to forwarding such |
897 | | // a message. |
898 | | |
899 | 29.7k | #ifndef ENVOY_ENABLE_UHV |
900 | | // This check is moved into default header validator. |
901 | | // TODO(yanavlasov): use runtime override here when UHV is moved into the main build |
902 | | |
903 | | // Reject message with Http::Code::BadRequest if both Transfer-Encoding and Content-Length |
904 | | // headers are present or if allowed by http1 codec settings and 'Transfer-Encoding' |
905 | | // is chunked - remove Content-Length and serve request. |
906 | 29.7k | if (parser_->hasTransferEncoding() != 0 && request_or_response_headers.ContentLength()) { |
907 | 48 | if (parser_->isChunked() && codec_settings_.allow_chunked_length_) { |
908 | 0 | request_or_response_headers.removeContentLength(); |
909 | 48 | } else { |
910 | 48 | error_code_ = Http::Code::BadRequest; |
911 | 48 | RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().ChunkedContentLength)); |
912 | 48 | return codecProtocolError( |
913 | 48 | "http/1.1 protocol error: both 'Content-Length' and 'Transfer-Encoding' are set."); |
914 | 48 | } |
915 | 48 | } |
916 | | |
917 | | // Per https://tools.ietf.org/html/rfc7230#section-3.3.1 Envoy should reject |
918 | | // transfer-codings it does not understand. |
919 | | // Per https://tools.ietf.org/html/rfc7231#section-4.3.6 a payload with a |
920 | | // CONNECT request has no defined semantics, and may be rejected. |
921 | 29.7k | if (request_or_response_headers.TransferEncoding()) { |
922 | 10.8k | const absl::string_view encoding = request_or_response_headers.getTransferEncodingValue(); |
923 | 10.8k | if (!absl::EqualsIgnoreCase(encoding, header_values.TransferEncodingValues.Chunked) || |
924 | 10.8k | parser_->methodName() == header_values.MethodValues.Connect) { |
925 | 516 | error_code_ = Http::Code::NotImplemented; |
926 | 516 | RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().InvalidTransferEncoding)); |
927 | 516 | return codecProtocolError("http/1.1 protocol error: unsupported transfer encoding"); |
928 | 516 | } |
929 | 10.8k | } |
930 | 29.2k | #endif |
931 | | |
932 | 29.2k | auto statusor = onHeadersCompleteBase(); |
933 | 29.2k | if (!statusor.ok()) { |
934 | 5.11k | RETURN_IF_ERROR(statusor.status()); |
935 | 5.11k | } |
936 | | |
937 | 24.0k | header_parsing_state_ = HeaderParsingState::Done; |
938 | | |
939 | | // Returning CallbackResult::NoBodyData informs http_parser to not expect a body or further data |
940 | | // on this connection. |
941 | 24.0k | return handling_upgrade_ ? CallbackResult::NoBodyData : statusor.value(); |
942 | 29.2k | } |
943 | | |
944 | 18.3k | StatusOr<CallbackResult> ConnectionImpl::onMessageCompleteImpl() { |
945 | 18.3k | ENVOY_CONN_LOG(trace, "message complete", connection_); |
946 | | |
947 | 18.3k | dispatchBufferedBody(); |
948 | | |
949 | 18.3k | if (handling_upgrade_) { |
950 | | // If this is an upgrade request, swallow the onMessageComplete. The |
951 | | // upgrade payload will be treated as stream body. |
952 | 186 | ASSERT(!deferred_end_stream_headers_); |
953 | 186 | ENVOY_CONN_LOG(trace, "Pausing parser due to upgrade.", connection_); |
954 | 186 | return parser_->pause(); |
955 | 186 | } |
956 | | |
957 | | // If true, this indicates we were processing trailers and must |
958 | | // move the last header into current_header_map_ |
959 | 18.1k | if (header_parsing_state_ == HeaderParsingState::Value) { |
960 | 60 | RETURN_IF_ERROR(completeCurrentHeader()); |
961 | 60 | } |
962 | | |
963 | 18.1k | return onMessageCompleteBase(); |
964 | 18.1k | } |
965 | | |
966 | 76.7k | void ConnectionImpl::dispatchBufferedBody() { |
967 | 76.7k | ASSERT(parser_->getStatus() == ParserStatus::Ok || parser_->getStatus() == ParserStatus::Paused); |
968 | 76.7k | ASSERT(codec_status_.ok()); |
969 | 76.7k | if (buffered_body_.length() > 0) { |
970 | 7.34k | onBody(buffered_body_); |
971 | 7.34k | buffered_body_.drain(buffered_body_.length()); |
972 | 7.34k | } |
973 | 76.7k | } |
974 | | |
975 | 1.84k | void ConnectionImpl::onResetStreamBase(StreamResetReason reason) { |
976 | 1.84k | ASSERT(!reset_stream_called_); |
977 | 1.84k | reset_stream_called_ = true; |
978 | 1.84k | onResetStream(reason); |
979 | 1.84k | } |
980 | | |
981 | 0 | void ConnectionImpl::dumpState(std::ostream& os, int indent_level) const { |
982 | 0 | const char* spaces = spacesForLevel(indent_level); |
983 | 0 | os << spaces << "Http1::ConnectionImpl " << this << DUMP_MEMBER(dispatching_) |
984 | 0 | << DUMP_MEMBER(dispatching_slice_already_drained_) << DUMP_MEMBER(reset_stream_called_) |
985 | 0 | << DUMP_MEMBER(handling_upgrade_) << DUMP_MEMBER(deferred_end_stream_headers_) |
986 | 0 | << DUMP_MEMBER(processing_trailers_) << DUMP_MEMBER(buffered_body_.length()); |
987 | | |
988 | | // Dump header parsing state, and any progress on headers. |
989 | 0 | os << DUMP_MEMBER(header_parsing_state_); |
990 | 0 | os << DUMP_MEMBER_AS(current_header_field_, current_header_field_.getStringView()); |
991 | 0 | os << DUMP_MEMBER_AS(current_header_value_, current_header_value_.getStringView()); |
992 | | |
993 | | // Dump Child |
994 | 0 | os << '\n'; |
995 | 0 | dumpAdditionalState(os, indent_level); |
996 | | |
997 | | // Dump the first slice of the dispatching buffer if not drained escaping |
998 | | // certain characters. We do this last as the slice could be rather large. |
999 | 0 | if (current_dispatching_buffer_ == nullptr || dispatching_slice_already_drained_) { |
1000 | | // Buffer is either null or already drained (in the body). |
1001 | | // Use the macro for consistent formatting. |
1002 | 0 | os << DUMP_NULLABLE_MEMBER(current_dispatching_buffer_, "drained"); |
1003 | 0 | return; |
1004 | 0 | } else { |
1005 | 0 | absl::string_view front_slice = [](Buffer::RawSlice slice) { |
1006 | 0 | return absl::string_view(static_cast<const char*>(slice.mem_), slice.len_); |
1007 | 0 | }(current_dispatching_buffer_->frontSlice()); |
1008 | | |
1009 | | // Dump buffer data escaping \r, \n, \t, ", ', and \. |
1010 | | // This is not the most performant implementation, but we're crashing and |
1011 | | // cannot allocate memory. |
1012 | 0 | os << spaces << "current_dispatching_buffer_ front_slice length: " << front_slice.length() |
1013 | 0 | << " contents: \""; |
1014 | 0 | StringUtil::escapeToOstream(os, front_slice); |
1015 | 0 | os << "\"\n"; |
1016 | 0 | } |
1017 | 0 | } |
1018 | | |
1019 | 0 | void ServerConnectionImpl::dumpAdditionalState(std::ostream& os, int indent_level) const { |
1020 | 0 | const char* spaces = spacesForLevel(indent_level); |
1021 | |
|
1022 | 0 | DUMP_DETAILS(active_request_); |
1023 | 0 | os << '\n'; |
1024 | | |
1025 | | // Dump header map, it may be null if it was moved to the request, and |
1026 | | // request_url. |
1027 | 0 | if (absl::holds_alternative<RequestHeaderMapPtr>(headers_or_trailers_)) { |
1028 | 0 | DUMP_DETAILS(absl::get<RequestHeaderMapPtr>(headers_or_trailers_)); |
1029 | 0 | } else { |
1030 | 0 | DUMP_DETAILS(absl::get<RequestTrailerMapPtr>(headers_or_trailers_)); |
1031 | 0 | } |
1032 | 0 | } |
1033 | | |
1034 | 0 | void ClientConnectionImpl::dumpAdditionalState(std::ostream& os, int indent_level) const { |
1035 | 0 | const char* spaces = spacesForLevel(indent_level); |
1036 | | // Dump header map, it may be null if it was moved to the request. |
1037 | 0 | if (absl::holds_alternative<ResponseHeaderMapPtr>(headers_or_trailers_)) { |
1038 | 0 | DUMP_DETAILS(absl::get<ResponseHeaderMapPtr>(headers_or_trailers_)); |
1039 | 0 | } else { |
1040 | 0 | DUMP_DETAILS(absl::get<ResponseTrailerMapPtr>(headers_or_trailers_)); |
1041 | 0 | } |
1042 | | |
1043 | | // Dump the associated request. |
1044 | 0 | os << spaces << "Dumping corresponding downstream request:"; |
1045 | 0 | if (pending_response_.has_value()) { |
1046 | 0 | os << '\n'; |
1047 | 0 | const ResponseDecoder* decoder = pending_response_.value().decoder_; |
1048 | 0 | DUMP_DETAILS(decoder); |
1049 | 0 | } else { |
1050 | 0 | os << " null\n"; |
1051 | 0 | } |
1052 | 0 | } |
1053 | | |
1054 | | ServerConnectionImpl::ServerConnectionImpl( |
1055 | | Network::Connection& connection, CodecStats& stats, ServerConnectionCallbacks& callbacks, |
1056 | | const Http1Settings& settings, uint32_t max_request_headers_kb, |
1057 | | const uint32_t max_request_headers_count, |
1058 | | envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction |
1059 | | headers_with_underscores_action, |
1060 | | Server::OverloadManager& overload_manager) |
1061 | | : ConnectionImpl(connection, stats, settings, MessageType::Request, max_request_headers_kb, |
1062 | | max_request_headers_count), |
1063 | | callbacks_(callbacks), |
1064 | 6.25k | response_buffer_releasor_([this](const Buffer::OwnedBufferFragmentImpl* fragment) { |
1065 | 6.25k | releaseOutboundResponse(fragment); |
1066 | 6.25k | }), |
1067 | | owned_output_buffer_(connection.dispatcher().getWatermarkFactory().createBuffer( |
1068 | 0 | [&]() -> void { this->onBelowLowWatermark(); }, |
1069 | 0 | [&]() -> void { this->onAboveHighWatermark(); }, |
1070 | 0 | []() -> void { /* TODO(adisuissa): handle overflow watermark */ })), |
1071 | | headers_with_underscores_action_(headers_with_underscores_action), |
1072 | | abort_dispatch_( |
1073 | 28.7k | overload_manager.getLoadShedPoint("envoy.load_shed_points.http1_server_abort_dispatch")) { |
1074 | 28.7k | ENVOY_LOG_ONCE_IF(trace, abort_dispatch_ == nullptr, |
1075 | 28.7k | "LoadShedPoint envoy.load_shed_points.http1_server_abort_dispatch is not " |
1076 | 28.7k | "found. Is it configured?"); |
1077 | 28.7k | owned_output_buffer_->setWatermarks(connection.bufferLimit()); |
1078 | | // Inform parent |
1079 | 28.7k | output_buffer_ = owned_output_buffer_.get(); |
1080 | 28.7k | } Unexecuted instantiation: Envoy::Http::Http1::ServerConnectionImpl::ServerConnectionImpl(Envoy::Network::Connection&, Envoy::Http::Http1::CodecStats&, Envoy::Http::ServerConnectionCallbacks&, Envoy::Http::Http1Settings const&, unsigned int, unsigned int, envoy::config::core::v3::HttpProtocolOptions_HeadersWithUnderscoresAction, Envoy::Server::OverloadManager&) Envoy::Http::Http1::ServerConnectionImpl::ServerConnectionImpl(Envoy::Network::Connection&, Envoy::Http::Http1::CodecStats&, Envoy::Http::ServerConnectionCallbacks&, Envoy::Http::Http1Settings const&, unsigned int, unsigned int, envoy::config::core::v3::HttpProtocolOptions_HeadersWithUnderscoresAction, Envoy::Server::OverloadManager&) Line | Count | Source | 1073 | 28.7k | overload_manager.getLoadShedPoint("envoy.load_shed_points.http1_server_abort_dispatch")) { | 1074 | 28.7k | ENVOY_LOG_ONCE_IF(trace, abort_dispatch_ == nullptr, | 1075 | 28.7k | "LoadShedPoint envoy.load_shed_points.http1_server_abort_dispatch is not " | 1076 | 28.7k | "found. Is it configured?"); | 1077 | 28.7k | owned_output_buffer_->setWatermarks(connection.bufferLimit()); | 1078 | | // Inform parent | 1079 | 28.7k | output_buffer_ = owned_output_buffer_.get(); | 1080 | 28.7k | } |
|
1081 | | |
1082 | 449k | uint32_t ServerConnectionImpl::getHeadersSize() { |
1083 | | // Add in the size of the request URL if processing request headers. |
1084 | 449k | const uint32_t url_size = |
1085 | 449k | (!processing_trailers_ && active_request_) ? active_request_->request_url_.size() : 0; |
1086 | 449k | return url_size + ConnectionImpl::getHeadersSize(); |
1087 | 449k | } |
1088 | | |
1089 | 6.25k | void ServerConnectionImpl::onEncodeComplete() { |
1090 | 6.25k | if (active_request_->remote_complete_) { |
1091 | | // Only do this if remote is complete. If we are replying before the request is complete the |
1092 | | // only logical thing to do is for higher level code to reset() / close the connection so we |
1093 | | // leave the request around so that it can fire reset callbacks. |
1094 | 5.14k | connection_.dispatcher().deferredDelete(std::move(active_request_)); |
1095 | 5.14k | } |
1096 | 6.25k | } |
1097 | | |
1098 | 16.8k | Status ServerConnectionImpl::handlePath(RequestHeaderMap& headers, absl::string_view method) { |
1099 | 16.8k | const Http::HeaderValues& header_values = Http::Headers::get(); |
1100 | 16.8k | HeaderString path(header_values.Path); |
1101 | | |
1102 | 16.8k | bool is_connect = (method == header_values.MethodValues.Connect); |
1103 | | |
1104 | | // The url is relative or a wildcard when the method is OPTIONS. Nothing to do here. |
1105 | 16.8k | if (!is_connect && !active_request_->request_url_.getStringView().empty() && |
1106 | 16.8k | (active_request_->request_url_.getStringView()[0] == '/' || |
1107 | 16.0k | (method == header_values.MethodValues.Options && |
1108 | 8.43k | active_request_->request_url_.getStringView()[0] == '*'))) { |
1109 | 8.43k | headers.addViaMove(std::move(path), std::move(active_request_->request_url_)); |
1110 | 8.43k | return okStatus(); |
1111 | 8.43k | } |
1112 | | |
1113 | | // If absolute_urls and/or connect are not going be handled, copy the url and return. |
1114 | | // This forces the behavior to be backwards compatible with the old codec behavior. |
1115 | | // CONNECT "urls" are actually host:port so look like absolute URLs to the above checks. |
1116 | | // Absolute URLS in CONNECT requests will be rejected below by the URL class validation. |
1117 | | |
1118 | | /** |
1119 | | * @param scheme the scheme to validate |
1120 | | * @return bool true if the scheme is http. |
1121 | | */ |
1122 | 8.37k | if (!codec_settings_.allow_absolute_url_ && !is_connect) { |
1123 | 4.66k | headers.addViaMove(std::move(path), std::move(active_request_->request_url_)); |
1124 | 4.66k | return okStatus(); |
1125 | 4.66k | } |
1126 | | |
1127 | 3.71k | Utility::Url absolute_url; |
1128 | 3.71k | if (!absolute_url.initialize(active_request_->request_url_.getStringView(), is_connect)) { |
1129 | 2.98k | RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().InvalidUrl)); |
1130 | 2.98k | return codecProtocolError("http/1.1 protocol error: invalid url in request line"); |
1131 | 2.98k | } |
1132 | | // RFC7230#5.7 |
1133 | | // When a proxy receives a request with an absolute-form of |
1134 | | // request-target, the proxy MUST ignore the received Host header field |
1135 | | // (if any) and instead replace it with the host information of the |
1136 | | // request-target. A proxy that forwards such a request MUST generate a |
1137 | | // new Host field-value based on the received request-target rather than |
1138 | | // forward the received Host field-value. |
1139 | 728 | headers.setHost(absolute_url.hostAndPort()); |
1140 | | // Add the scheme and validate to ensure no https:// |
1141 | | // requests are accepted over unencrypted connections by front-line Envoys. |
1142 | 728 | if (!is_connect) { |
1143 | 674 | if (Runtime::runtimeFeatureEnabled( |
1144 | 674 | "envoy.reloadable_features.allow_absolute_url_with_mixed_scheme")) { |
1145 | 674 | headers.setScheme(absl::AsciiStrToLower(absolute_url.scheme())); |
1146 | 674 | } else { |
1147 | 0 | headers.setScheme(absolute_url.scheme()); |
1148 | 0 | } |
1149 | 674 | if (!Utility::schemeIsValid(headers.getSchemeValue())) { |
1150 | 529 | RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().InvalidScheme)); |
1151 | 529 | return codecProtocolError("http/1.1 protocol error: invalid scheme"); |
1152 | 529 | } |
1153 | 145 | if (codec_settings_.validate_scheme_ && Utility::schemeIsHttps(absolute_url.scheme()) && |
1154 | 145 | !connection().ssl()) { |
1155 | 0 | error_code_ = Http::Code::Forbidden; |
1156 | 0 | RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().HttpsInPlaintext)); |
1157 | 0 | return codecProtocolError("http/1.1 protocol error: https in the clear"); |
1158 | 0 | } |
1159 | 145 | } |
1160 | | |
1161 | 199 | if (!absolute_url.pathAndQueryParams().empty()) { |
1162 | 147 | headers.setPath(absolute_url.pathAndQueryParams()); |
1163 | 147 | } |
1164 | 199 | active_request_->request_url_.clear(); |
1165 | 199 | return okStatus(); |
1166 | 728 | } |
1167 | | |
1168 | 13.2k | Status ServerConnectionImpl::checkProtocolVersion(RequestHeaderMap& headers) { |
1169 | 13.2k | if (protocol() == Protocol::Http10) { |
1170 | | // Assume this is HTTP/1.0. This is fine for HTTP/0.9 but this code will also affect any |
1171 | | // requests with non-standard version numbers (0.9, 1.3), basically anything which is not |
1172 | | // HTTP/1.1. |
1173 | | // |
1174 | | // The protocol may have shifted in the HTTP/1.0 case so reset it. |
1175 | 4.54k | if (!codec_settings_.accept_http_10_) { |
1176 | | // Send "Upgrade Required" if HTTP/1.0 support is not explicitly configured on. |
1177 | 115 | error_code_ = Http::Code::UpgradeRequired; |
1178 | 115 | RETURN_IF_ERROR(sendProtocolError(StreamInfo::ResponseCodeDetails::get().LowVersion)); |
1179 | 115 | return codecProtocolError("Upgrade required for HTTP/1.0 or HTTP/0.9"); |
1180 | 115 | } |
1181 | 4.42k | if (!headers.Host() && !codec_settings_.default_host_for_http_10_.empty()) { |
1182 | | // Add a default host if configured to do so. |
1183 | 3.94k | headers.setHost(codec_settings_.default_host_for_http_10_); |
1184 | 3.94k | } |
1185 | 4.42k | } |
1186 | 13.1k | return okStatus(); |
1187 | 13.2k | } |
1188 | | |
1189 | 17.3k | Envoy::StatusOr<CallbackResult> ServerConnectionImpl::onHeadersCompleteBase() { |
1190 | | // Handle the case where response happens prior to request complete. It's up to upper layer code |
1191 | | // to disconnect the connection but we shouldn't fire any more events since it doesn't make |
1192 | | // sense. |
1193 | 17.3k | if (active_request_) { |
1194 | 17.3k | auto& headers = absl::get<RequestHeaderMapPtr>(headers_or_trailers_); |
1195 | 17.3k | ENVOY_CONN_LOG(trace, "Server: onHeadersComplete size={}", connection_, headers->size()); |
1196 | | |
1197 | 17.3k | if (!handling_upgrade_ && headers->Connection()) { |
1198 | | // If we fail to sanitize the request, return a 400 to the client |
1199 | 4.12k | if (!Utility::sanitizeConnectionHeader(*headers)) { |
1200 | 534 | absl::string_view header_value = headers->getConnectionValue(); |
1201 | 534 | ENVOY_CONN_LOG(debug, "Invalid nominated headers in Connection: {}", connection_, |
1202 | 534 | header_value); |
1203 | 534 | error_code_ = Http::Code::BadRequest; |
1204 | 534 | RETURN_IF_ERROR( |
1205 | 534 | sendProtocolError(Http1ResponseCodeDetails::get().ConnectionHeaderSanitization)); |
1206 | 534 | return codecProtocolError("Invalid nominated headers in Connection."); |
1207 | 534 | } |
1208 | 4.12k | } |
1209 | | |
1210 | | // Inform the response encoder about any HEAD method, so it can set content |
1211 | | // length and transfer encoding headers correctly. |
1212 | 16.8k | const Http::HeaderValues& header_values = Http::Headers::get(); |
1213 | 16.8k | active_request_->response_encoder_.setIsResponseToHeadRequest(parser_->methodName() == |
1214 | 16.8k | header_values.MethodValues.Head); |
1215 | 16.8k | active_request_->response_encoder_.setIsResponseToConnectRequest( |
1216 | 16.8k | parser_->methodName() == header_values.MethodValues.Connect); |
1217 | | |
1218 | 16.8k | RETURN_IF_ERROR(handlePath(*headers, parser_->methodName())); |
1219 | 13.2k | ASSERT(active_request_->request_url_.empty()); |
1220 | | |
1221 | 13.2k | headers->setMethod(parser_->methodName()); |
1222 | 13.2k | RETURN_IF_ERROR(checkProtocolVersion(*headers)); |
1223 | | |
1224 | | // Make sure the host is valid. |
1225 | 13.1k | auto details = HeaderUtility::requestHeadersValid(*headers); |
1226 | 13.1k | if (details.has_value()) { |
1227 | 419 | RETURN_IF_ERROR(sendProtocolError(details.value().get())); |
1228 | 419 | return codecProtocolError( |
1229 | 419 | "http/1.1 protocol error: request headers failed spec compliance checks"); |
1230 | 419 | } |
1231 | | |
1232 | | // Determine here whether we have a body or not. This uses the new RFC semantics where the |
1233 | | // presence of content-length or chunked transfer-encoding indicates a body vs. a particular |
1234 | | // method. If there is no body, we defer raising decodeHeaders() until the parser is flushed |
1235 | | // with message complete. This allows upper layers to behave like HTTP/2 and prevents a proxy |
1236 | | // scenario where the higher layers stream through and implicitly switch to chunked transfer |
1237 | | // encoding because end stream with zero body length has not yet been indicated. |
1238 | 12.7k | if (parser_->isChunked() || |
1239 | 12.7k | (parser_->contentLength().has_value() && parser_->contentLength().value() > 0) || |
1240 | 12.7k | handling_upgrade_) { |
1241 | 8.16k | active_request_->request_decoder_->decodeHeaders(std::move(headers), false); |
1242 | | |
1243 | | // If the connection has been closed (or is closing) after decoding headers, pause the parser |
1244 | | // so we return control to the caller. |
1245 | 8.16k | if (connection_.state() != Network::Connection::State::Open) { |
1246 | 1 | return parser_->pause(); |
1247 | 1 | } |
1248 | 8.16k | } else { |
1249 | 4.60k | deferred_end_stream_headers_ = true; |
1250 | 4.60k | } |
1251 | 12.7k | } |
1252 | | |
1253 | 12.7k | return CallbackResult::Success; |
1254 | 17.3k | } |
1255 | | |
1256 | 27.9k | Status ServerConnectionImpl::onMessageBeginBase() { |
1257 | 27.9k | if (!resetStreamCalled()) { |
1258 | 27.9k | ASSERT(active_request_ == nullptr); |
1259 | 27.9k | active_request_ = std::make_unique<ActiveRequest>(*this, std::move(bytes_meter_before_stream_)); |
1260 | 27.9k | active_request_->request_decoder_ = &callbacks_.newStream(active_request_->response_encoder_); |
1261 | | |
1262 | | // Check for pipelined request flood as we prepare to accept a new request. |
1263 | | // Parse errors that happen prior to onMessageBegin result in stream termination, it is not |
1264 | | // possible to overflow output buffers with early parse errors. |
1265 | 27.9k | RETURN_IF_ERROR(doFloodProtectionChecks()); |
1266 | 27.9k | } |
1267 | 27.8k | return okStatus(); |
1268 | 27.9k | } |
1269 | | |
1270 | 21.8k | Status ServerConnectionImpl::onUrlBase(const char* data, size_t length) { |
1271 | 21.8k | if (active_request_) { |
1272 | 21.8k | active_request_->request_url_.append(data, length); |
1273 | | |
1274 | 21.8k | RETURN_IF_ERROR(checkMaxHeadersSize()); |
1275 | 21.8k | } |
1276 | | |
1277 | 21.8k | return okStatus(); |
1278 | 21.8k | } |
1279 | | |
1280 | 4.80k | void ServerConnectionImpl::onBody(Buffer::Instance& data) { |
1281 | 4.80k | ASSERT(!deferred_end_stream_headers_); |
1282 | 4.80k | if (active_request_) { |
1283 | 4.80k | ENVOY_CONN_LOG(trace, "body size={}", connection_, data.length()); |
1284 | 4.80k | active_request_->request_decoder_->decodeData(data, false); |
1285 | 4.80k | } |
1286 | 4.80k | } |
1287 | | |
1288 | 60.1k | Http::Status ServerConnectionImpl::dispatch(Buffer::Instance& data) { |
1289 | 60.1k | if (abort_dispatch_ != nullptr && abort_dispatch_->shouldShedLoad()) { |
1290 | 0 | RETURN_IF_ERROR(sendOverloadError()); |
1291 | 0 | return envoyOverloadError("Aborting Server Dispatch"); |
1292 | 0 | } |
1293 | | |
1294 | 60.1k | if (active_request_ != nullptr && active_request_->remote_complete_) { |
1295 | | // Eagerly read disable the connection if the downstream is sending pipelined requests as we |
1296 | | // serially process them. Reading from the connection will be re-enabled after the active |
1297 | | // request is completed. |
1298 | 1.88k | active_request_->response_encoder_.readDisable(true); |
1299 | 1.88k | return okStatus(); |
1300 | 1.88k | } |
1301 | | |
1302 | 58.3k | Http::Status status = ConnectionImpl::dispatch(data); |
1303 | | |
1304 | 58.3k | if (active_request_ != nullptr && active_request_->remote_complete_) { |
1305 | | // Read disable the connection if the downstream is sending additional data while we are working |
1306 | | // on an existing request. Reading from the connection will be re-enabled after the active |
1307 | | // request is completed. |
1308 | 4.03k | if (data.length() > 0) { |
1309 | 821 | active_request_->response_encoder_.readDisable(true); |
1310 | 821 | } |
1311 | 4.03k | } |
1312 | 58.3k | return status; |
1313 | 60.1k | } |
1314 | | |
1315 | 7.26k | CallbackResult ServerConnectionImpl::onMessageCompleteBase() { |
1316 | 7.26k | ASSERT(!handling_upgrade_); |
1317 | 7.26k | if (active_request_) { |
1318 | | |
1319 | | // The request_decoder should be non-null after we've called the newStream on callbacks. |
1320 | 7.26k | ASSERT(active_request_->request_decoder_); |
1321 | 7.26k | active_request_->remote_complete_ = true; |
1322 | | |
1323 | 7.26k | if (deferred_end_stream_headers_) { |
1324 | 4.58k | active_request_->request_decoder_->decodeHeaders( |
1325 | 4.58k | std::move(absl::get<RequestHeaderMapPtr>(headers_or_trailers_)), true); |
1326 | 4.58k | deferred_end_stream_headers_ = false; |
1327 | 4.58k | } else if (processing_trailers_) { |
1328 | 44 | active_request_->request_decoder_->decodeTrailers( |
1329 | 44 | std::move(absl::get<RequestTrailerMapPtr>(headers_or_trailers_))); |
1330 | 2.63k | } else { |
1331 | 2.63k | Buffer::OwnedImpl buffer; |
1332 | 2.63k | active_request_->request_decoder_->decodeData(buffer, true); |
1333 | 2.63k | } |
1334 | | |
1335 | | // Reset to ensure no information from one requests persists to the next. |
1336 | 7.26k | headers_or_trailers_.emplace<RequestHeaderMapPtr>(nullptr); |
1337 | 7.26k | } |
1338 | | |
1339 | | // Always pause the parser so that the calling code can process 1 request at a time and apply |
1340 | | // back pressure. However this means that the calling code needs to detect if there is more data |
1341 | | // in the buffer and dispatch it again. |
1342 | 7.26k | return parser_->pause(); |
1343 | 7.26k | } |
1344 | | |
1345 | 1.32k | void ServerConnectionImpl::onResetStream(StreamResetReason reason) { |
1346 | 1.32k | if (active_request_) { |
1347 | 1.32k | active_request_->response_encoder_.runResetCallbacks(reason); |
1348 | 1.32k | connection_.dispatcher().deferredDelete(std::move(active_request_)); |
1349 | 1.32k | } |
1350 | 1.32k | } |
1351 | | |
1352 | 0 | Status ServerConnectionImpl::sendOverloadError() { |
1353 | 0 | const bool latched_dispatching = dispatching_; |
1354 | | |
1355 | | // The codec might be in the early stages of server dispatching where this isn't yet |
1356 | | // flipped to true. |
1357 | 0 | dispatching_ = true; |
1358 | 0 | error_code_ = Http::Code::InternalServerError; |
1359 | 0 | auto status = sendProtocolError(Envoy::StreamInfo::ResponseCodeDetails::get().Overload); |
1360 | 0 | dispatching_ = latched_dispatching; |
1361 | 0 | return status; |
1362 | 0 | } |
1363 | | |
1364 | 13.8k | Status ServerConnectionImpl::sendProtocolError(absl::string_view details) { |
1365 | | // We do this here because we may get a protocol error before we have a logical stream. |
1366 | 13.8k | if (active_request_ == nullptr) { |
1367 | 879 | RETURN_IF_ERROR(onMessageBeginImpl()); |
1368 | 879 | } |
1369 | 13.8k | ASSERT(active_request_); |
1370 | | |
1371 | 13.8k | active_request_->response_encoder_.setDetails(details); |
1372 | 13.8k | if (!active_request_->response_encoder_.startedResponse()) { |
1373 | 13.8k | active_request_->request_decoder_->sendLocalReply( |
1374 | 13.8k | error_code_, CodeUtility::toString(error_code_), nullptr, absl::nullopt, details); |
1375 | 13.8k | } |
1376 | 13.8k | return okStatus(); |
1377 | 13.8k | } |
1378 | | |
1379 | 0 | void ServerConnectionImpl::onAboveHighWatermark() { |
1380 | 0 | if (active_request_) { |
1381 | 0 | active_request_->response_encoder_.runHighWatermarkCallbacks(); |
1382 | 0 | } |
1383 | 0 | } |
1384 | 0 | void ServerConnectionImpl::onBelowLowWatermark() { |
1385 | 0 | if (active_request_) { |
1386 | 0 | active_request_->response_encoder_.runLowWatermarkCallbacks(); |
1387 | 0 | } |
1388 | 0 | } |
1389 | | |
1390 | | void ServerConnectionImpl::releaseOutboundResponse( |
1391 | 6.25k | const Buffer::OwnedBufferFragmentImpl* fragment) { |
1392 | 6.25k | ASSERT(outbound_responses_ >= 1); |
1393 | 6.25k | --outbound_responses_; |
1394 | 6.25k | delete fragment; |
1395 | 6.25k | } |
1396 | | |
1397 | 202k | Status ServerConnectionImpl::checkHeaderNameForUnderscores() { |
1398 | 202k | #ifndef ENVOY_ENABLE_UHV |
1399 | | // This check has been moved to UHV |
1400 | 202k | if (headers_with_underscores_action_ != envoy::config::core::v3::HttpProtocolOptions::ALLOW && |
1401 | 202k | Http::HeaderUtility::headerNameContainsUnderscore(current_header_field_.getStringView())) { |
1402 | 17.0k | if (headers_with_underscores_action_ == |
1403 | 17.0k | envoy::config::core::v3::HttpProtocolOptions::DROP_HEADER) { |
1404 | 17.0k | ENVOY_CONN_LOG(debug, "Dropping header with invalid characters in its name: {}", connection_, |
1405 | 17.0k | current_header_field_.getStringView()); |
1406 | 17.0k | stats_.incDroppedHeadersWithUnderscores(); |
1407 | 17.0k | current_header_field_.clear(); |
1408 | 17.0k | current_header_value_.clear(); |
1409 | 17.0k | } else { |
1410 | 0 | ENVOY_CONN_LOG(debug, "Rejecting request due to header name with underscores: {}", |
1411 | 0 | connection_, current_header_field_.getStringView()); |
1412 | 0 | error_code_ = Http::Code::BadRequest; |
1413 | 0 | RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().InvalidUnderscore)); |
1414 | 0 | stats_.incRequestsRejectedWithUnderscoresInHeaders(); |
1415 | 0 | return codecProtocolError("http/1.1 protocol error: header name contains underscores"); |
1416 | 0 | } |
1417 | 17.0k | } |
1418 | | #else |
1419 | | // Workaround for gcc not understanding [[maybe_unused]] for class members. |
1420 | | (void)headers_with_underscores_action_; |
1421 | | #endif |
1422 | 202k | return okStatus(); |
1423 | 202k | } |
1424 | | |
1425 | 0 | void ServerConnectionImpl::ActiveRequest::dumpState(std::ostream& os, int indent_level) const { |
1426 | 0 | (void)indent_level; |
1427 | 0 | os << DUMP_MEMBER_AS( |
1428 | 0 | request_url_, !request_url_.getStringView().empty() ? request_url_.getStringView() : "null"); |
1429 | 0 | os << DUMP_MEMBER(response_encoder_.local_end_stream_); |
1430 | 0 | } |
1431 | | |
1432 | | ClientConnectionImpl::ClientConnectionImpl(Network::Connection& connection, CodecStats& stats, |
1433 | | ConnectionCallbacks&, const Http1Settings& settings, |
1434 | | const uint32_t max_response_headers_count, |
1435 | | bool passing_through_proxy) |
1436 | | : ConnectionImpl(connection, stats, settings, MessageType::Response, MAX_RESPONSE_HEADERS_KB, |
1437 | | max_response_headers_count), |
1438 | | owned_output_buffer_(connection.dispatcher().getWatermarkFactory().createBuffer( |
1439 | 0 | [&]() -> void { this->onBelowLowWatermark(); }, |
1440 | 0 | [&]() -> void { this->onAboveHighWatermark(); }, |
1441 | 0 | []() -> void { /* TODO(adisuissa): handle overflow watermark */ })), |
1442 | 27.6k | passing_through_proxy_(passing_through_proxy) { |
1443 | 27.6k | owned_output_buffer_->setWatermarks(connection.bufferLimit()); |
1444 | | // Inform parent |
1445 | 27.6k | output_buffer_ = owned_output_buffer_.get(); |
1446 | 27.6k | } Unexecuted instantiation: Envoy::Http::Http1::ClientConnectionImpl::ClientConnectionImpl(Envoy::Network::Connection&, Envoy::Http::Http1::CodecStats&, Envoy::Http::ConnectionCallbacks&, Envoy::Http::Http1Settings const&, unsigned int, bool) Envoy::Http::Http1::ClientConnectionImpl::ClientConnectionImpl(Envoy::Network::Connection&, Envoy::Http::Http1::CodecStats&, Envoy::Http::ConnectionCallbacks&, Envoy::Http::Http1Settings const&, unsigned int, bool) Line | Count | Source | 1442 | 27.6k | passing_through_proxy_(passing_through_proxy) { | 1443 | 27.6k | owned_output_buffer_->setWatermarks(connection.bufferLimit()); | 1444 | | // Inform parent | 1445 | 27.6k | output_buffer_ = owned_output_buffer_.get(); | 1446 | 27.6k | } |
|
1447 | | |
1448 | 17.0k | bool ClientConnectionImpl::cannotHaveBody() { |
1449 | 17.0k | if (pending_response_.has_value() && pending_response_.value().encoder_.headRequest()) { |
1450 | 714 | ASSERT(!pending_response_done_); |
1451 | 714 | return true; |
1452 | 16.3k | } else if (parser_->statusCode() == Http::Code::NoContent || |
1453 | 16.3k | parser_->statusCode() == Http::Code::NotModified || |
1454 | 16.3k | (parser_->statusCode() >= Http::Code::OK && |
1455 | 16.2k | (parser_->contentLength().has_value() && parser_->contentLength().value() == 0) && |
1456 | 16.2k | !parser_->isChunked())) { |
1457 | 4.87k | return true; |
1458 | 11.4k | } else { |
1459 | 11.4k | return false; |
1460 | 11.4k | } |
1461 | 17.0k | } |
1462 | | |
1463 | 11.6k | RequestEncoder& ClientConnectionImpl::newStream(ResponseDecoder& response_decoder) { |
1464 | | // If reads were disabled due to flow control, we expect reads to always be enabled again before |
1465 | | // reusing this connection. This is done when the response is received. |
1466 | 11.6k | ASSERT(connection_.readEnabled()); |
1467 | | |
1468 | 11.6k | ASSERT(!pending_response_.has_value()); |
1469 | 11.6k | ASSERT(pending_response_done_); |
1470 | 11.6k | pending_response_.emplace(*this, std::move(bytes_meter_before_stream_), &response_decoder); |
1471 | 11.6k | pending_response_done_ = false; |
1472 | 11.6k | return pending_response_.value().encoder_; |
1473 | 11.6k | } |
1474 | | |
1475 | 12.1k | Status ClientConnectionImpl::onStatusBase(const char* data, size_t length) { |
1476 | 12.1k | auto& headers = absl::get<ResponseHeaderMapPtr>(headers_or_trailers_); |
1477 | 12.1k | StatefulHeaderKeyFormatterOptRef formatter(headers->formatter()); |
1478 | 12.1k | if (formatter.has_value()) { |
1479 | 0 | formatter->setReasonPhrase(absl::string_view(data, length)); |
1480 | 0 | } |
1481 | | |
1482 | 12.1k | return okStatus(); |
1483 | 12.1k | } |
1484 | | |
1485 | 11.8k | Envoy::StatusOr<CallbackResult> ClientConnectionImpl::onHeadersCompleteBase() { |
1486 | 11.8k | ENVOY_CONN_LOG(trace, "status_code {}", connection_, enumToInt(parser_->statusCode())); |
1487 | | |
1488 | | // Handle the case where the client is closing a kept alive connection (by sending a 408 |
1489 | | // with a 'Connection: close' header). In this case we just let response flush out followed |
1490 | | // by the remote close. |
1491 | 11.8k | if (!pending_response_.has_value() && !resetStreamCalled()) { |
1492 | 515 | return prematureResponseError("", parser_->statusCode()); |
1493 | 11.3k | } else if (pending_response_.has_value()) { |
1494 | 11.3k | ASSERT(!pending_response_done_); |
1495 | 11.3k | auto& headers = absl::get<ResponseHeaderMapPtr>(headers_or_trailers_); |
1496 | 11.3k | ENVOY_CONN_LOG(trace, "Client: onHeadersComplete size={}", connection_, headers->size()); |
1497 | 11.3k | headers->setStatus(enumToInt(parser_->statusCode())); |
1498 | | |
1499 | 11.3k | if (parser_->statusCode() >= Http::Code::OK && |
1500 | 11.3k | parser_->statusCode() < Http::Code::MultipleChoices && |
1501 | 11.3k | pending_response_.value().encoder_.connectRequest()) { |
1502 | 7 | ENVOY_CONN_LOG(trace, "codec entering upgrade mode for CONNECT response.", connection_); |
1503 | 7 | handling_upgrade_ = true; |
1504 | 7 | } |
1505 | | |
1506 | 11.3k | if (parser_->statusCode() < Http::Code::OK || parser_->statusCode() == Http::Code::NoContent) { |
1507 | 5.71k | if (headers->TransferEncoding()) { |
1508 | 10 | RETURN_IF_ERROR( |
1509 | 10 | sendProtocolError(Http1ResponseCodeDetails::get().TransferEncodingNotAllowed)); |
1510 | 10 | return codecProtocolError( |
1511 | 10 | "http/1.1 protocol error: transfer encoding not allowed in 1xx or 204"); |
1512 | 10 | } |
1513 | | |
1514 | 5.70k | if (headers->ContentLength()) { |
1515 | | // Report a protocol error for non-zero Content-Length, but paper over zero Content-Length. |
1516 | 49 | if (headers->ContentLength()->value().getStringView() != "0") { |
1517 | 11 | RETURN_IF_ERROR( |
1518 | 11 | sendProtocolError(Http1ResponseCodeDetails::get().ContentLengthNotAllowed)); |
1519 | 11 | return codecProtocolError( |
1520 | 11 | "http/1.1 protocol error: content length not allowed in 1xx or 204"); |
1521 | 11 | } |
1522 | | |
1523 | 38 | headers->removeContentLength(); |
1524 | 38 | } |
1525 | 5.70k | } |
1526 | | |
1527 | 11.3k | if (HeaderUtility::isSpecial1xx(*headers)) { |
1528 | 5.62k | pending_response_.value().decoder_->decode1xxHeaders(std::move(headers)); |
1529 | 5.69k | } else if (cannotHaveBody() && !handling_upgrade_) { |
1530 | 2.54k | deferred_end_stream_headers_ = true; |
1531 | 3.15k | } else { |
1532 | 3.15k | pending_response_.value().decoder_->decodeHeaders(std::move(headers), false); |
1533 | 3.15k | } |
1534 | | |
1535 | | // http-parser treats 1xx headers as their own complete response. Swallow the spurious |
1536 | | // onMessageComplete and continue processing for purely informational headers. |
1537 | | // 101-SwitchingProtocols is exempt as all data after the header is proxied through after |
1538 | | // upgrading. |
1539 | 11.3k | if (CodeUtility::is1xx(enumToInt(parser_->statusCode())) && |
1540 | 11.3k | parser_->statusCode() != Http::Code::SwitchingProtocols) { |
1541 | 5.64k | ignore_message_complete_for_1xx_ = true; |
1542 | | // Reset to ensure no information from the 1xx headers is used for the response headers. |
1543 | 5.64k | headers_or_trailers_.emplace<ResponseHeaderMapPtr>(nullptr); |
1544 | 5.64k | } |
1545 | 11.3k | } |
1546 | | |
1547 | | // Here we deal with cases where the response cannot have a body by returning |
1548 | | // CallbackResult::NoBody, but http_parser does not deal with it for us. |
1549 | 11.3k | return cannotHaveBody() ? CallbackResult::NoBody : CallbackResult::Success; |
1550 | 11.8k | } |
1551 | | |
1552 | 71 | bool ClientConnectionImpl::upgradeAllowed() const { |
1553 | 71 | if (pending_response_.has_value()) { |
1554 | 49 | return pending_response_->encoder_.upgradeRequest(); |
1555 | 49 | } |
1556 | 22 | return false; |
1557 | 71 | } |
1558 | | |
1559 | 3.06k | void ClientConnectionImpl::onBody(Buffer::Instance& data) { |
1560 | 3.06k | ASSERT(!deferred_end_stream_headers_); |
1561 | 3.06k | if (pending_response_.has_value()) { |
1562 | 3.06k | ASSERT(!pending_response_done_); |
1563 | 3.06k | pending_response_.value().decoder_->decodeData(data, false); |
1564 | 3.06k | } |
1565 | 3.06k | } |
1566 | | |
1567 | 10.8k | CallbackResult ClientConnectionImpl::onMessageCompleteBase() { |
1568 | 10.8k | ENVOY_CONN_LOG(trace, "message complete", connection_); |
1569 | 10.8k | if (ignore_message_complete_for_1xx_) { |
1570 | 5.62k | ignore_message_complete_for_1xx_ = false; |
1571 | 5.62k | return CallbackResult::Success; |
1572 | 5.62k | } |
1573 | 5.24k | if (pending_response_.has_value()) { |
1574 | 5.24k | ASSERT(!pending_response_done_); |
1575 | | // After calling decodeData() with end stream set to true, we should no longer be able to reset. |
1576 | 5.24k | PendingResponse& response = pending_response_.value(); |
1577 | | // Encoder is used as part of decode* calls later in this function so pending_response_ can not |
1578 | | // be reset just yet. Preserve the state in pending_response_done_ instead. |
1579 | 5.24k | pending_response_done_ = true; |
1580 | | |
1581 | 5.24k | if (deferred_end_stream_headers_) { |
1582 | 2.53k | response.decoder_->decodeHeaders( |
1583 | 2.53k | std::move(absl::get<ResponseHeaderMapPtr>(headers_or_trailers_)), true); |
1584 | 2.53k | deferred_end_stream_headers_ = false; |
1585 | 2.71k | } else if (processing_trailers_) { |
1586 | 0 | response.decoder_->decodeTrailers( |
1587 | 0 | std::move(absl::get<ResponseTrailerMapPtr>(headers_or_trailers_))); |
1588 | 2.71k | } else { |
1589 | 2.71k | Buffer::OwnedImpl buffer; |
1590 | 2.71k | response.decoder_->decodeData(buffer, true); |
1591 | 2.71k | } |
1592 | | |
1593 | | // Reset to ensure no information from one requests persists to the next. |
1594 | 5.24k | pending_response_.reset(); |
1595 | 5.24k | headers_or_trailers_.emplace<ResponseHeaderMapPtr>(nullptr); |
1596 | 5.24k | } |
1597 | | |
1598 | | // Pause the parser after a response is complete. Any remaining data indicates an error. |
1599 | 5.24k | return parser_->pause(); |
1600 | 5.24k | } |
1601 | | |
1602 | 520 | void ClientConnectionImpl::onResetStream(StreamResetReason reason) { |
1603 | | // Only raise reset if we did not already dispatch a complete response. |
1604 | 520 | if (pending_response_.has_value() && !pending_response_done_) { |
1605 | 517 | pending_response_.value().encoder_.runResetCallbacks(reason); |
1606 | 517 | pending_response_done_ = true; |
1607 | 517 | pending_response_.reset(); |
1608 | 517 | } |
1609 | 520 | } |
1610 | | |
1611 | 10.9k | Status ClientConnectionImpl::sendProtocolError(absl::string_view details) { |
1612 | 10.9k | if (pending_response_.has_value()) { |
1613 | 506 | ASSERT(!pending_response_done_); |
1614 | 506 | pending_response_.value().encoder_.setDetails(details); |
1615 | 506 | } |
1616 | 10.9k | return okStatus(); |
1617 | 10.9k | } |
1618 | | |
1619 | 0 | void ClientConnectionImpl::onAboveHighWatermark() { |
1620 | | // This should never happen without an active stream/request. |
1621 | 0 | pending_response_.value().encoder_.runHighWatermarkCallbacks(); |
1622 | 0 | } |
1623 | | |
1624 | 0 | void ClientConnectionImpl::onBelowLowWatermark() { |
1625 | | // This can get called without an active stream/request when the response completion causes us to |
1626 | | // close the connection, but in doing so go below low watermark. |
1627 | 0 | if (pending_response_.has_value() && !pending_response_done_) { |
1628 | 0 | pending_response_.value().encoder_.runLowWatermarkCallbacks(); |
1629 | 0 | } |
1630 | 0 | } |
1631 | | |
1632 | | } // namespace Http1 |
1633 | | } // namespace Http |
1634 | | } // namespace Envoy |