%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /opt/cpanel/ea-ruby27/src/passenger-release-6.0.23/src/cxx_supportlib/ServerKit/
Upload File :
Create Path :
Current File : //opt/cpanel/ea-ruby27/src/passenger-release-6.0.23/src/cxx_supportlib/ServerKit/Server.h

/*
 *  Phusion Passenger - https://www.phusionpassenger.com/
 *  Copyright (c) 2014-2018 Phusion Holding B.V.
 *
 *  "Passenger", "Phusion Passenger" and "Union Station" are registered
 *  trademarks of Phusion Holding B.V.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
#ifndef _PASSENGER_SERVER_KIT_SERVER_H_
#define _PASSENGER_SERVER_KIT_SERVER_H_

#include <psg_sysqueue.h>

#include <boost/cstdint.hpp>
#include <boost/config.hpp>
#include <boost/scoped_ptr.hpp>
#include <oxt/system_calls.hpp>
#include <oxt/backtrace.hpp>
#include <oxt/macros.hpp>
#include <vector>
#include <new>
#include <ev++.h>

// for std::swap()
#if __cplusplus >= 201103L
	#include <utility>
#else
	#include <algorithm>
#endif
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <cstdio>
#include <jsoncpp/json.h>

#include <LoggingKit/LoggingKit.h>
#include <SafeLibev.h>
#include <Constants.h>
#include <ServerKit/Context.h>
#include <ServerKit/Errors.h>
#include <ServerKit/Hooks.h>
#include <ServerKit/Client.h>
#include <ServerKit/ClientRef.h>
#include <ConfigKit/ConfigKit.h>
#include <Algorithms/MovingAverage.h>
#include <Utils.h>
#include <Utils/ScopeGuard.h>
#include <StrIntTools/StrIntUtils.h>
#include <IOTools/IOUtils.h>
#include <SystemTools/SystemTime.h>

namespace Passenger {
namespace ServerKit {

using namespace std;
using namespace boost;
using namespace oxt;


// We use 'this' so that the macros work in derived template classes like HttpServer<>.
#define SKS_LOG(level, file, line, expr)  P_LOG(Passenger::LoggingKit::context, level, file, line, "[" << this->getServerName() << "] " << expr)
#define SKS_ERROR(expr)  P_ERROR("[" << this->getServerName() << "] " << expr)
#define SKS_WARN(expr)   P_WARN("[" << this->getServerName() << "] " << expr)
#define SKS_INFO(expr)   P_INFO("[" << this->getServerName() << "] " << expr)
#define SKS_NOTICE(expr) P_NOTICE("[" << this->getServerName() << "] " << expr)
#define SKS_DEBUG(expr)  P_DEBUG("[" << this->getServerName() << "] " << expr)
#define SKS_TRACE(level, expr) P_TRACE(level, "[" << this->getServerName() << "] " << expr)

#define SKS_NOTICE_FROM_STATIC(server, expr) P_NOTICE("[" << server->getServerName() << "] " << expr)

#define SKC_LOG(client, level, expr) SKC_LOG_FROM_STATIC(this, client, level, expr)
#define SKC_ERROR(client, expr) SKC_ERROR_FROM_STATIC(this, client, expr)
#define SKC_WARN(client, expr) SKC_WARN_FROM_STATIC(this, client, expr)
#define SKC_NOTICE(client, expr) SKC_NOTICE_FROM_STATIC(this, client, expr)
#define SKC_INFO(client, expr) SKC_INFO_FROM_STATIC(this, client, expr)
#define SKC_DEBUG(client, expr) SKC_DEBUG_FROM_STATIC(this, client, expr)
#define SKC_DEBUG_WITH_POS(client, file, line, expr) \
	SKC_DEBUG_FROM_STATIC_WITH_POS(this, client, file, line, expr)
#define SKC_TRACE(client, level, expr) SKC_TRACE_FROM_STATIC(this, client, level, expr)
#define SKC_TRACE_WITH_POS(client, level, file, line, expr) \
	SKC_TRACE_FROM_STATIC_WITH_POS(this, client, level, file, line, expr)

#define SKC_LOG_FROM_STATIC(server, client, level, expr) \
	do { \
		if (Passenger::LoggingKit::getLevel() >= level) { \
			char _clientName[16]; \
			int _clientNameSize = server->getClientName((client), _clientName, sizeof(_clientName)); \
			P_LOG(LoggingKit::context, level, __FILE__, __LINE__, \
				"[Client " << StaticString(_clientName, _clientNameSize) << "] " << expr); \
		} \
	} while (0)
#define SKC_ERROR_FROM_STATIC(server, client, expr) \
	SKC_LOG_FROM_STATIC(server, client, Passenger::LoggingKit::ERROR, expr)
#define SKC_WARN_FROM_STATIC(server, client, expr) \
	SKC_LOG_FROM_STATIC(server, client, Passenger::LoggingKit::WARN, expr)
#define SKC_NOTICE_FROM_STATIC(server, client, expr) \
	SKC_LOG_FROM_STATIC(server, client, Passenger::LoggingKit::NOTICE, expr)
#define SKC_INFO_FROM_STATIC(server, client, expr) \
	SKC_LOG_FROM_STATIC(server, client, Passenger::LoggingKit::INFO, expr)
#define SKC_DEBUG_FROM_STATIC(server, client, expr) \
	SKC_LOG_FROM_STATIC(server, client, Passenger::LoggingKit::DEBUG, expr)
#define SKC_DEBUG_FROM_STATIC_WITH_POS(server, client, file, line, expr) \
	do { \
		if (OXT_UNLIKELY(Passenger::LoggingKit::getLevel() >= Passenger::LoggingKit::DEBUG)) { \
			char _clientName[16]; \
			int _clientNameSize = server->getClientName((client), _clientName, sizeof(_clientName)); \
			P_DEBUG_WITH_POS(file, line, \
				"[Client " << StaticString(_clientName, _clientNameSize) << "] " << expr); \
		} \
	} while (0)
#define SKC_TRACE_FROM_STATIC(server, client, level, expr) \
	SKC_TRACE_FROM_STATIC_WITH_POS(server, client, level, __FILE__, __LINE__, expr)
#define SKC_TRACE_FROM_STATIC_WITH_POS(server, client, level, file, line, expr) \
	do { \
		if (OXT_UNLIKELY(Passenger::LoggingKit::getLevel() >= Passenger::LoggingKit::INFO + level)) { \
			char _clientName[16]; \
			int _clientNameSize = server->getClientName((client), _clientName, sizeof(_clientName)); \
			P_TRACE_WITH_POS(level, file, line, \
				"[Client " << StaticString(_clientName, _clientNameSize) << "] " << expr); \
		} \
	} while (0)

#define SKC_LOG_EVENT(klass, client, eventName) SKC_LOG_EVENT_FROM_STATIC(this, klass, client, eventName)
#define SKC_LOG_EVENT_FROM_STATIC(server, klass, client, eventName) \
	TRACE_POINT_WITH_DATA_FUNCTION(klass::_getClientNameFromTracePoint, client); \
	SKC_TRACE_FROM_STATIC(server, client, 3, "Event: " eventName)


/*
 * BEGIN ConfigKit schema: Passenger::ServerKit::BaseServerSchema
 * (do not edit: following text is automatically generated
 * by 'rake configkit_schemas_inline_comments')
 *
 *   accept_burst_count           unsigned integer   -   default(32)
 *   client_freelist_limit        unsigned integer   -   default(0)
 *   min_spare_clients            unsigned integer   -   default(0)
 *   start_reading_after_accept   boolean            -   default(true)
 *
 * END
 */
