/**
* Copyright (C) 2017 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include
#include
#include "mongo/base/status_with.h"
#include "mongo/config.h"
#include "mongo/db/server_options.h"
#include "mongo/stdx/condition_variable.h"
#include "mongo/stdx/memory.h"
#include "mongo/stdx/mutex.h"
#include "mongo/stdx/thread.h"
#include "mongo/transport/transport_layer.h"
#include "mongo/transport/transport_mode.h"
#include "mongo/util/fail_point_service.h"
#include "mongo/util/net/hostandport.h"
#include "mongo/util/net/ssl_options.h"
#include "mongo/util/net/ssl_types.h"
namespace asio {
class io_context;
template
class basic_socket_acceptor;
namespace generic {
class stream_protocol;
} // namespace generic
namespace ssl {
class context;
} // namespace ssl
} // namespace asio
namespace mongo {
class ServiceContext;
class ServiceEntryPoint;
namespace transport {
// This fail point simulates reads and writes that always return 1 byte and fail with EAGAIN
MONGO_FAIL_POINT_DECLARE(transportLayerASIOshortOpportunisticReadWrite);
// This fail point will cause an asyncConnect to timeout after it's successfully connected
// to the remote peer
MONGO_FAIL_POINT_DECLARE(transportLayerASIOasyncConnectTimesOut);
/**
* A TransportLayer implementation based on ASIO networking primitives.
*/
class TransportLayerASIO final : public TransportLayer {
MONGO_DISALLOW_COPYING(TransportLayerASIO);
public:
struct Options {
constexpr static auto kIngress = 0x1;
constexpr static auto kEgress = 0x10;
explicit Options(const ServerGlobalParams* params);
Options() = default;
int mode = kIngress | kEgress;
bool isIngress() const {
return mode & kIngress;
}
bool isEgress() const {
return mode & kEgress;
}
int port = ServerGlobalParams::DefaultDBPort; // port to bind to
std::vector ipList; // addresses to bind to
#ifndef _WIN32
bool useUnixSockets = true; // whether to allow UNIX sockets in ipList
#endif
bool enableIPv6 = false; // whether to allow IPv6 sockets in ipList
Mode transportMode = Mode::kSynchronous; // whether accepted sockets should be put into
// non-blocking mode after they're accepted
size_t maxConns = DEFAULT_MAX_CONN; // maximum number of active connections
};
TransportLayerASIO(const Options& opts, ServiceEntryPoint* sep);
virtual ~TransportLayerASIO();
StatusWith connect(HostAndPort peer,
ConnectSSLMode sslMode,
Milliseconds timeout) final;
Future asyncConnect(HostAndPort peer,
ConnectSSLMode sslMode,
const ReactorHandle& reactor,
Milliseconds timeout) final;
Status setup() final;
ReactorHandle getReactor(WhichReactor which) final;
Status start() final;
void shutdown() final;
int listenerPort() const {
return _listenerPort;
}
BatonHandle makeBaton(OperationContext* opCtx) override;
private:
class BatonASIO;
class ASIOSession;
class ASIOReactor;
using ASIOSessionHandle = std::shared_ptr;
using ConstASIOSessionHandle = std::shared_ptr;
using GenericAcceptor = asio::basic_socket_acceptor;
void _acceptConnection(GenericAcceptor& acceptor);
template
StatusWith _doSyncConnect(Endpoint endpoint,
const HostAndPort& peer,
const Milliseconds& timeout);
#ifdef MONGO_CONFIG_SSL
SSLParams::SSLModes _sslMode() const;
#endif
stdx::mutex _mutex;
// There are three reactors that are used by TransportLayerASIO. The _ingressReactor contains
// all the accepted sockets and all ingress networking activity. The _acceptorReactor contains
// all the sockets in _acceptors. The _egressReactor contains egress connections.
//
// TransportLayerASIO should never call run() on the _ingressReactor.
// In synchronous mode, this will cause a massive performance degradation due to
// unnecessary wakeups on the asio thread for sockets we don't intend to interact
// with asynchronously. The additional IO context avoids registering those sockets
// with the acceptors epoll set, thus avoiding those wakeups. Calling run will
// undo that benefit.
//
// TransportLayerASIO should run its own thread that calls run() on the _acceptorReactor
// to process calls to async_accept - this is the equivalent of the "listener" thread in
// other TransportLayers.
//
// The underlying problem that caused this is here:
// https://github.com/chriskohlhoff/asio/issues/240
//
// It is important that the reactors be declared before the vector of acceptors (or any other
// state that is associated with the reactors), so that we destroy any existing acceptors or
// other reactor associated state before we drop the refcount on the reactor, which may destroy
// it.
std::shared_ptr _ingressReactor;
std::shared_ptr _egressReactor;
std::shared_ptr _acceptorReactor;
#ifdef MONGO_CONFIG_SSL
std::unique_ptr _ingressSSLContext;
std::unique_ptr _egressSSLContext;
#endif
std::vector> _acceptors;
// Only used if _listenerOptions.async is false.
stdx::thread _listenerThread;
ServiceEntryPoint* const _sep = nullptr;
AtomicWord _running{false};
Options _listenerOptions;
// The real incoming port in case of _listenerOptions.port==0 (ephemeral).
int _listenerPort = 0;
};
} // namespace transport
} // namespace mongo