/* @file db/client.h "Client" represents a connection to the database (the server-side) and corresponds to an open socket (or logical connection if pooling on sockets) from a client. todo: switch to asio...this will fit nicely with that. */ /** * Copyright (C) 2008 10gen 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 "mongo/base/disallow_copying.h" #include "mongo/db/client.h" #include "mongo/db/namespace_string.h" #include "mongo/db/service_context.h" #include "mongo/platform/random.h" #include "mongo/platform/unordered_set.h" #include "mongo/stdx/thread.h" #include "mongo/transport/session.h" #include "mongo/util/concurrency/spin_lock.h" #include "mongo/util/concurrency/threadlocal.h" #include "mongo/util/decorable.h" #include "mongo/util/net/abstract_message_port.h" #include "mongo/util/net/hostandport.h" namespace mongo { class AbstractMessagingPort; class Collection; class OperationContext; typedef long long ConnectionId; /** * The database's concept of an outside "client". * */ class Client final : public Decorable { public: /** * Creates a Client object and stores it in TLS for the current thread. * * An unowned pointer to a transport::Session may optionally be provided. If 'session' * is non-null, then it will be used to augment the thread name, and for reporting purposes. * * If provided, session's ref count will be bumped by this Client. */ static void initThread(StringData desc, transport::SessionHandle session = nullptr); static void initThread(StringData desc, ServiceContext* serviceContext, transport::SessionHandle session); /** * Moves client into the thread_local for this thread. After this call, Client::getCurrent * and cc() will return client.get(). The client will be destroyed with the thread exits * or Client::destroy() is called. */ static void setCurrent(ServiceContext::UniqueClient client); /** * Releases the client being managed by the thread_local for this thread. After this call * cc() will crash the server and Client::getCurrent() will return nullptr until either * Client::initThread() or Client::setCurrent() is called. * * The client will be released to the caller. */ static ServiceContext::UniqueClient releaseCurrent(); static Client* getCurrent(); bool getIsLocalHostConnection() { if (!hasRemote()) { return false; } return getRemote().isLocalHost(); } bool hasRemote() const { return (_session != nullptr); } HostAndPort getRemote() const { verify(_session); return _session->remote(); } /** * Returns the ServiceContext that owns this client session context. */ ServiceContext* getServiceContext() const { return _serviceContext; } /** * Returns the Session to which this client is bound, if any. */ const transport::SessionHandle& session() const& { return _session; } transport::SessionHandle session() && { return std::move(_session); } /** * Inits a thread if that thread has not already been init'd, setting the thread name to * "desc". */ static void initThreadIfNotAlready(StringData desc); /** * Inits a thread if that thread has not already been init'd, using the existing thread name */ static void initThreadIfNotAlready(); /** * Destroys the Client object stored in TLS for the current thread. The current thread must have * a Client. * * If destroy() is not called explicitly, then the Client stored in TLS will be destroyed upon * exit of the current thread. */ static void destroy(); std::string clientAddress(bool includePort = false) const; const std::string& desc() const { return _desc; } void reportState(BSONObjBuilder& builder); // Ensures stability of the client's OperationContext. When the client is locked, // the OperationContext will not disappear. void lock() { _lock.lock(); } void unlock() { _lock.unlock(); } /** * Makes a new operation context representing an operation on this client. At most * one operation context may be in scope on a client at a time. * * If provided, the LogicalSessionId links this operation to a logical session. */ ServiceContext::UniqueOperationContext makeOperationContext(); /** * Sets the active operation context on this client to "opCtx", which must be non-NULL. * * It is an error to call this method if there is already an operation context on Client. * It is an error to call this on an unlocked client. */ void setOperationContext(OperationContext* opCtx); /** * Clears the active operation context on this client. * * There must already be such a context set on this client. * It is an error to call this on an unlocked client. */ void resetOperationContext(); /** * Gets the operation context active on this client, or nullptr if there is no such context. * * It is an error to call this method on an unlocked client, or to use the value returned * by this method while the client is not locked. */ OperationContext* getOperationContext() { return _opCtx; } // TODO(spencer): SERVER-10228 SERVER-14779 Remove this/move it fully into OperationContext. bool isInDirectClient() const { return _inDirectClient; } void setInDirectClient(bool newVal) { _inDirectClient = newVal; } ConnectionId getConnectionId() const { return _connectionId; } bool isFromUserConnection() const { return _connectionId > 0; } PseudoRandom& getPrng() { return _prng; } private: friend class ServiceContext; explicit Client(std::string desc, ServiceContext* serviceContext, transport::SessionHandle session); ServiceContext* const _serviceContext; const transport::SessionHandle _session; // Description for the client (e.g. conn8) const std::string _desc; // OS id of the thread, which owns this client const stdx::thread::id _threadId; // > 0 for things "conn", 0 otherwise const ConnectionId _connectionId; // Protects the contents of the Client (such as changing the OperationContext, etc) SpinLock _lock; // Whether this client is running as DBDirectClient bool _inDirectClient = false; // If != NULL, then contains the currently active OperationContext OperationContext* _opCtx = nullptr; PseudoRandom _prng; }; /** get the Client object for this thread. */ Client& cc(); bool haveClient(); };