class BaseServerSchema: public ConfigKit::Schema {
private:
	void initialize() {
		using namespace ConfigKit;

		add("accept_burst_count", UINT_TYPE, OPTIONAL, 32);
		add("start_reading_after_accept", BOOL_TYPE, OPTIONAL, true);
		add("min_spare_clients", UINT_TYPE, OPTIONAL, 0);
		add("client_freelist_limit", UINT_TYPE, OPTIONAL, 0);
	}

public:
	BaseServerSchema() {
		initialize();
		finalize();
	}

	BaseServerSchema(bool _subclassing) {
		initialize();
	}
};

struct BaseServerConfigRealization {
	unsigned int acceptBurstCount: 7;
	bool startReadingAfterAccept: 1;
	unsigned int minSpareClients: 12;
	unsigned int clientFreelistLimit: 12;

	BaseServerConfigRealization(const ConfigKit::Store &config)
		: acceptBurstCount(config["accept_burst_count"].asUInt()),
		  startReadingAfterAccept(config["start_reading_after_accept"].asBool()),
		  minSpareClients(config["min_spare_clients"].asUInt()),
		  clientFreelistLimit(config["client_freelist_limit"].asUInt())
		{ }

	void swap(BaseServerConfigRealization &other) BOOST_NOEXCEPT_OR_NOTHROW {
		#define SWAP_BITFIELD(Type, name) \
			do { \
				Type tmp = name; \
				name = other.name; \
				other.name = tmp; \
			} while (false)

		SWAP_BITFIELD(unsigned int, acceptBurstCount);
		SWAP_BITFIELD(bool, startReadingAfterAccept);
		SWAP_BITFIELD(unsigned int, minSpareClients);
		SWAP_BITFIELD(unsigned int, clientFreelistLimit);

		#undef SWAP_BITFIELD
	}
};

struct BaseServerConfigChangeRequest {
	boost::scoped_ptr<ConfigKit::Store> config;
	boost::scoped_ptr<BaseServerConfigRealization> configRlz;
};


/**
 * A highly optimized generic base class for evented socket servers, implementing basic,
 * low-level connection management.
 *
 * ## Features
 *
 * ### Client objects
 *
 * Every connected client is represented by a client object, which inherits from
 * ServerKit::BaseClient. The client object provides input and output, and you can
 * extend with your own fields.
 *
 * Client objects are reference counted, for easy memory management.
 *
 * Creation and destruction is very efficient, because client objects are put on a
 * freelist upon destruction, so that no malloc calls are necessary next time.
 *
 * ### Zero-copy buffers
 *
 * All input is handled in a zero-copy manner, by using the mbuf system.
 *
 * ### Channel I/O abstraction
 *
 * All input is handled through the Channel abstraction, and all output is
 * handled through the FileBufferedFdSinkChannel abstraction.. This makes writing
 * evented servers very easy.
 *
 * ### Multiple listen endpoints
 *
 * The server can listen on multiple server endpoints at the same time (e.g. TCP and
 * Unix domain sockets), up to SERVER_KIT_MAX_SERVER_ENDPOINTS.
 *
 * ### Automatic backoff when too many file descriptors are active
 *
 * If ENFILES or EMFILES is encountered when accepting new clients, Server will stop
 * accepting new clients for a few seconds so that doesn't keep triggering the error
 * in a busy loop.
 *
 * ### Logging
 *
 * Provides basic logging macros that also log the client name.
 */
template<typename DerivedServer, typename Client = Client>
class BaseServer: public HooksImpl {
public:
	/***** Types *****/
	typedef BaseServer BaseClass;
	typedef Client ClientType;
	typedef ClientRef<DerivedServer, Client> ClientRefType;
	STAILQ_HEAD(FreeClientList, Client);
	TAILQ_HEAD(ClientList, Client);

