Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/common/http/codec_client.cc
Line
Count
Source (jump to first uncovered line)
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
25.7k
      idle_timeout_(host_->cluster().idleTimeout()) {
28
25.7k
  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
25.7k
    connection_->detectEarlyCloseWhenReadDisabled(false);
33
25.7k
  }
34
25.7k
  connection_->addConnectionCallbacks(*this);
35
25.7k
  connection_->addReadFilter(Network::ReadFilterSharedPtr{new CodecReadFilter(*this)});
36
37
25.7k
  if (idle_timeout_) {
38
1.32k
    idle_timer_ = dispatcher.createTimer([this]() -> void { onIdleTimeout(); });
39
1.32k
    enableIdleTimer();
40
1.32k
  }
41
42
  // We just universally set no delay on connections. Theoretically we might at some point want
43
  // to make this configurable.
44
25.7k
  connection_->noDelay(true);
45
25.7k
}
46
47
25.7k
void CodecClient::connect() {
48
25.7k
  ASSERT(!connect_called_);
49
25.7k
  connect_called_ = true;
50
25.7k
  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
25.7k
  if (!connection_->connecting()) {
54
20.6k
    ASSERT(connection_->state() == Network::Connection::State::Open);
55
20.6k
    connected_ = true;
56
20.6k
  } else {
57
5.07k
    ENVOY_CONN_LOG(debug, "connecting", *connection_);
58
5.07k
    connection_->connect();
59
5.07k
  }
60
25.7k
}
61
62
22.9k
void CodecClient::close(Network::ConnectionCloseType type) { connection_->close(type); }
63
64
28.4k
void CodecClient::deleteRequest(ActiveRequest& request) {
65
28.4k
  connection_->dispatcher().deferredDelete(request.removeFromList(active_requests_));
66
28.4k
  if (codec_client_callbacks_) {
67
3.38k
    codec_client_callbacks_->onStreamDestroy();
68
3.38k
  }
69
28.4k
  if (numActiveRequests() == 0) {
70
28.4k
    enableIdleTimer();
71
28.4k
  }
72
28.4k
}
73
74
28.4k
RequestEncoder& CodecClient::newStream(ResponseDecoder& response_decoder) {
75
28.4k
  ActiveRequestPtr request(new ActiveRequest(*this, response_decoder));
76
28.4k
  request->setEncoder(codec_->newStream(*request));
77
28.4k
  LinkedList::moveIntoList(std::move(request), active_requests_);
78
79
28.4k
  auto upstream_info = connection_->streamInfo().upstreamInfo();
80
28.4k
  upstream_info->setUpstreamNumStreams(upstream_info->upstreamNumStreams() + 1);
81
82
28.4k
  disableIdleTimer();
83
28.4k
  return *active_requests_.front();
84
28.4k
}
85
86
31.5k
void CodecClient::onEvent(Network::ConnectionEvent event) {
87
31.5k
  if (event == Network::ConnectionEvent::Connected) {
88
5.85k
    ENVOY_CONN_LOG(debug, "connected", *connection_);
89
5.85k
    connected_ = true;
90
5.85k
    return;
91
5.85k
  }
92
93
25.7k
  if (event == Network::ConnectionEvent::RemoteClose) {
94
958
    remote_closed_ = true;
95
958
  }
96
97
  // HTTP/1 can signal end of response by disconnecting. We need to handle that case.
98
25.7k
  if (type_ == CodecType::HTTP1 && event == Network::ConnectionEvent::RemoteClose &&
99
25.7k
      !active_requests_.empty()) {
100
325
    Buffer::OwnedImpl empty;
101
325
    onData(empty);
102
325
  }
103
104
25.7k
  if (event == Network::ConnectionEvent::RemoteClose ||
105
25.7k
      event == Network::ConnectionEvent::LocalClose) {
106
25.7k
    ENVOY_CONN_LOG(debug, "disconnect. resetting {} pending requests", *connection_,
107
25.7k
                   active_requests_.size());
108
25.7k
    disableIdleTimer();
109
25.7k
    idle_timer_.reset();
110
25.7k
    StreamResetReason reason = event == Network::ConnectionEvent::RemoteClose
111
25.7k
                                   ? StreamResetReason::RemoteConnectionFailure
112
25.7k
                                   : StreamResetReason::LocalConnectionFailure;
113
25.7k
    if (connected_) {
114
25.7k
      reason = StreamResetReason::ConnectionTermination;
115
25.7k
      if (protocol_error_) {
116
89
        reason = StreamResetReason::ProtocolError;
117
89
        connection_->streamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamProtocolError);
118
89
      }
119
25.7k
    }
120
31.8k
    while (!active_requests_.empty()) {
121
      // Fake resetting all active streams so that reset() callbacks get invoked.
122
6.09k
      active_requests_.front()->getStream().resetStream(reason);
123
6.09k
    }
124
25.7k
  }
