1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/tools/quic/quic_server_session.h"
6
7#include "base/logging.h"
8#include "net/quic/crypto/source_address_token.h"
9#include "net/quic/quic_connection.h"
10#include "net/quic/quic_flags.h"
11#include "net/quic/reliable_quic_stream.h"
12#include "net/tools/quic/quic_spdy_server_stream.h"
13
14namespace net {
15namespace tools {
16
17QuicServerSession::QuicServerSession(const QuicConfig& config,
18                                     QuicConnection* connection,
19                                     QuicServerSessionVisitor* visitor)
20    : QuicSession(connection, config),
21      visitor_(visitor),
22      bandwidth_estimate_sent_to_client_(QuicBandwidth::Zero()),
23      last_server_config_update_time_(QuicTime::Zero()) {}
24
25QuicServerSession::~QuicServerSession() {}
26
27void QuicServerSession::InitializeSession(
28    const QuicCryptoServerConfig& crypto_config) {
29  QuicSession::InitializeSession();
30  crypto_stream_.reset(CreateQuicCryptoServerStream(crypto_config));
31}
32
33QuicCryptoServerStream* QuicServerSession::CreateQuicCryptoServerStream(
34    const QuicCryptoServerConfig& crypto_config) {
35  return new QuicCryptoServerStream(crypto_config, this);
36}
37
38void QuicServerSession::OnConfigNegotiated() {
39  QuicSession::OnConfigNegotiated();
40  if (!FLAGS_enable_quic_fec ||
41      !config()->HasReceivedConnectionOptions() ||
42      !net::ContainsQuicTag(config()->ReceivedConnectionOptions(), kFHDR)) {
43    return;
44  }
45  // kFHDR config maps to FEC protection always for headers stream.
46  // TODO(jri): Add crypto stream in addition to headers for kHDR.
47  headers_stream_->set_fec_policy(FEC_PROTECT_ALWAYS);
48}
49
50void QuicServerSession::OnConnectionClosed(QuicErrorCode error,
51                                           bool from_peer) {
52  QuicSession::OnConnectionClosed(error, from_peer);
53  // In the unlikely event we get a connection close while doing an asynchronous
54  // crypto event, make sure we cancel the callback.
55  if (crypto_stream_.get() != NULL) {
56    crypto_stream_->CancelOutstandingCallbacks();
57  }
58  visitor_->OnConnectionClosed(connection()->connection_id(), error);
59}
60
61void QuicServerSession::OnWriteBlocked() {
62  QuicSession::OnWriteBlocked();
63  visitor_->OnWriteBlocked(connection());
64}
65
66void QuicServerSession::OnCongestionWindowChange(QuicTime now) {
67  if (connection()->version() <= QUIC_VERSION_21) {
68    return;
69  }
70
71  // If not enough time has passed since the last time we sent an update to the
72  // client, then return early.
73  const QuicSentPacketManager& sent_packet_manager =
74      connection()->sent_packet_manager();
75  int64 srtt_ms =
76      sent_packet_manager.GetRttStats()->SmoothedRtt().ToMilliseconds();
77  int64 now_ms = now.Subtract(last_server_config_update_time_).ToMilliseconds();
78  if (now_ms < (kMinIntervalBetweenServerConfigUpdatesRTTs * srtt_ms) ||
79      now_ms < kMinIntervalBetweenServerConfigUpdatesMs) {
80    return;
81  }
82
83  // If the bandwidth recorder does not have a valid estimate, return early.
84  const QuicSustainedBandwidthRecorder& bandwidth_recorder =
85      sent_packet_manager.SustainedBandwidthRecorder();
86  if (!bandwidth_recorder.HasEstimate()) {
87    return;
88  }
89
90  // The bandwidth recorder has recorded at least one sustained bandwidth
91  // estimate. Check that it's substantially different from the last one that
92  // we sent to the client, and if so, send the new one.
93  QuicBandwidth new_bandwidth_estimate = bandwidth_recorder.BandwidthEstimate();
94
95  int64 bandwidth_delta =
96      std::abs(new_bandwidth_estimate.ToBitsPerSecond() -
97               bandwidth_estimate_sent_to_client_.ToBitsPerSecond());
98
99  // Define "substantial" difference as a 50% increase or decrease from the
100  // last estimate.
101  bool substantial_difference =
102      bandwidth_delta >
103      0.5 * bandwidth_estimate_sent_to_client_.ToBitsPerSecond();
104  if (!substantial_difference) {
105    return;
106  }
107
108  bandwidth_estimate_sent_to_client_ = new_bandwidth_estimate;
109  DVLOG(1) << "Server: sending new bandwidth estimate (KBytes/s): "
110           << bandwidth_estimate_sent_to_client_.ToKBytesPerSecond();
111
112  // Include max bandwidth in the update.
113  QuicBandwidth max_bandwidth_estimate =
114      bandwidth_recorder.MaxBandwidthEstimate();
115  int32 max_bandwidth_timestamp = bandwidth_recorder.MaxBandwidthTimestamp();
116
117  // Fill the proto before passing it to the crypto stream to send.
118  CachedNetworkParameters cached_network_params;
119  cached_network_params.set_bandwidth_estimate_bytes_per_second(
120      bandwidth_estimate_sent_to_client_.ToBytesPerSecond());
121  cached_network_params.set_max_bandwidth_estimate_bytes_per_second(
122      max_bandwidth_estimate.ToBytesPerSecond());
123  cached_network_params.set_max_bandwidth_timestamp_seconds(
124      max_bandwidth_timestamp);
125  cached_network_params.set_min_rtt_ms(
126      sent_packet_manager.GetRttStats()->min_rtt().ToMilliseconds());
127  cached_network_params.set_previous_connection_state(
128      bandwidth_recorder.EstimateRecordedDuringSlowStart()
129          ? CachedNetworkParameters::SLOW_START
130          : CachedNetworkParameters::CONGESTION_AVOIDANCE);
131  if (!serving_region_.empty()) {
132    cached_network_params.set_serving_region(serving_region_);
133  }
134
135  crypto_stream_->SendServerConfigUpdate(&cached_network_params);
136  last_server_config_update_time_ = now;
137}
138
139bool QuicServerSession::ShouldCreateIncomingDataStream(QuicStreamId id) {
140  if (id % 2 == 0) {
141    DVLOG(1) << "Invalid incoming even stream_id:" << id;
142    connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
143    return false;
144  }
145  if (GetNumOpenStreams() >= get_max_open_streams()) {
146    DVLOG(1) << "Failed to create a new incoming stream with id:" << id
147             << " Already " << GetNumOpenStreams() << " streams open (max "
148             << get_max_open_streams() << ").";
149    connection()->SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS);
150    return false;
151  }
152  return true;
153}
154
155QuicDataStream* QuicServerSession::CreateIncomingDataStream(
156    QuicStreamId id) {
157  if (!ShouldCreateIncomingDataStream(id)) {
158    return NULL;
159  }
160
161  return new QuicSpdyServerStream(id, this);
162}
163
164QuicDataStream* QuicServerSession::CreateOutgoingDataStream() {
165  DLOG(ERROR) << "Server push not yet supported";
166  return NULL;
167}
168
169QuicCryptoServerStream* QuicServerSession::GetCryptoStream() {
170  return crypto_stream_.get();
171}
172
173}  // namespace tools
174}  // namespace net
175