	enum State {
		ACTIVE,
		TOO_MANY_FDS,
		SHUTTING_DOWN,
		FINISHED_SHUTDOWN
	};

	static const unsigned int MAX_ACCEPT_BURST_COUNT = 127;

	typedef void (*Callback)(DerivedServer *server);
	typedef BaseServerConfigChangeRequest ConfigChangeRequest;

	/***** Configuration *****/
	ConfigKit::Store config;
	BaseServerConfigRealization configRlz;
	Callback shutdownFinishCallback;

	/***** Working state and statistics (do not modify) *****/
	State serverState;
	FreeClientList freeClients;
	ClientList activeClients, disconnectedClients;
	unsigned int freeClientCount, activeClientCount, disconnectedClientCount;
	unsigned int peakActiveClientCount;
	unsigned long totalClientsAccepted, lastTotalClientsAccepted;
	unsigned long long totalBytesConsumed;
	ev_tstamp lastStatisticsUpdateTime;
	double clientAcceptSpeed1m, clientAcceptSpeed1h;

private:
	Context *ctx;
	unsigned int nextClientNumber: 28;
	uint8_t nEndpoints: 3;
	bool accept4Available: 1;
	ev::timer acceptResumptionWatcher;
	ev::timer statisticsUpdateWatcher;
	ev::io endpoints[SERVER_KIT_MAX_SERVER_ENDPOINTS];


	/***** Private methods *****/

	void preinitialize(Context *context) {
		STAILQ_INIT(&freeClients);
		TAILQ_INIT(&activeClients);
		TAILQ_INIT(&disconnectedClients);

		acceptResumptionWatcher.set(context->libev->getLoop());
		acceptResumptionWatcher.set<
			BaseServer<DerivedServer, Client>,
			&BaseServer<DerivedServer, Client>::onAcceptResumeTimeout>(this);

		statisticsUpdateWatcher.set(context->libev->getLoop());
		statisticsUpdateWatcher.set<
			BaseServer<DerivedServer, Client>,
			&BaseServer<DerivedServer, Client>::onStatisticsUpdateTimeout>(this);
	}

	static void _onAcceptable(EV_P_ ev_io *io, int revents) {
		static_cast<BaseServer *>(io->data)->onAcceptable(io, revents);
	}

	void onAcceptable(ev_io *io, int revents) {
		TRACE_POINT();
		unsigned int acceptCount = 0;
		bool error = false;
		int fd, errcode = 0;
		Client *client;
		Client *acceptedClients[MAX_ACCEPT_BURST_COUNT];

		P_ASSERT_EQ(serverState, ACTIVE);
		SKS_DEBUG("New clients can be accepted on a server socket");

		for (unsigned int i = 0; i < configRlz.acceptBurstCount; i++) {
			fd = acceptNonBlockingSocket(io->fd);
			if (fd == -1) {
				error = true;
				errcode = errno;
				break;
			}

			FdGuard guard(fd, NULL, 0);
			client = checkoutClientObject();
			TAILQ_INSERT_HEAD(&activeClients, client, nextClient.activeOrDisconnectedClient);
			acceptedClients[acceptCount] = client;
			activeClientCount++;
			acceptCount++;
			totalClientsAccepted++;
			client->number = getNextClientNumber();
			reinitializeClient(client, fd);
			P_LOG_FILE_DESCRIPTOR_PURPOSE(fd, "Server " << getServerName()
				<< ", client " << getClientName(client));
			guard.clear();
		}

		if (acceptCount > 0) {
			SKS_DEBUG(acceptCount << " new client(s) accepted; there are now " <<
				activeClientCount << " active client(s)");
		}
		if (error && errcode != EAGAIN && errcode != EWOULDBLOCK) {
			SKS_ERROR("Cannot accept client: " << getErrorDesc(errcode) <<
				" (errno=" << errcode << "). " <<
				"Stop accepting clients for 3 seconds. " <<
				"Current client count: " << activeClientCount);
			serverState = TOO_MANY_FDS;
			acceptResumptionWatcher.set(3, 0);
			acceptResumptionWatcher.start();
			for (uint8_t i = 0; i < nEndpoints; i++) {
				ev_io_stop(ctx->libev->getLoop(), &endpoints[i]);
			}
		}

		onClientsAccepted(acceptedClients, acceptCount);
	}

	void onAcceptResumeTimeout(ev::timer &timer, int revents) {
		TRACE_POINT();
		P_ASSERT_EQ(serverState, TOO_MANY_FDS);
		SKS_NOTICE("Resuming accepting new clients");
		serverState = ACTIVE;
		for (uint8_t i = 0; i < nEndpoints; i++) {
			ev_io_start(ctx->libev->getLoop(), &endpoints[i]);
		}
	}

