// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "osp/impl/quic/quic_server.h" #include #include #include "platform/api/task_runner.h" #include "platform/api/time.h" #include "util/osp_logging.h" namespace openscreen { namespace osp { QuicServer::QuicServer( const ServerConfig& config, MessageDemuxer* demuxer, std::unique_ptr connection_factory, ProtocolConnectionServer::Observer* observer, ClockNowFunctionPtr now_function, TaskRunner* task_runner) : ProtocolConnectionServer(demuxer, observer), connection_endpoints_(config.connection_endpoints), connection_factory_(std::move(connection_factory)), cleanup_alarm_(now_function, task_runner) {} QuicServer::~QuicServer() { CloseAllConnections(); } bool QuicServer::Start() { if (state_ != State::kStopped) return false; state_ = State::kRunning; connection_factory_->SetServerDelegate(this, connection_endpoints_); Cleanup(); // Start periodic clean-ups. observer_->OnRunning(); return true; } bool QuicServer::Stop() { if (state_ != State::kRunning && state_ != State::kSuspended) return false; connection_factory_->SetServerDelegate(nullptr, {}); CloseAllConnections(); state_ = State::kStopped; Cleanup(); // Final clean-up. observer_->OnStopped(); return true; } bool QuicServer::Suspend() { // TODO(btolsch): QuicStreams should either buffer or reject writes. if (state_ != State::kRunning) return false; state_ = State::kSuspended; observer_->OnSuspended(); return true; } bool QuicServer::Resume() { if (state_ != State::kSuspended) return false; state_ = State::kRunning; observer_->OnRunning(); return true; } void QuicServer::Cleanup() { for (auto& entry : connections_) entry.second.delegate->DestroyClosedStreams(); for (uint64_t endpoint_id : delete_connections_) { auto it = connections_.find(endpoint_id); if (it != connections_.end()) { connections_.erase(it); } } delete_connections_.clear(); constexpr Clock::duration kQuicCleanupPeriod = std::chrono::milliseconds(500); if (state_ != State::kStopped) { cleanup_alarm_.ScheduleFromNow([this] { Cleanup(); }, kQuicCleanupPeriod); } } std::unique_ptr QuicServer::CreateProtocolConnection( uint64_t endpoint_id) { if (state_ != State::kRunning) { return nullptr; } auto connection_entry = connections_.find(endpoint_id); if (connection_entry == connections_.end()) { return nullptr; } return QuicProtocolConnection::FromExisting( this, connection_entry->second.connection.get(), connection_entry->second.delegate.get(), endpoint_id); } void QuicServer::OnConnectionDestroyed(QuicProtocolConnection* connection) { if (!connection->stream()) return; auto connection_entry = connections_.find(connection->endpoint_id()); if (connection_entry == connections_.end()) return; connection_entry->second.delegate->DropProtocolConnection(connection); } uint64_t QuicServer::OnCryptoHandshakeComplete( ServiceConnectionDelegate* delegate, uint64_t connection_id) { OSP_DCHECK_EQ(state_, State::kRunning); const IPEndpoint& endpoint = delegate->endpoint(); auto pending_entry = pending_connections_.find(endpoint); if (pending_entry == pending_connections_.end()) return 0; ServiceConnectionData connection_data = std::move(pending_entry->second); pending_connections_.erase(pending_entry); uint64_t endpoint_id = next_endpoint_id_++; endpoint_map_[endpoint] = endpoint_id; connections_.emplace(endpoint_id, std::move(connection_data)); return endpoint_id; } void QuicServer::OnIncomingStream( std::unique_ptr connection) { OSP_DCHECK_EQ(state_, State::kRunning); observer_->OnIncomingConnection(std::move(connection)); } void QuicServer::OnConnectionClosed(uint64_t endpoint_id, uint64_t connection_id) { OSP_DCHECK_EQ(state_, State::kRunning); auto connection_entry = connections_.find(endpoint_id); if (connection_entry == connections_.end()) return; delete_connections_.push_back(endpoint_id); // TODO(crbug.com/openscreen/42): If we reset request IDs when a connection is // closed, we might end up re-using request IDs when a new connection is // created to the same endpoint. endpoint_request_ids_.ResetRequestId(endpoint_id); } void QuicServer::OnDataReceived(uint64_t endpoint_id, uint64_t connection_id, const uint8_t* data, size_t data_size) { OSP_DCHECK_EQ(state_, State::kRunning); demuxer_->OnStreamData(endpoint_id, connection_id, data, data_size); } void QuicServer::CloseAllConnections() { for (auto& conn : pending_connections_) conn.second.connection->Close(); pending_connections_.clear(); for (auto& conn : connections_) conn.second.connection->Close(); connections_.clear(); endpoint_map_.clear(); next_endpoint_id_ = 0; endpoint_request_ids_.Reset(); } QuicConnection::Delegate* QuicServer::NextConnectionDelegate( const IPEndpoint& source) { OSP_DCHECK_EQ(state_, State::kRunning); OSP_DCHECK(!pending_connection_delegate_); pending_connection_delegate_ = std::make_unique(this, source); return pending_connection_delegate_.get(); } void QuicServer::OnIncomingConnection( std::unique_ptr connection) { OSP_DCHECK_EQ(state_, State::kRunning); const IPEndpoint& endpoint = pending_connection_delegate_->endpoint(); pending_connections_.emplace( endpoint, ServiceConnectionData(std::move(connection), std::move(pending_connection_delegate_))); } } // namespace osp } // namespace openscreen