125
25.7k
}
126
127
8.14k
void CodecClient::responsePreDecodeComplete(ActiveRequest& request) {
128
8.14k
  ENVOY_CONN_LOG(debug, "response complete", *connection_);
129
8.14k
  if (codec_client_callbacks_) {
130
2.26k
    codec_client_callbacks_->onStreamPreDecodeComplete();
131
2.26k
  }
132
8.14k
  request.decode_complete_ = true;
133
8.14k
  if (request.encode_complete_ || !request.wait_encode_complete_) {
134
8.05k
    completeRequest(request);
135
8.05k
  } else {
136
96
    ENVOY_CONN_LOG(debug, "waiting for encode to complete", *connection_);
137
96
  }
138
8.14k
}
139
140
28.3k
void CodecClient::requestEncodeComplete(ActiveRequest& request) {
141
28.3k
  ENVOY_CONN_LOG(debug, "encode complete", *connection_);
142
28.3k
  request.encode_complete_ = true;
143
28.3k
  if (request.decode_complete_) {
144
0
    completeRequest(request);
145
0
  }
146
28.3k
}
147
148
8.05k
void CodecClient::completeRequest(ActiveRequest& request) {
149
8.05k
  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
8.05k
  request.removeEncoderCallbacks();
155
8.05k
}
156
157
20.4k
void CodecClient::onReset(ActiveRequest& request, StreamResetReason reason) {
158
20.4k
  ENVOY_CONN_LOG(debug, "request reset", *connection_);
159
20.4k
  if (codec_client_callbacks_) {
160
1.21k
    codec_client_callbacks_->onStreamReset(reason);
161
1.21k
  }
162
163
20.4k
  deleteRequest(request);
164
20.4k
}
165
166
11.6k
void CodecClient::onData(Buffer::Instance& data) {
167
11.6k
  const Status status = codec_->dispatch(data);
168
169
11.6k
  if (!status.ok()) {
170
691
    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
691
    if (!isPrematureResponseError(status) ||
174
691
        (!active_requests_.empty() ||
175
691
         getPrematureResponseHttpCode(status) != Code::RequestTimeout)) {
176
691
      host_->cluster().trafficStats()->upstream_cx_protocol_error_.inc();
177
691
      protocol_error_ = true;
178
691
    }
179
691
    close();
180
691
  }
181
182
  // All data should be consumed at this point if the connection remains open.
183
11.6k
  ASSERT(data.length() == 0 || connection_->state() != Network::Connection::State::Open,
184
11.6k
         absl::StrCat("extraneous bytes after response complete: ", data.length()));
185
11.6k
}
186
187
28.4k
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
28.4k
  return RequestEncoderWrapper::encodeHeaders(headers, end_stream);
222
28.4k
}
223
224
9.78k
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
9.78k
  ResponseDecoderWrapper::decodeHeaders(std::move(headers), end_stream);
260
9.78k
}
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
5.07k
                               options) {
269
5.07k
  connect();
270
5.07k
}
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
5.07k
    : CodecClient(type, std::move(connection), host, dispatcher) {
278
5.07k
  switch (type) {
279
2.70k
  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
2.70k
    bool proxied = false;
283
2.70k
    if (options && options->http11ProxyInfo().has_value()) {
284
0
      proxied = true;
285
0
    }
286
2.70k
    codec_ = std::make_unique<Http1::ClientConnectionImpl>(
287
2.70k
        *connection_, host->cluster().http1CodecStats(), *this, host->cluster().http1Settings(),
288
2.70k
        host->cluster().maxResponseHeadersCount(), proxied);
289
2.70k
    break;
290
0
  }
291
2.37k
  case CodecType::HTTP2:
292
2.37k
    codec_ = std::make_unique<Http2::ClientConnectionImpl>(
293
2.37k
        *connection_, *this, host->cluster().http2CodecStats(), random_generator,
294
2.37k
        host->cluster().http2Options(), Http::DEFAULT_MAX_REQUEST_HEADERS_KB,
295
2.37k
        host->cluster().maxResponseHeadersCount(), Http2::ProdNghttp2SessionFactory::get());
296
2.37k
    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
5.07k
  }
313
5.07k
}
314
315
} // namespace Http
316
} // namespace Envoy