	int acceptNonBlockingSocket(int serverFd) {
		union {
			struct sockaddr_in inaddr;
			struct sockaddr_in6 inaddr6;
			struct sockaddr_un unaddr;
		} u;
		socklen_t addrlen = sizeof(u);

		if (accept4Available) {
			int fd = callAccept4(serverFd,
				(struct sockaddr *) &u,
				&addrlen,
				O_NONBLOCK);
			// FreeBSD returns EINVAL if accept4() is called with invalid flags.
			if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) {
				accept4Available = false;
				return acceptNonBlockingSocket(serverFd);
			} else {
				return fd;
			}
		} else {
			int fd = syscalls::accept(serverFd,
				(struct sockaddr *) &u,
				&addrlen);
			FdGuard guard(fd, __FILE__, __LINE__);
			if (fd == -1) {
				return -1;
			} else {
				try {
					setNonBlocking(fd);
				} catch (const SystemException &e) {
					SKS_DEBUG("Unable to set non-blocking flag on accepted client socket: " <<
						e.what() << " (errno=" << e.code() << ")");
					errno = e.code();
					return -1;
				}
				guard.clear();
				return fd;
			}
		}
	}

	void onStatisticsUpdateTimeout(ev::timer &timer, int revents) {
		TRACE_POINT();

		this->onUpdateStatistics();
		this->onFinalizeStatisticsUpdate();

		timer.repeat = timeToNextMultipleD(5, ev_now(this->getLoop()));
		timer.again();
	}

	unsigned int getNextClientNumber() {
		return nextClientNumber++;
	}

	Client *checkoutClientObject() {
		// Try to obtain client object from freelist.
		if (!STAILQ_EMPTY(&freeClients)) {
			return checkoutClientObjectFromFreelist();
		} else {
			return createNewClientObject();
		}
	}

	Client *checkoutClientObjectFromFreelist() {
		assert(freeClientCount > 0);
		SKS_TRACE(3, "Checking out client object from freelist (" <<
			freeClientCount << " -> " << (freeClientCount - 1) << ")");
		Client *client = STAILQ_FIRST(&freeClients);
		P_ASSERT_EQ(client->getConnState(), Client::IN_FREELIST);
		client->refcount.store(2, boost::memory_order_relaxed);
		freeClientCount--;
		STAILQ_REMOVE_HEAD(&freeClients, nextClient.freeClient);
		return client;
	}

	Client *createNewClientObject() {
		Client *client;
		SKS_TRACE(3, "Creating new client object");
		try {
			client = new Client(this);
		} catch (const std::bad_alloc &) {
			return NULL;
		}
		onClientObjectCreated(client);
		return client;
	}

	void clientReachedZeroRefcount(Client *client) {
		TRACE_POINT();
		assert(disconnectedClientCount > 0);
		assert(!TAILQ_EMPTY(&disconnectedClients));

		SKC_TRACE(client, 3, "Client object reached a reference count of 0");
		TAILQ_REMOVE(&disconnectedClients, client, nextClient.activeOrDisconnectedClient);
		disconnectedClientCount--;

		if (addClientToFreelist(client)) {
			SKC_TRACE(client, 3, "Client object added to freelist (" <<
				(freeClientCount - 1) << " -> " << freeClientCount << ")");
		} else {
			SKC_TRACE(client, 3, "Client object destroyed; not added to freelist " <<
				"because it's full (" << freeClientCount << ")");
			delete client;
		}

		if (serverState == SHUTTING_DOWN
		 && activeClientCount == 0
		 && disconnectedClientCount == 0)
		{
			finishShutdown();
		}
	}

	bool addClientToFreelist(Client *client) {
		if (freeClientCount < configRlz.clientFreelistLimit) {
			STAILQ_INSERT_HEAD(&freeClients, client, nextClient.freeClient);
			freeClientCount++;
			client->refcount.store(2, boost::memory_order_relaxed);
			client->setConnState(Client::IN_FREELIST);
			return true;
		} else {
			return false;
		}
	}

	void passClientToEventLoopThread(Client *client) {
		// The shutdown procedure waits until all ACTIVE and DISCONNECTED
		// clients are gone before destroying a Server, so we know for sure
		// that this async callback outlives the Server.
		ctx->libev->runLater(boost::bind(&BaseServer::passClientToEventLoopThreadCallback,
			this, ClientRefType(client, __FILE__, __LINE__)));
	}

	void passClientToEventLoopThreadCallback(ClientRefType clientRef) {
		// Do nothing. Once this method returns, the reference count of the
		// client drops to 0, and clientReachedZeroRefcount() is called.
	}

	const char *getServerStateString() const {
		switch (serverState) {
		case ACTIVE:
			return "ACTIVE";
		case TOO_MANY_FDS:
			return "TOO_MANY_FDS";
		case SHUTTING_DOWN:
			return "SHUTTING_DOWN";
		case FINISHED_SHUTDOWN:
			return "FINISHED_SHUTDOWN";
		default:
			return "UNKNOWN";
		}
	}

	void finishShutdown() {
		TRACE_POINT();
		compact(LoggingKit::INFO);

		acceptResumptionWatcher.stop();
		statisticsUpdateWatcher.stop();

		SKS_NOTICE("Shutdown finished");
		serverState = FINISHED_SHUTDOWN;
		if (shutdownFinishCallback) {
			shutdownFinishCallback(static_cast<DerivedServer *>(this));
		}
	}

	void logClientDataReceived(Client *client, const MemoryKit::mbuf &buffer, int errcode) {
		if (buffer.size() > 0) {
			SKC_TRACE(client, 3, "Processing " << buffer.size() << " bytes of client data");
		} else if (errcode == 0) {
			SKC_TRACE(client, 2, "Client sent EOF");
		} else {
			SKC_TRACE(client, 2, "Error reading from client socket: " <<
				getErrorDesc(errcode) << " (errno=" << errcode << ")");
		}
	}

	static Channel::Result _onClientDataReceived(Channel *_channel,
		const MemoryKit::mbuf &buffer, int errcode)
	{
		FdSourceChannel *channel = reinterpret_cast<FdSourceChannel *>(_channel);
		Client *client = static_cast<Client *>(static_cast<BaseClient *>(
			channel->getHooks()->userData));
		BaseServer *server = getServerFromClient(client);
		const size_t bufferSize = buffer.size();

		server->logClientDataReceived(client, buffer, errcode);
		Channel::Result result = server->onClientDataReceived(client, buffer, errcode);

		// This counter is mostly useful for unit tests, so it's too much hassle to
		// support cases where result.consumed < 1.
		size_t consumed = std::max<size_t>(0,
			std::min<size_t>(result.consumed, bufferSize));
		server->totalBytesConsumed += consumed;
		SKC_TRACE_FROM_STATIC(server, client, 2,
			consumed << " bytes of client data consumed in this callback");

		return result;
	}

	static void _onClientOutputError(FileBufferedFdSinkChannel *_channel, int errcode) {
		FileBufferedFdSinkChannel *channel =
			reinterpret_cast<FileBufferedFdSinkChannel *>(_channel);
		Client *client = static_cast<Client *>(static_cast<BaseClient *>(
			channel->getHooks()->userData));
		BaseServer *server = getServerFromClient(client);
		server->onClientOutputError(client, errcode);
	}

