You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
5.9 KiB
193 lines
5.9 KiB
7 months ago
|
// 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 <functional>
|
||
|
#include <memory>
|
||
|
|
||
|
#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<QuicConnectionFactory> 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<ProtocolConnection> 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<QuicProtocolConnection> 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<ServiceConnectionDelegate>(this, source);
|
||
|
return pending_connection_delegate_.get();
|
||
|
}
|
||
|
|
||
|
void QuicServer::OnIncomingConnection(
|
||
|
std::unique_ptr<QuicConnection> 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
|