Line data Source code
1 : #include "source/common/http/codec_client.h"
2 :
3 : #include <cstdint>
4 : #include <memory>
5 :
6 : #include "envoy/http/codec.h"
7 :
8 : #include "source/common/common/enum_to_int.h"
9 : #include "source/common/config/utility.h"
10 : #include "source/common/http/exception.h"
11 : #include "source/common/http/http1/codec_impl.h"
12 : #include "source/common/http/http2/codec_impl.h"
13 : #include "source/common/http/status.h"
14 : #include "source/common/http/utility.h"
15 :
16 : #ifdef ENVOY_ENABLE_QUIC
17 : #include "source/common/quic/client_codec_impl.h"
18 : #endif
19 :
20 : namespace Envoy {
21 : namespace Http {
22 :
23 : CodecClient::CodecClient(CodecType type, Network::ClientConnectionPtr&& connection,
24 : Upstream::HostDescriptionConstSharedPtr host,
25 : Event::Dispatcher& dispatcher)
26 : : type_(type), host_(host), connection_(std::move(connection)),
27 365 : idle_timeout_(host_->cluster().idleTimeout()) {
28 365 : if (type_ != CodecType::HTTP3) {
29 : // Make sure upstream connections process data and then the FIN, rather than processing
30 : // TCP disconnects immediately. (see https://github.com/envoyproxy/envoy/issues/1679 for
31 : // details)
32 365 : connection_->detectEarlyCloseWhenReadDisabled(false);
33 365 : }
34 365 : connection_->addConnectionCallbacks(*this);
35 365 : connection_->addReadFilter(Network::ReadFilterSharedPtr{new CodecReadFilter(*this)});
36 :
37 365 : if (idle_timeout_) {
38 173 : idle_timer_ = dispatcher.createTimer([this]() -> void { onIdleTimeout(); });
39 173 : enableIdleTimer();
40 173 : }
41 :
42 : // We just universally set no delay on connections. Theoretically we might at some point want
43 : // to make this configurable.
44 365 : connection_->noDelay(true);
45 365 : }
46 :
47 365 : void CodecClient::connect() {
48 365 : ASSERT(!connect_called_);
49 365 : connect_called_ = true;
50 365 : ASSERT(codec_ != nullptr);
51 : // In general, codecs are handed new not-yet-connected connections, but in the
52 : // case of ALPN, the codec may be handed an already connected connection.
53 365 : if (!connection_->connecting()) {
54 54 : ASSERT(connection_->state() == Network::Connection::State::Open);
55 54 : connected_ = true;
56 365 : } else {
57 311 : ENVOY_CONN_LOG(debug, "connecting", *connection_);
58 311 : connection_->connect();
59 311 : }
60 365 : }
61 :
62 271 : void CodecClient::close(Network::ConnectionCloseType type) { connection_->close(type); }
63 :
64 398 : void CodecClient::deleteRequest(ActiveRequest& request) {
65 398 : connection_->dispatcher().deferredDelete(request.removeFromList(active_requests_));
66 398 : if (codec_client_callbacks_) {
67 188 : codec_client_callbacks_->onStreamDestroy();
68 188 : }
69 398 : if (numActiveRequests() == 0) {
70 398 : enableIdleTimer();
71 398 : }
72 398 : }
73 :
74 398 : RequestEncoder& CodecClient::newStream(ResponseDecoder& response_decoder) {
75 398 : ActiveRequestPtr request(new ActiveRequest(*this, response_decoder));
76 398 : request->setEncoder(codec_->newStream(*request));
77 398 : LinkedList::moveIntoList(std::move(request), active_requests_);
78 :
79 398 : auto upstream_info = connection_->streamInfo().upstreamInfo();
80 398 : upstream_info->setUpstreamNumStreams(upstream_info->upstreamNumStreams() + 1);
81 :
82 398 : disableIdleTimer();
83 398 : return *active_requests_.front();
84 398 : }
85 :
86 669 : void CodecClient::onEvent(Network::ConnectionEvent event) {
87 669 : if (event == Network::ConnectionEvent::Connected) {
88 304 : ENVOY_CONN_LOG(debug, "connected", *connection_);
89 304 : connected_ = true;
90 304 : return;
91 304 : }
92 :
93 365 : if (event == Network::ConnectionEvent::RemoteClose) {
94 110 : remote_closed_ = true;
95 110 : }
96 :
97 : // HTTP/1 can signal end of response by disconnecting. We need to handle that case.
98 365 : if (type_ == CodecType::HTTP1 && event == Network::ConnectionEvent::RemoteClose &&
99 365 : !active_requests_.empty()) {
100 8 : Buffer::OwnedImpl empty;
101 8 : onData(empty);
102 8 : }
103 :
104 365 : if (event == Network::ConnectionEvent::RemoteClose ||
105 365 : event == Network::ConnectionEvent::LocalClose) {
106 365 : ENVOY_CONN_LOG(debug, "disconnect. resetting {} pending requests", *connection_,
107 365 : active_requests_.size());
108 365 : disableIdleTimer();
109 365 : idle_timer_.reset();
110 365 : StreamResetReason reason = event == Network::ConnectionEvent::RemoteClose
111 365 : ? StreamResetReason::RemoteConnectionFailure
112 365 : : StreamResetReason::LocalConnectionFailure;
113 365 : if (connected_) {
114 357 : reason = StreamResetReason::ConnectionTermination;
115 357 : if (protocol_error_) {
116 63 : reason = StreamResetReason::ProtocolError;
117 63 : connection_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamProtocolError);
118 63 : }
119 357 : }
120 468 : while (!active_requests_.empty()) {
121 : // Fake resetting all active streams so that reset() callbacks get invoked.
122 103 : active_requests_.front()->getStream().resetStream(reason);
123 103 : }
124 365 : }
125 365 : }
126 :
127 260 : void CodecClient::responsePreDecodeComplete(ActiveRequest& request) {
128 260 : ENVOY_CONN_LOG(debug, "response complete", *connection_);
129 260 : if (codec_client_callbacks_) {
130 105 : codec_client_callbacks_->onStreamPreDecodeComplete();
131 105 : }
132 260 : request.decode_complete_ = true;
133 260 : if (request.encode_complete_ || !request.wait_encode_complete_) {
134 235 : completeRequest(request);
135 235 : } else {
136 25 : ENVOY_CONN_LOG(debug, "waiting for encode to complete", *connection_);
137 25 : }
138 260 : }
139 :
140 343 : void CodecClient::requestEncodeComplete(ActiveRequest& request) {
141 343 : ENVOY_CONN_LOG(debug, "encode complete", *connection_);
142 343 : request.encode_complete_ = true;
143 343 : if (request.decode_complete_) {
144 0 : completeRequest(request);
145 0 : }
146 343 : }
147 :
148 235 : void CodecClient::completeRequest(ActiveRequest& request) {
149 235 : deleteRequest(request);
150 :
151 : // HTTP/2 can send us a reset after a complete response if the request was not complete. Users
152 : // of CodecClient will deal with the premature response case and we should not handle any
153 : // further reset notification.
154 235 : request.removeEncoderCallbacks();
155 235 : }
156 :
157 163 : void CodecClient::onReset(ActiveRequest& request, StreamResetReason reason) {
158 163 : ENVOY_CONN_LOG(debug, "request reset", *connection_);
159 163 : if (codec_client_callbacks_) {
160 108 : codec_client_callbacks_->onStreamReset(reason);
161 108 : }
162 :
163 163 : deleteRequest(request);
164 163 : }
165 :
166 683 : void CodecClient::onData(Buffer::Instance& data) {
167 683 : const Status status = codec_->dispatch(data);
168 :
169 683 : if (!status.ok()) {
170 68 : ENVOY_CONN_LOG(debug, "Error dispatching received data: {}", *connection_, status.message());
171 :
172 : // Don't count 408 responses where we have no active requests as protocol errors
173 68 : if (!isPrematureResponseError(status) ||
174 68 : (!active_requests_.empty() ||
175 68 : getPrematureResponseHttpCode(status) != Code::RequestTimeout)) {
176 68 : host_->cluster().trafficStats()->upstream_cx_protocol_error_.inc();
177 68 : protocol_error_ = true;
178 68 : }
179 68 : close();
180 68 : }
181 :
182 : // All data should be consumed at this point if the connection remains open.
183 683 : ASSERT(data.length() == 0 || connection_->state() != Network::Connection::State::Open,
184 683 : absl::StrCat("extraneous bytes after response complete: ", data.length()));
185 683 : }
186 :
187 398 : Status CodecClient::ActiveRequest::encodeHeaders(const RequestHeaderMap& headers, bool end_stream) {
188 : #ifdef ENVOY_ENABLE_UHV
189 : if (header_validator_) {
190 : bool failure = false;
191 : std::string failure_details;
192 : auto transformation_result = header_validator_->transformRequestHeaders(headers);
193 : if (!transformation_result.status.ok()) {
194 : ENVOY_CONN_LOG(debug, "Request header transformation failed: {}\n{}", *parent_.connection_,
195 : transformation_result.status.details(), headers);
196 : failure = true;
197 : failure_details = std::string(transformation_result.status.details());
198 : } else {
199 : // Validate header map after request encoder transformations
200 : const ::Envoy::Http::HeaderValidator::ValidationResult validation_result =
201 : header_validator_->validateRequestHeaders(
202 : transformation_result.new_headers ? *transformation_result.new_headers : headers);
203 : if (!validation_result.ok()) {
204 : ENVOY_CONN_LOG(debug, "Request header validation failed: {}\n{}", *parent_.connection_,
205 : validation_result.details(),
206 : transformation_result.new_headers ? *transformation_result.new_headers
207 : : headers);
208 : failure = true;
209 : failure_details = std::string(validation_result.details());
210 : }
211 : }
212 : if (failure) {
213 : return absl::InvalidArgumentError(
214 : absl::StrCat("header validation failed: ", failure_details));
215 : }
216 : return RequestEncoderWrapper::encodeHeaders(
217 : transformation_result.new_headers ? *transformation_result.new_headers : headers,
218 : end_stream);
219 : }
220 : #endif
221 398 : return RequestEncoderWrapper::encodeHeaders(headers, end_stream);
222 398 : }
223 :
224 305 : void CodecClient::ActiveRequest::decodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream) {
225 : #ifdef ENVOY_ENABLE_UHV
226 : if (header_validator_) {
227 : const ::Envoy::Http::HeaderValidator::ValidationResult validation_result =
228 : header_validator_->validateResponseHeaders(*headers);
229 : bool failure = !validation_result.ok();
230 : std::string failure_details(validation_result.details());
231 : if (!failure) {
232 : const ::Envoy::Http::ClientHeaderValidator::TransformationResult transformation_result =
233 : header_validator_->transformResponseHeaders(*headers);
234 : failure = !transformation_result.ok();
235 : failure_details = std::string(validation_result.details());
236 : }
237 : if (failure) {
238 : ENVOY_CONN_LOG(debug, "Response header validation failed: {}\n{}", *parent_.connection_,
239 : failure_details, *headers);
240 : if ((parent_.codec_->protocol() == Protocol::Http2 &&
241 : !parent_.host_->cluster()
242 : .http2Options()
243 : .override_stream_error_on_invalid_http_message()
244 : .value()) ||
245 : (parent_.codec_->protocol() == Protocol::Http3 &&
246 : !parent_.host_->cluster()
247 : .http3Options()
248 : .override_stream_error_on_invalid_http_message()
249 : .value())) {
250 : parent_.protocol_error_ = true;
251 : parent_.close();
252 : } else {
253 : inner_encoder_->getStream().resetStream(StreamResetReason::ProtocolError);
254 : }
255 : return;
256 : }
257 : }
258 : #endif
259 305 : ResponseDecoderWrapper::decodeHeaders(std::move(headers), end_stream);
260 305 : }
261 :
262 : CodecClientProd::CodecClientProd(CodecType type, Network::ClientConnectionPtr&& connection,
263 : Upstream::HostDescriptionConstSharedPtr host,
264 : Event::Dispatcher& dispatcher,
265 : Random::RandomGenerator& random_generator,
266 : const Network::TransportSocketOptionsConstSharedPtr& options)
267 : : NoConnectCodecClientProd(type, std::move(connection), host, dispatcher, random_generator,
268 311 : options) {
269 311 : connect();
270 311 : }
271 :
272 : NoConnectCodecClientProd::NoConnectCodecClientProd(
273 : CodecType type, Network::ClientConnectionPtr&& connection,
274 : Upstream::HostDescriptionConstSharedPtr host, Event::Dispatcher& dispatcher,
275 : Random::RandomGenerator& random_generator,
276 : const Network::TransportSocketOptionsConstSharedPtr& options)
277 311 : : CodecClient(type, std::move(connection), host, dispatcher) {
278 311 : switch (type) {
279 166 : case CodecType::HTTP1: {
280 : // If the transport socket indicates this is being proxied, inform the HTTP/1.1 codec. It will
281 : // send fully qualified URLs iff the underlying transport is plaintext.
282 166 : bool proxied = false;
283 166 : if (options && options->http11ProxyInfo().has_value()) {
284 0 : proxied = true;
285 0 : }
286 166 : codec_ = std::make_unique<Http1::ClientConnectionImpl>(
287 166 : *connection_, host->cluster().http1CodecStats(), *this, host->cluster().http1Settings(),
288 166 : host->cluster().maxResponseHeadersCount(), proxied);
289 166 : break;
290 0 : }
291 145 : case CodecType::HTTP2:
292 145 : codec_ = std::make_unique<Http2::ClientConnectionImpl>(
293 145 : *connection_, *this, host->cluster().http2CodecStats(), random_generator,
294 145 : host->cluster().http2Options(), Http::DEFAULT_MAX_REQUEST_HEADERS_KB,
295 145 : host->cluster().maxResponseHeadersCount(), Http2::ProdNghttp2SessionFactory::get());
296 145 : break;
297 0 : case CodecType::HTTP3: {
298 0 : #ifdef ENVOY_ENABLE_QUIC
299 0 : auto& quic_session = dynamic_cast<Quic::EnvoyQuicClientSession&>(*connection_);
300 0 : codec_ = std::make_unique<Quic::QuicHttpClientConnectionImpl>(
301 0 : quic_session, *this, host->cluster().http3CodecStats(), host->cluster().http3Options(),
302 0 : Http::DEFAULT_MAX_REQUEST_HEADERS_KB, host->cluster().maxResponseHeadersCount());
303 : // Initialize the session after max request header size is changed in above http client
304 : // connection creation.
305 0 : quic_session.Initialize();
306 0 : break;
307 : #else
308 : // Should be blocked by configuration checking at an earlier point.
309 : PANIC("unexpected");
310 : #endif
311 0 : }
312 311 : }
313 311 : }
314 :
315 : } // namespace Http
316 : } // namespace Envoy
|