protected:
	/***** Hooks *****/

	virtual void onClientObjectCreated(Client *client) {
		TRACE_POINT();

		client->hooks.impl        = this;
		client->hooks.userData    = static_cast<BaseClient *>(client);

		client->input.setContext(ctx);
		client->input.setHooks(&client->hooks);
		client->input.setDataCallback(_onClientDataReceived);

		client->output.setContext(ctx);
		client->output.setHooks(&client->hooks);
		client->output.errorCallback = _onClientOutputError;
	}

	virtual void onClientsAccepted(Client **clients, unsigned int size) {
		unsigned int i;

		peakActiveClientCount = std::max(peakActiveClientCount, activeClientCount);

		for (i = 0; i < size; i++) {
			Client *client = clients[i];

			onClientAccepted(client);
			if (client->connected()) {
				if (configRlz.startReadingAfterAccept) {
					client->input.startReading();
				} else {
					client->input.startReadingInNextTick();
				}
			}
			// A Client object starts with a refcount of 2 so that we can
			// be sure it won't be destroyted while we're looping inside this
			// function. But we also need an extra unref here.
			unrefClient(client, __FILE__, __LINE__);
		}
	}

	virtual void onClientAccepted(Client *client) {
		// Do nothing.
	}

	virtual void onClientDisconnecting(Client *client) {
		// Do nothing.
	}

	virtual void onClientDisconnected(Client *client) {
		// Do nothing.
	}

	virtual bool shouldDisconnectClientOnShutdown(Client *client) {
		return false;
	}

	virtual Channel::Result onClientDataReceived(Client *client, const MemoryKit::mbuf &buffer,
		int errcode)
	{
		if (buffer.empty()) {
			disconnect(&client);
		}
		return Channel::Result(0, true);
	}

	virtual void onClientOutputError(Client *client, int errcode) {
		SKC_LOG_EVENT(DerivedServer, client, "onClientOutputError");
		char message[1024];
		int ret = snprintf(message, sizeof(message),
			"client socket write error: %s (errno=%d)",
			getErrorDesc(errcode), errcode);
		disconnectWithError(&client, StaticString(message, ret),
			getClientOutputErrorDisconnectionLogLevel(client, errcode));
	}

	virtual LoggingKit::Level getClientOutputErrorDisconnectionLogLevel(
		Client *client, int errcode) const
	{
		return LoggingKit::WARN;
	}

	virtual void onUpdateStatistics() {
		SKS_DEBUG("Updating statistics");
		ev_tstamp now = ev_now(this->getLoop());
		ev_tstamp duration = now - lastStatisticsUpdateTime;

		// Statistics are updated about every 5 seconds, so about 12 updates
		// per minute. We want the old average to decay to 5% after 1 minute
		// and 1 hour, respectively, so:
		// 1 minute: 1 - exp(ln(0.05) / 12) = 0.22092219194555585
		// 1 hour  : 1 - exp(ln(0.05) / (60 * 12)) = 0.0041520953856636345
		clientAcceptSpeed1m = expMovingAverage(clientAcceptSpeed1m,
			(totalClientsAccepted - lastTotalClientsAccepted) / duration,
			0.22092219194555585);
		clientAcceptSpeed1h = expMovingAverage(clientAcceptSpeed1h,
			(totalClientsAccepted - lastTotalClientsAccepted) / duration,
			0.0041520953856636345);
	}

	virtual void onFinalizeStatisticsUpdate() {
		lastTotalClientsAccepted = totalClientsAccepted;
		lastStatisticsUpdateTime = ev_now(this->getLoop());
	}

	virtual void reinitializeClient(Client *client, int fd) {
		client->setConnState(Client::ACTIVE);
		SKC_TRACE(client, 2, "Client associated with file descriptor: " << fd);
		client->input.reinitialize(fd);
		client->output.reinitialize(fd);
	}

	virtual void deinitializeClient(Client *client) {
		client->input.deinitialize();
		client->output.deinitialize();
	}

	virtual void onShutdown(bool forceDisconnect) {
		// Do nothing.
	}

