1
#include "source/common/quic/envoy_quic_server_session.h"
2

            
3
#include <iterator>
4
#include <memory>
5
#include <type_traits>
6
#include <utility>
7

            
8
#include "envoy/event/dispatcher.h"
9

            
10
#include "source/common/common/assert.h"
11
#include "source/common/common/logger.h"
12
#include "source/common/common/scope_tracker.h"
13
#include "source/common/http/session_idle_list_interface.h"
14
#include "source/common/quic/envoy_quic_connection_debug_visitor_factory_interface.h"
15
#include "source/common/quic/envoy_quic_proof_source.h"
16
#include "source/common/quic/envoy_quic_server_connection.h"
17
#include "source/common/quic/envoy_quic_server_stream.h"
18
#include "source/common/quic/quic_filter_manager_connection_impl.h"
19

            
20
#include "absl/types/optional.h"
21
#include "quiche/quic/core/quic_config.h"
22
#include "quiche/quic/core/quic_error_codes.h"
23
#include "quiche/quic/core/quic_stream.h"
24
#include "quiche/quic/core/quic_types.h"
25
#include "quiche/quic/core/quic_versions.h"
26

            
27
namespace Envoy {
28
namespace Quic {
29

            
30
namespace {
31
class EnvoyQuicConnectionContextListener : public quic::QuicConnectionContextListener {
32
public:
33
  EnvoyQuicConnectionContextListener(const ScopeTrackedObject* object, Event::ScopeTracker& tracker)
34
3009
      : object_(object), tracker_(tracker) {}
35

            
36
private:
37
261314
  void Activate() override { state_.emplace(object_, tracker_); }
38
261313
  void Deactivate() override { state_.reset(); }
39

            
40
  const ScopeTrackedObject* object_;
41
  Event::ScopeTracker& tracker_;
42
  absl::optional<ScopeTrackerScopeState> state_;
43
};
44
} // namespace
45

            
46
EnvoyQuicServerSession::EnvoyQuicServerSession(
47
    const quic::QuicConfig& config, const quic::ParsedQuicVersionVector& supported_versions,
48
    std::unique_ptr<EnvoyQuicServerConnection> connection, quic::QuicSession::Visitor* visitor,
49
    quic::QuicCryptoServerStream::Helper* helper, const quic::QuicCryptoServerConfig* crypto_config,
50
    quic::QuicCompressedCertsCache* compressed_certs_cache, Event::Dispatcher& dispatcher,
51
    uint32_t send_buffer_limit, QuicStatNames& quic_stat_names, Stats::Scope& listener_scope,
52
    EnvoyQuicCryptoServerStreamFactoryInterface& crypto_server_stream_factory,
53
    std::unique_ptr<StreamInfo::StreamInfo>&& stream_info, QuicConnectionStats& connection_stats,
54
    EnvoyQuicConnectionDebugVisitorFactoryInterfaceOptRef debug_visitor_factory,
55
    Http::SessionIdleListInterface* session_idle_list)
56
3009
    : quic::QuicServerSessionBase(config, supported_versions, connection.get(), visitor, helper,
57
3009
                                  crypto_config, compressed_certs_cache),
58
3009
      QuicFilterManagerConnectionImpl(*connection, connection->connection_id(), dispatcher,
59
3009
                                      send_buffer_limit,
60
3009
                                      std::make_shared<QuicSslConnectionInfo>(*this),
61
3009
                                      std::move(stream_info), quic_stat_names, listener_scope),
62
3009
      quic_connection_(std::move(connection)),
63
3009
      crypto_server_stream_factory_(crypto_server_stream_factory),
64
3009
      connection_stats_(connection_stats), session_idle_list_(session_idle_list) {
65
3009
#ifdef ENVOY_ENABLE_HTTP_DATAGRAMS
66
3009
  http_datagram_support_ = quic::HttpDatagramSupport::kRfc;
67
3009
#endif
68
  // If a factory is available, create a debug visitor and attach it to the connection.
69
3009
  if (debug_visitor_factory.has_value()) {
70
39
    debug_visitor_ =
71
39
        debug_visitor_factory->createQuicConnectionDebugVisitor(dispatcher, *this, streamInfo());
72
39
    quic_connection_->set_debug_visitor(debug_visitor_.get());
73
39
  }
74
3009
  quic_connection_->set_context_listener(
75
3009
      std::make_unique<EnvoyQuicConnectionContextListener>(this, dispatcher));
76
3009
}
77

            
78
3009
EnvoyQuicServerSession::~EnvoyQuicServerSession() {
79
3009
  ASSERT(!quic_connection_->connected());
80
  // Session is destroyed, remove from idle list.
81
3009
  MaybeRemoveSessionFromIdleList();
82
3009
  QuicFilterManagerConnectionImpl::network_connection_ = nullptr;
83
3009
}
84

            
85
2
absl::string_view EnvoyQuicServerSession::requestedServerName() const {
86
2
  return {GetCryptoStream()->crypto_negotiated_params().sni};
87
2
}
88

            
89
std::unique_ptr<quic::QuicCryptoServerStreamBase>
90
EnvoyQuicServerSession::CreateQuicCryptoServerStream(
91
    const quic::QuicCryptoServerConfig* crypto_config,
92
3009
    quic::QuicCompressedCertsCache* compressed_certs_cache) {
93
3009
  return crypto_server_stream_factory_.createEnvoyQuicCryptoServerStream(
94
3009
      crypto_config, compressed_certs_cache, this, stream_helper(),
95
3009
      makeOptRefFromPtr(position_.has_value() ? &position_->filter_chain_.transportSocketFactory()
96
3009
                                              : nullptr),
97
3009
      dispatcher());
98
3009
}
99

            
100
3983
quic::QuicSpdyStream* EnvoyQuicServerSession::CreateIncomingStream(quic::QuicStreamId id) {
101
3983
  if (!ShouldCreateIncomingStream(id)) {
102
    return nullptr;
103
  }
104
3983
  if (!codec_stats_.has_value() || !http3_options_.has_value()) {
105
1
    ENVOY_BUG(false,
106
1
              fmt::format(
107
1
                  "Quic session {} attempts to create stream {} before HCM filter is initialized.",
108
1
                  this->id(), id));
109
1
    return nullptr;
110
1
  }
111
3982
  auto stream = new EnvoyQuicServerStream(id, this, quic::BIDIRECTIONAL, codec_stats_.value(),
112
3982
                                          http3_options_.value(), headers_with_underscores_action_);
113
3982
  ActivateStream(absl::WrapUnique(stream));
114
3982
  if (aboveHighWatermark()) {
115
4
    stream->runHighWatermarkCallbacks();
116
4
  }
117
3982
  setUpRequestDecoder(*stream);
118
3982
  return stream;
119
3983
}
120

            
121
quic::QuicSpdyStream*
122
EnvoyQuicServerSession::CreateIncomingStream(quic::PendingStream* /*pending*/) {
123
  IS_ENVOY_BUG("Unexpected disallowed server push call");
124
  return nullptr;
125
}
126

            
127
quic::QuicSpdyStream* EnvoyQuicServerSession::CreateOutgoingBidirectionalStream() {
128
  IS_ENVOY_BUG("Unexpected disallowed server initiated stream");
129
  return nullptr;
130
}
131

            
132
3982
void EnvoyQuicServerSession::setUpRequestDecoder(EnvoyQuicServerStream& stream) {
133
3982
  ASSERT(http_connection_callbacks_ != nullptr);
134
3982
  Http::RequestDecoder& decoder = http_connection_callbacks_->newStream(stream);
135
3982
  stream.setRequestDecoder(decoder);
136
3982
}
137

            
138
void EnvoyQuicServerSession::OnConnectionClosed(const quic::QuicConnectionCloseFrame& frame,
139
3009
                                                quic::ConnectionCloseSource source) {
140
3009
  quic::QuicServerSessionBase::OnConnectionClosed(frame, source);
141
3009
  MaybeRemoveSessionFromIdleList();
142
3009
  if (source == quic::ConnectionCloseSource::FROM_SELF) {
143
1677
    setLocalCloseReason(frame.error_details);
144
1677
  }
145
3009
  onConnectionCloseEvent(frame, source, version());
146
3009
  if (position_.has_value()) {
147
    // Remove this connection from the map.
148
2970
    std::list<std::reference_wrapper<Network::Connection>>& connections =
149
2970
        position_->connection_map_[&position_->filter_chain_];
150
2970
    connections.erase(position_->iterator_);
151
2970
    if (connections.empty()) {
152
      // Remove the whole entry if this is the last connection using this filter chain.
153
2907
      position_->connection_map_.erase(&position_->filter_chain_);
154
2907
    }
155
2970
    position_.reset();
156
2970
  }
157
3009
  on_connection_closed_called_ = true;
158
3009
}
159

            
160
3009
void EnvoyQuicServerSession::Initialize() {
161
3009
  quic::QuicServerSessionBase::Initialize();
162
3009
  initialized_ = true;
163
3009
  MaybeAddSessionToIdleList();
164
3009
  quic_connection_->setEnvoyConnection(*this, *this);
165
3009
}
166

            
167
19863
void EnvoyQuicServerSession::OnCanWrite() {
168
19863
  uint64_t old_bytes_to_send = bytesToSend();
169
19863
  quic::QuicServerSessionBase::OnCanWrite();
170
  // Do not update delay close timer according to connection level packet egress because that is
171
  // equivalent to TCP transport layer egress. But only do so if the session gets chance to write.
172
19863
  const bool has_sent_any_data = bytesToSend() != old_bytes_to_send;
173
19863
  maybeUpdateDelayCloseTimer(has_sent_any_data);
174
19863
}
175

            
176
260920
bool EnvoyQuicServerSession::hasDataToWrite() { return HasDataToWrite(); }
177

            
178
9464
const quic::QuicConnection* EnvoyQuicServerSession::quicConnection() const {
179
9464
  return initialized_ ? connection() : nullptr;
180
9464
}
181

            
182
1692
quic::QuicConnection* EnvoyQuicServerSession::quicConnection() {
183
1692
  return initialized_ ? connection() : nullptr;
184
1692
}
185

            
186
2875
void EnvoyQuicServerSession::OnTlsHandshakeComplete() {
187
2875
  quic::QuicServerSessionBase::OnTlsHandshakeComplete();
188
2875
  streamInfo().downstreamTiming().onDownstreamHandshakeComplete(dispatcher_.timeSource());
189
2875
  raiseConnectionEvent(Network::ConnectionEvent::Connected);
190
2875
}
191

            
192
444
void EnvoyQuicServerSession::OnRstStream(const quic::QuicRstStreamFrame& frame) {
193
444
  QuicServerSessionBase::OnRstStream(frame);
194
444
  incrementSentQuicResetStreamErrorStats(frame.error(),
195
444
                                         /*from_self*/ false, /*is_upstream*/ false);
196
444
}
197

            
198
void EnvoyQuicServerSession::setHttp3Options(
199
2953
    const envoy::config::core::v3::Http3ProtocolOptions& http3_options) {
200
2953
  QuicFilterManagerConnectionImpl::setHttp3Options(http3_options);
201
2953
  if (http3_options_->has_quic_protocol_options() &&
202
2953
      http3_options_->quic_protocol_options().has_connection_keepalive()) {
203
2
    const uint64_t initial_interval = PROTOBUF_GET_MS_OR_DEFAULT(
204
2
        http3_options_->quic_protocol_options().connection_keepalive(), initial_interval, 0);
205
2
    const uint64_t max_interval =
206
2
        PROTOBUF_GET_MS_OR_DEFAULT(http3_options_->quic_protocol_options().connection_keepalive(),
207
2
                                   max_interval, quic::kPingTimeoutSecs);
208
2
    if (max_interval != 0) {
209
1
      if (initial_interval > 0) {
210
1
        connection()->set_keep_alive_ping_timeout(
211
1
            quic::QuicTime::Delta::FromMilliseconds(max_interval));
212
1
        connection()->set_initial_retransmittable_on_wire_timeout(
213
1
            quic::QuicTime::Delta::FromMilliseconds(initial_interval));
214
1
      }
215
1
    }
216
2
  }
217
2953
  set_allow_extended_connect(http3_options_->allow_extended_connect());
218
2953
  if (http3_options_->disable_qpack()) {
219
1
    DisableHuffmanEncoding();
220
1
    DisableCookieCrumbling();
221
1
    set_qpack_maximum_dynamic_table_capacity(0);
222
1
  }
223
2953
}
224

            
225
void EnvoyQuicServerSession::storeConnectionMapPosition(FilterChainToConnectionMap& connection_map,
226
                                                        const Network::FilterChain& filter_chain,
227
2970
                                                        ConnectionMapIter position) {
228
2970
  position_.emplace(connection_map, filter_chain, position);
229
2970
}
230

            
231
3009
quic::QuicSSLConfig EnvoyQuicServerSession::GetSSLConfig() const {
232
3009
  quic::QuicSSLConfig config = quic::QuicServerSessionBase::GetSSLConfig();
233
3009
  config.early_data_enabled = position_.has_value()
234
3009
                                  ? dynamic_cast<const QuicServerTransportSocketFactory&>(
235
2970
                                        position_->filter_chain_.transportSocketFactory())
236
2970
                                        .earlyDataEnabled()
237
3009
                                  : true;
238
3009
  return config;
239
3009
}
240

            
241
void EnvoyQuicServerSession::ProcessUdpPacket(const quic::QuicSocketAddress& self_address,
242
                                              const quic::QuicSocketAddress& peer_address,
243
239414
                                              const quic::QuicReceivedPacket& packet) {
244
  // If L4 filters causes the connection to be closed early during initialization, now
245
  // is the time to actually close the connection.
246
239414
  maybeHandleCloseDuringInitialize();
247

            
248
239414
  if (should_send_go_away_and_close_on_dispatch_ != nullptr &&
249
239414
      should_send_go_away_and_close_on_dispatch_->shouldShedLoad()) {
250
1
    ENVOY_LOG_EVERY_POW_2(info, "EnvoyQuicServerSession::ProcessUdpPacket: "
251
1
                                "sending GOAWAY and close on dispatch");
252
1
    SendHttp3GoAway(quic::QUIC_PEER_GOING_AWAY, "Server overloaded");
253
1
    closeConnectionImmediately();
254
239413
  } else if (should_send_go_away_on_dispatch_ != nullptr &&
255
239413
             should_send_go_away_on_dispatch_->shouldShedLoad() && !h3_go_away_sent_) {
256
1
    ENVOY_LOG_EVERY_POW_2(info, "EnvoyQuicServerSession::ProcessUdpPacket: "
257
1
                                "sending GOAWAY on dispatch");
258
1
    SendHttp3GoAway(quic::QUIC_PEER_GOING_AWAY, "Server overloaded");
259
1
    h3_go_away_sent_ = true;
260
1
  }
261

            
262
239414
  quic::QuicServerSessionBase::ProcessUdpPacket(self_address, peer_address, packet);
263
239414
  if (connection()->expected_server_preferred_address().IsInitialized() &&
264
239414
      self_address == connection()->expected_server_preferred_address()) {
265
51
    connection_stats_.num_packets_rx_on_preferred_address_.inc();
266
51
  }
267
239414
  maybeApplyDelayedClose();
268
239414
}
269

            
270
std::vector<absl::string_view>::const_iterator
271
2965
EnvoyQuicServerSession::SelectAlpn(const std::vector<absl::string_view>& alpns) const {
272
2965
  if (!position_.has_value()) {
273
    return quic::QuicServerSessionBase::SelectAlpn(alpns);
274
  }
275
2965
  const std::vector<absl::string_view>& configured_alpns =
276
2965
      dynamic_cast<const QuicServerTransportSocketFactory&>(
277
2965
          position_->filter_chain_.transportSocketFactory())
278
2965
          .supportedAlpnProtocols();
279
2965
  if (configured_alpns.empty()) {
280
2936
    return quic::QuicServerSessionBase::SelectAlpn(alpns);
281
2936
  }
282

            
283
30
  for (absl::string_view configured_alpn : configured_alpns) {
284
30
    auto it = absl::c_find(alpns, configured_alpn);
285
30
    if (it != alpns.end()) {
286
29
      return it;
287
29
    }
288
30
  }
289
  return alpns.end();
290
29
}
291

            
292
19584
void EnvoyQuicServerSession::ActivateStream(std::unique_ptr<quic::QuicStream> stream) {
293
19584
  bool streams_was_empty = !HasActiveRequestStreams();
294
19584
  QuicServerSessionBase::ActivateStream(std::move(stream));
295
19584
  if (streams_was_empty && HasActiveRequestStreams()) {
296
3157
    MaybeRemoveSessionFromIdleList();
297
3157
  }
298
19584
}
299

            
300
3982
void EnvoyQuicServerSession::OnStreamClosed(quic::QuicStreamId id) {
301
3982
  bool streams_was_empty = !HasActiveRequestStreams();
302

            
303
3982
  QuicServerSessionBase::OnStreamClosed(id);
304
3982
  if (on_connection_closed_called_) {
305
    return;
306
  }
307

            
308
3982
  if (!streams_was_empty && !HasActiveRequestStreams()) {
309
3157
    OnLastActiveStreamClosed();
310
3157
  }
311
3982
}
312

            
313
5
void EnvoyQuicServerSession::TerminateIdleSession() {
314
5
  ENVOY_BUG(!on_connection_closed_called_,
315
5
            "TerminateIdleSession called after session on close called.");
316
5
  connection()->CloseConnection(quic::QUIC_NETWORK_IDLE_TIMEOUT, "Server overload",
317
5
                                quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
318
5
}
319

            
320
3157
void EnvoyQuicServerSession::OnLastActiveStreamClosed() { MaybeAddSessionToIdleList(); }
321

            
322
6167
void EnvoyQuicServerSession::MaybeAddSessionToIdleList() {
323
6167
  if (session_idle_list_ == nullptr || is_in_idle_list_) {
324
6097
    return;
325
6097
  }
326
70
  is_in_idle_list_ = true;
327
70
  session_idle_list_->AddSession(*this);
328
70
}
329

            
330
9176
void EnvoyQuicServerSession::MaybeRemoveSessionFromIdleList() {
331
9176
  if (session_idle_list_ == nullptr || !is_in_idle_list_) {
332
9106
    return;
333
9106
  }
334
70
  is_in_idle_list_ = false;
335
70
  session_idle_list_->RemoveSession(*this);
336
70
}
337

            
338
} // namespace Quic
339
} // namespace Envoy