// Copyright 2014 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/tools/quic/quic_simple_server.h" #include #include #include "base/bind.h" #include "base/location.h" #include "base/run_loop.h" #include "base/task/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/log/net_log_source.h" #include "net/quic/address_utils.h" #include "net/socket/udp_server_socket.h" #include "net/third_party/quiche/src/quiche/quic/core/crypto/crypto_handshake.h" #include "net/third_party/quiche/src/quiche/quic/core/crypto/quic_random.h" #include "net/third_party/quiche/src/quiche/quic/core/quic_crypto_stream.h" #include "net/third_party/quiche/src/quiche/quic/core/quic_data_reader.h" #include "net/third_party/quiche/src/quiche/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quiche/quic/tools/quic_simple_dispatcher.h" #include "net/tools/quic/quic_simple_server_packet_writer.h" #include "net/tools/quic/quic_simple_server_session_helper.h" #include "net/tools/quic/quic_simple_server_socket.h" namespace net { namespace { const char kSourceAddressTokenSecret[] = "secret"; const size_t kNumSessionsToCreatePerSocketEvent = 16; // Allocate some extra space so we can send an error if the client goes over // the limit. const int kReadBufferSize = 2 * quic::kMaxIncomingPacketSize; } // namespace QuicSimpleServer::QuicSimpleServer( std::unique_ptr proof_source, const quic::QuicConfig& config, const quic::QuicCryptoServerConfig::ConfigOptions& crypto_config_options, const quic::ParsedQuicVersionVector& supported_versions, quic::QuicSimpleServerBackend* quic_simple_server_backend) : version_manager_(supported_versions), helper_( new QuicChromiumConnectionHelper(&clock_, quic::QuicRandom::GetInstance())), alarm_factory_(new QuicChromiumAlarmFactory( base::ThreadTaskRunnerHandle::Get().get(), &clock_)), config_(config), crypto_config_options_(crypto_config_options), crypto_config_(kSourceAddressTokenSecret, quic::QuicRandom::GetInstance(), std::move(proof_source), quic::KeyExchangeSource::Default()), read_buffer_(base::MakeRefCounted(kReadBufferSize)), quic_simple_server_backend_(quic_simple_server_backend), connection_id_generator_(quic::kQuicDefaultConnectionIdLength) { DCHECK(quic_simple_server_backend); Initialize(); } void QuicSimpleServer::Initialize() { #if MMSG_MORE use_recvmmsg_ = true; #endif // If an initial flow control window has not explicitly been set, then use a // sensible value for a server: 1 MB for session, 64 KB for each stream. const uint32_t kInitialSessionFlowControlWindow = 1 * 1024 * 1024; // 1 MB const uint32_t kInitialStreamFlowControlWindow = 64 * 1024; // 64 KB if (config_.GetInitialStreamFlowControlWindowToSend() == quic::kMinimumFlowControlSendWindow) { config_.SetInitialStreamFlowControlWindowToSend( kInitialStreamFlowControlWindow); } if (config_.GetInitialSessionFlowControlWindowToSend() == quic::kMinimumFlowControlSendWindow) { config_.SetInitialSessionFlowControlWindowToSend( kInitialSessionFlowControlWindow); } std::unique_ptr scfg( crypto_config_.AddDefaultConfig(helper_->GetRandomGenerator(), helper_->GetClock(), crypto_config_options_)); } QuicSimpleServer::~QuicSimpleServer() = default; bool QuicSimpleServer::CreateUDPSocketAndListen( const quic::QuicSocketAddress& address) { return Listen(ToIPEndPoint(address)); } void QuicSimpleServer::HandleEventsForever() { base::RunLoop().Run(); } bool QuicSimpleServer::Listen(const IPEndPoint& address) { socket_ = CreateQuicSimpleServerSocket(address, &server_address_); if (socket_ == nullptr) return false; dispatcher_ = std::make_unique( &config_, &crypto_config_, &version_manager_, std::unique_ptr(helper_), std::make_unique( quic::QuicRandom::GetInstance()), std::unique_ptr(alarm_factory_), quic_simple_server_backend_, quic::kQuicDefaultConnectionIdLength, connection_id_generator_); QuicSimpleServerPacketWriter* writer = new QuicSimpleServerPacketWriter(socket_.get(), dispatcher_.get()); dispatcher_->InitializeWithWriter(writer); StartReading(); return true; } void QuicSimpleServer::Shutdown() { DVLOG(1) << "QuicSimpleServer is shutting down"; // Before we shut down the epoll server, give all active sessions a chance to // notify clients that they're closing. dispatcher_->Shutdown(); if (!socket_) { return; } socket_->Close(); socket_.reset(); } void QuicSimpleServer::StartReading() { if (synchronous_read_count_ == 0) { // Only process buffered packets once per message loop. dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent); } if (read_pending_) { return; } read_pending_ = true; int result = socket_->RecvFrom( read_buffer_.get(), read_buffer_->size(), &client_address_, base::BindOnce(&QuicSimpleServer::OnReadComplete, base::Unretained(this))); if (result == ERR_IO_PENDING) { synchronous_read_count_ = 0; if (dispatcher_->HasChlosBuffered()) { // No more packets to read, so yield before processing buffered packets. base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&QuicSimpleServer::StartReading, weak_factory_.GetWeakPtr())); } return; } if (++synchronous_read_count_ > 32) { synchronous_read_count_ = 0; // Schedule the processing through the message loop to 1) prevent infinite // recursion and 2) avoid blocking the thread for too long. base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&QuicSimpleServer::OnReadComplete, weak_factory_.GetWeakPtr(), result)); } else { OnReadComplete(result); } } void QuicSimpleServer::OnReadComplete(int result) { read_pending_ = false; if (result > 0) { quic::QuicReceivedPacket packet(read_buffer_->data(), result, helper_->GetClock()->Now(), false); dispatcher_->ProcessPacket(ToQuicSocketAddress(server_address_), ToQuicSocketAddress(client_address_), packet); } else { LOG(ERROR) << "QuicSimpleServer read failed: " << ErrorToString(result); // Do not act on ERR_MSG_TOO_BIG as that indicates that we received a UDP // packet whose payload is larger than our receive buffer. Do not act on 0 // as that indicates that we received a UDP packet with an empty payload. // In both cases, the socket should still be usable. // Also do not act on ERR_CONNECTION_RESET as this is happening when the // network service restarts on Windows. if (result != ERR_MSG_TOO_BIG && result != ERR_CONNECTION_RESET && result != 0) { Shutdown(); return; } } StartReading(); } } // namespace net