public:
	/***** Public methods *****/

	BaseServer(Context *context, const BaseServerSchema &schema,
		const Json::Value &initialConfig = Json::Value(),
		const ConfigKit::Translator &translator = ConfigKit::DummyTranslator())
		: config(schema, initialConfig, translator),
		  configRlz(config),
		  shutdownFinishCallback(NULL),
		  serverState(ACTIVE),
		  freeClientCount(0),
		  activeClientCount(0),
		  disconnectedClientCount(0),
		  peakActiveClientCount(0),
		  totalClientsAccepted(0),
		  lastTotalClientsAccepted(0),
		  totalBytesConsumed(0),
		  lastStatisticsUpdateTime(ev_time()),
		  clientAcceptSpeed1m(-1),
		  clientAcceptSpeed1h(-1),
		  ctx(context),
		  nextClientNumber(1),
		  nEndpoints(0),
		  accept4Available(true)
	{
		preinitialize(context);
	}

	virtual ~BaseServer() {
		P_ASSERT_EQ(serverState, FINISHED_SHUTDOWN);
	}


	/***** Initialization, listening and shutdown *****/

	virtual void initialize() {
		statisticsUpdateWatcher.set(5, 5);
		statisticsUpdateWatcher.start();
	}

	// Pre-create multiple client objects so that they get allocated
	// near each other in memory. Hopefully increases CPU cache locality.
	void createSpareClients() {
		for (unsigned int i = 0; i < configRlz.minSpareClients; i++) {
			Client *client = createNewClientObject();
			client->setConnState(Client::IN_FREELIST);
			STAILQ_INSERT_HEAD(&freeClients, client, nextClient.freeClient);
			freeClientCount++;
		}
	}

	void listen(int fd) {
		#ifdef EOPNOTSUPP
			#define EXTENSION_EOPNOTSUPP EOPNOTSUPP
		#else
			#define EXTENSION_EOPNOTSUPP ENOTSUP
		#endif

		TRACE_POINT();
		assert(nEndpoints < SERVER_KIT_MAX_SERVER_ENDPOINTS);
		int flag = 1;
		setNonBlocking(fd);
		if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int)) == -1
		 && errno != ENOPROTOOPT
		 && errno != ENOTSUP
		 && errno != EXTENSION_EOPNOTSUPP)
		{
			int e = errno;
			SKS_WARN("Cannot disable Nagle's algorithm on a TCP socket: " <<
				strerror(e) << " (errno=" << e << ")");
		}
		ev_io_init(&endpoints[nEndpoints], _onAcceptable, fd, EV_READ);
		endpoints[nEndpoints].data = this;
		ev_io_start(ctx->libev->getLoop(), &endpoints[nEndpoints]);
		nEndpoints++;

		#undef EXTENSION_EOPNOTSUPP
	}

	void shutdown(bool forceDisconnect = false) {
		if (serverState != ACTIVE) {
			return;
		}

		vector<Client *> clients;
		Client *client;
		typename vector<Client *>::iterator v_it, v_end;

		SKS_DEBUG("Shutting down");
		serverState = SHUTTING_DOWN;
		onShutdown(forceDisconnect);

		// Stop listening on all endpoints.
		acceptResumptionWatcher.stop();
		for (uint8_t i = 0; i < nEndpoints; i++) {
			ev_io_stop(ctx->libev->getLoop(), &endpoints[i]);
		}

		if (activeClientCount == 0 && disconnectedClientCount == 0) {
			finishShutdown();
			return;
		}

		// Once we've set serverState to SHUTTING_DOWN, `activeClientCount` will no
		// longer grow, but may change due to hooks and callbacks.
		// So we make a copy of the client list here and operate on that.
		clients.reserve(activeClientCount);
		TAILQ_FOREACH (client, &activeClients, nextClient.activeOrDisconnectedClient) {
			P_ASSERT_EQ(client->getConnState(), Client::ACTIVE);
			refClient(client, __FILE__, __LINE__);
			clients.push_back(client);
		}

		// Disconnect each active client.
		v_end = clients.end();
		for (v_it = clients.begin(); v_it != v_end; v_it++) {
			client = (Client *) *v_it;
			Client *c = client;
			if (forceDisconnect || shouldDisconnectClientOnShutdown(client)) {
				disconnectWithError(&client, "server is shutting down");
			}
			unrefClient(c, __FILE__, __LINE__);
		}

		// When all active and disconnected clients are gone,
		// finishShutdown() will be called to set state to FINISHED_SHUTDOWN.
	}


	void feedNewClients(const int *fds, unsigned int size) {
		Client *client;
		Client *acceptedClients[MAX_ACCEPT_BURST_COUNT];

		assert(size > 0);
		assert(size <= MAX_ACCEPT_BURST_COUNT);
		P_ASSERT_EQ(serverState, ACTIVE);

		activeClientCount += size;
		totalClientsAccepted += size;

		for (unsigned int i = 0; i < size; i++) {
			client = checkoutClientObject();
			TAILQ_INSERT_HEAD(&activeClients, client, nextClient.activeOrDisconnectedClient);
			acceptedClients[i] = client;
			client->number = getNextClientNumber();
			reinitializeClient(client, fds[i]);
			P_LOG_FILE_DESCRIPTOR_PURPOSE(fds[i], "Server " << getServerName()
				<< ", client " << getClientName(client));
		}

		SKS_DEBUG(size << " new client(s) accepted; there are now " <<
			activeClientCount << " active client(s)");

		onClientsAccepted(acceptedClients, size);
	}


	/***** Server management *****/

	virtual void compact(LoggingKit::Level logLevel = LoggingKit::NOTICE) {
		unsigned int count = freeClientCount;

		while (!STAILQ_EMPTY(&freeClients)) {
			Client *client = STAILQ_FIRST(&freeClients);
			P_ASSERT_EQ(client->getConnState(), Client::IN_FREELIST);
			client->refcount.store(2, boost::memory_order_relaxed);
			freeClientCount--;
			STAILQ_REMOVE_HEAD(&freeClients, nextClient.freeClient);
			delete client;
		}
		assert(freeClientCount == 0);

		SKS_LOG(logLevel, __FILE__, __LINE__,
			"Freed " << count << " spare client objects");
	}


	/***** Client management *****/

	// Ensures that the buffer is NULL-terminated.
	virtual unsigned int getClientName(const Client *client, char *buf, size_t size) const {
		unsigned int ret = uintToString(client->number, buf, size - 1);
		buf[ret] = '\0';
		return ret;
	}

	string getClientName(const Client *client) {
		char buf[128];
		unsigned int size = getClientName(client, buf, sizeof(buf));
		return string(buf, size);
	}

	vector<ClientRefType> getActiveClients() {
		vector<ClientRefType> result;
		Client *client;

		TAILQ_FOREACH (client, &activeClients, nextClient.activeOrDisconnectedClient) {
			P_ASSERT_EQ(client->getConnState(), Client::ACTIVE);
			result.push_back(ClientRefType(client, __FILE__, __LINE__));
		}
		return result;
	}

	Client *lookupClient(int fd) {
		Client *client;

		TAILQ_FOREACH (client, &activeClients, nextClient.activeOrDisconnectedClient) {
			P_ASSERT_EQ(client->getConnState(), Client::ACTIVE);
			if (client->fd == fd) {
				return client;
			}
		}
		return NULL;
	}

	Client *lookupClient(const StaticString &clientName) {
		Client *client;

		TAILQ_FOREACH (client, &activeClients, nextClient.activeOrDisconnectedClient) {
			P_ASSERT_EQ(client->getConnState(), Client::ACTIVE);
			char buf[512];
			unsigned int size;

			size = getClientName(client, buf, sizeof(buf));
			if (StaticString(buf, size) == clientName) {
				return client;
			}
		}
		return NULL;
	}

	bool clientOnUnixDomainSocket(Client *client) {
		union {
			struct sockaddr genericAddress;
			struct sockaddr_un unixAddress;
			struct sockaddr_in inetAddress;
		} addr;
		socklen_t len = sizeof(addr);
		int ret;

		do {
			ret = getsockname(client->getFd(), &addr.genericAddress, &len);
		} while (ret == -1 && errno == EINTR);
		if (ret == -1) {
			int e = errno;
			throw SystemException("Unable to autodetect socket type (getsockname() failed)", e);
		} else {
			#ifdef AF_UNIX
				return addr.genericAddress.sa_family == AF_UNIX;
			#else
				return addr.genericAddress.sa_family == AF_LOCAL;
			#endif
		}
	}

	/** Increase client reference count. */
	void refClient(Client *client, const char *file, unsigned int line) {
		int oldRefcount = client->refcount.fetch_add(1, boost::memory_order_relaxed);
		SKC_TRACE_WITH_POS(client, 3, file, line,
			"Refcount increased; it is now " << (oldRefcount + 1));
	}

	/** Decrease client reference count. Adds client to the
	 * freelist if reference count drops to 0.
	 */
	void unrefClient(Client *client, const char *file, unsigned int line) {
		int oldRefcount = client->refcount.fetch_sub(1, boost::memory_order_release);
		assert(oldRefcount >= 1);

		SKC_TRACE_WITH_POS(client, 3, file, line,
			"Refcount decreased; it is now " << (oldRefcount - 1));
		if (oldRefcount == 1) {
			boost::atomic_thread_fence(boost::memory_order_acquire);

			if (ctx->libev->onEventLoopThread()) {
				assert(client->getConnState() != Client::IN_FREELIST);
				/* As long as the client is still in the ACTIVE state, it has at least
				 * one reference, namely from the Server itself. Therefore it's impossible
				 * to get to a zero reference count without having disconnected a client. */
				P_ASSERT_EQ(client->getConnState(), Client::DISCONNECTED);
				clientReachedZeroRefcount(client);
			} else {
				// Let the event loop handle the client reaching the 0 refcount.
				SKC_TRACE(client, 3, "Passing client object to event loop thread");
				passClientToEventLoopThread(client);
			}
		}
	}

	bool disconnect(int fd) {
		assert(serverState != FINISHED_SHUTDOWN);
		Client *client = lookupClient(fd);
		if (client != NULL) {
			return disconnect(&client);
		} else {
			return false;
		}
	}

	bool disconnect(const StaticString &clientName) {
		assert(serverState != FINISHED_SHUTDOWN);
		Client *client = lookupClient(clientName);
		if (client != NULL) {
			return disconnect(&client);
		} else {
			return false;
		}
	}

	bool disconnect(Client **client) {
		Client *c = *client;
		if (c->getConnState() != Client::ACTIVE) {
			return false;
		}

		int fdnum = c->getFd();
		SKC_TRACE(c, 2, "Disconnecting; there are now " << (activeClientCount - 1) <<
			" active clients");
		onClientDisconnecting(c);

		c->setConnState(ClientType::DISCONNECTED);
		TAILQ_REMOVE(&activeClients, c, nextClient.activeOrDisconnectedClient);
		activeClientCount--;
		TAILQ_INSERT_HEAD(&disconnectedClients, c, nextClient.activeOrDisconnectedClient);
		disconnectedClientCount++;

		deinitializeClient(c);
		SKC_TRACE(c, 2, "Closing client file descriptor: " << fdnum);
		try {
			safelyClose(fdnum);
			P_LOG_FILE_DESCRIPTOR_CLOSE(fdnum);
		} catch (const SystemException &e) {
			SKC_WARN(c, "An error occurred while closing the client file descriptor: " <<
				e.what() << " (errno=" << e.code() << ")");
		}

		*client = NULL;
		onClientDisconnected(c);
		unrefClient(c, __FILE__, __LINE__);
		return true;
	}

	void disconnectWithWarning(Client **client, const StaticString &message) {
		SKC_WARN(*client, "Disconnecting client with warning: " << message);
		disconnect(client);
	}

	void disconnectWithError(Client **client, const StaticString &message,
		LoggingKit::Level logLevel = LoggingKit::WARN)
	{
		SKC_LOG(*client, logLevel, "Disconnecting client with error: " << message);
		disconnect(client);
	}


	/***** Introspection *****/

	OXT_FORCE_INLINE
	Context *getContext() {
		return ctx;
	}

	OXT_FORCE_INLINE
	const Context *getContext() const {
		return ctx;
	}

	OXT_FORCE_INLINE
	struct ev_loop *getLoop() const {
		return ctx->libev->getLoop();
	}

	virtual StaticString getServerName() const {
		return P_STATIC_STRING("Server");
	}

	bool prepareConfigChange(const Json::Value &updates,
		vector<ConfigKit::Error> &errors, BaseServerConfigChangeRequest &req)
	{
		req.config.reset(new ConfigKit::Store(config, updates, errors));
		if (errors.empty()) {
			req.configRlz.reset(new BaseServerConfigRealization(*req.config));
		}
		return errors.empty();
	}

	void commitConfigChange(BaseServerConfigChangeRequest &req)
		BOOST_NOEXCEPT_OR_NOTHROW
	{
		config.swap(*req.config);
		configRlz.swap(*req.configRlz);
	}

	virtual Json::Value inspectConfig() const {
		return config.inspect();
	}

	virtual Json::Value inspectStateAsJson() const {
		Json::Value doc = ctx->inspectStateAsJson();
		const Client *client;

		doc["pid"] = (unsigned int) getpid();
		doc["server_state"] = getServerStateString();
		doc["free_client_count"] = freeClientCount;
		Json::Value &activeClientsDoc = doc["active_clients"] = Json::Value(Json::objectValue);
		doc["active_client_count"] = activeClientCount;
		Json::Value &disconnectedClientsDoc = doc["disconnected_clients"] = Json::Value(Json::objectValue);
		doc["disconnected_client_count"] = disconnectedClientCount;
		doc["peak_active_client_count"] = peakActiveClientCount;
		doc["client_accept_speed"]["1m"] = averageSpeedToJson(
			capFloatPrecision(clientAcceptSpeed1m * 60),
			"minute", "1 minute", -1);
		doc["client_accept_speed"]["1h"] = averageSpeedToJson(
			capFloatPrecision(clientAcceptSpeed1h * 60),
			"minute", "1 hour", -1);
		doc["total_clients_accepted"] = (Json::UInt64) totalClientsAccepted;
		doc["total_bytes_consumed"] = (Json::UInt64) totalBytesConsumed;

		TAILQ_FOREACH (client, &activeClients, nextClient.activeOrDisconnectedClient) {
			Json::Value subdoc;
			char clientName[16];

			getClientName(client, clientName, sizeof(clientName));
			activeClientsDoc[clientName] = inspectClientStateAsJson(client);
		}

		TAILQ_FOREACH (client, &disconnectedClients, nextClient.activeOrDisconnectedClient) {
			Json::Value subdoc;
			char clientName[16];

			getClientName(client, clientName, sizeof(clientName));
			disconnectedClientsDoc[clientName] = inspectClientStateAsJson(client);
		}

		return doc;
	}

	virtual Json::Value inspectClientStateAsJson(const Client *client) const {
		Json::Value doc;
		char clientName[16];

		assert(client->getConnState() != Client::IN_FREELIST);
		getClientName(client, clientName, sizeof(clientName));
		doc["connection_state"] = client->getConnStateString();
		doc["name"] = clientName;
		doc["number"] = client->number;
		doc["refcount"] = client->refcount.load(boost::memory_order_relaxed);
		doc["output_channel_state"] = client->output.inspectAsJson();

		return doc;
	}


	/***** Miscellaneous *****/

	/** Get a thread-safe reference to the client. As long as the client
	 * has a reference, it will never be added to the freelist.
	 */
	ClientRefType getClientRef(Client *client, const char *file, unsigned int line) {
		return ClientRefType(client, file, line);
	}

	/**
	 * Returns a pointer to the BaseServer that created the given Client object.
	 * Unlike the void pointer that Client::getServerBaseClassPointer() returns,
	 * this method's typed return value allows safe recasting of the result pointer.
	 */
	static const BaseServer *getConstServerFromClient(const Client *client) {
		return static_cast<BaseServer *>(client->getServerBaseClassPointer());
	}

	static BaseServer *getServerFromClient(Client *client) {
		return static_cast<BaseServer *>(client->getServerBaseClassPointer());
	}


	/***** Friend-public methods and hook implementations *****/

	void _refClient(Client *client, const char *file, unsigned int line) {
		refClient(client, file, line);
	}

	void _unrefClient(Client *client, const char *file, unsigned int line) {
		unrefClient(client, __FILE__, __LINE__);
	}

	static bool _getClientNameFromTracePoint(char *output, unsigned int size, void *userData) {
		Client *client = static_cast<Client *>(userData);
		BaseServer *server = getServerFromClient(client);
		char *pos = output;
		const char *end = output + size;

		pos = appendData(pos, end, "Client ");
		server->getClientName(client, pos, end - pos);
		return true;
	}

	virtual bool hook_isConnected(Hooks *hooks, void *source) {
		Client *client = static_cast<Client *>(static_cast<BaseClient *>(hooks->userData));
		return client->connected();
	}

	virtual void hook_ref(Hooks *hooks, void *source, const char *file, unsigned int line) {
		Client *client = static_cast<Client *>(static_cast<BaseClient *>(hooks->userData));
		refClient(client, file, line);
	}

	virtual void hook_unref(Hooks *hooks, void *source, const char *file, unsigned int line) {
		Client *client = static_cast<Client *>(static_cast<BaseClient *>(hooks->userData));
		unrefClient(client, file, line);
	}
};


template <typename Client = Client>
class Server: public BaseServer<Server<Client>, Client> {
public:
	Server(Context *context, const BaseServerSchema &schema,
		const Json::Value &initialConfig = Json::Value())
		: BaseServer<Server, Client>(context, schema, initialConfig)
		{ }
};


} // namespace ServerKit
} // namespace Passenger

#endif /* _PASSENGER_SERVER_KIT_SERVER_H_ */

Zerion Mini Shell 1.0