<commit_before>/*
 *  Phusion Passenger - http://www.modrails.com/
 *  Copyright (c) 2010 Phusion
 *
 *  "Phusion Passenger" is a trademark of <NAME> & <NAME>.
 *
 *  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_EVENTED_SERVER_H_
#define _PASSENGER_EVENTED_SERVER_H_

#include <ev++.h>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <oxt/system_calls.hpp>
#include <oxt/macros.hpp>
#include <algorithm>
#include <string>
#include <set>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cerrno>
#include "FileDescriptor.h"
#include "StaticString.h"
#include "Logging.h"
#include "Utils/IOUtils.h"

namespace Passenger {

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


/**
 * A base class for writing single-threaded, evented servers that use non-blocking I/O.
 * It uses libev for its event loop. EventedServer handles much of the situps regarding
 * client connection management and output buffering and tries to make it easy to
 * implement a zero-copy architecture.
 *
 * <h2>Basic usage</h2>
 * Derived classes can override the onClientReadable() method, which is called every time
 * a specific client becomes readable. It is passed a Client object which contains information
 * about the client, such as its file descriptor. One can use the read() system call in
 * that method to receive data from the client. Please note that client file descriptors
 * are always set to non-blocking mode so you need to handle this gracefully.
 *
 * EventedServer provides the write() method for sending data to the client. This method
 * will attempt to send the data to the client immediately; if it fails with EAGAIN then
 * EventedServer will take care of scheduling the send at a later time when the client
 * is ready again to receive data.
 *
 * To disconnect the client, call disconnect(). The connection might not be actually
 * closed until all pending outgoing data have been sent out, but all the gory details
 * is taken care of for you.
 *
 * <h2>Keeping per-client information</h2>
 * If you need to keep per-client information then you can override the createClient()
 * method and make it return an object that's a subclass of EventedServer::Client. This
 * object is passed to onClientReadable(), so in there you can just cast the client object
 * to your subclass.
 */
class EventedServer {
protected:
	/** Contains various client information. */
	struct Client: public enable_shared_from_this<Client> {
		enum {
			/**
			 * This is the initial state for a client. It means we're
			 * connected to the client, ready to receive data and
			 * there's no pending outgoing data.
			 */
			ES_CONNECTED,
			
			/**
			 * This state is entered from ES_CONNECTED when the write()
			 * method fails to send all data immediately and EventedServer
			 * schedules some data to be sent later, when the socket becomes
			 * readable again. In here we might be watching for read events,
			 * but not if there's too much pending data, in which case
			 * EventedServer will concentrate on sending out all
			 * pending data before watching read events again.
			 */
			ES_WRITES_PENDING,
			
			/**
			 * This state is entered from ES_WRITES_PENDING state when
			 * disconnect() is called. It means that we want to close
			 * the connection as soon as all pending outgoing data has
			 * been sent. As soon as that happens it'll transition
			 * to ES_DISCONNECTED.
			 */
			ES_DISCONNECTING_WITH_WRITES_PENDING,
			
			/**
			 * Final state. Client connection has been closed. No
			 * I/O with the client is possible.
			 */
			ES_DISCONNECTED
		} state;
		
		EventedServer *server;
		FileDescriptor fd;
		ev::io readWatcher;
		ev::io writeWatcher;
		bool readWatcherStarted;
		bool writeWatcherStarted;
		/** Pending outgoing data is stored here. */
		string outbox;
		
		Client(EventedServer *_server)
			: server(_server),
			  readWatcher(_server->loop),
			  writeWatcher(_server->loop)
		{ }
		
		void _onReadable(ev::io &w, int revents) {
			server->onClientReadable(shared_from_this());
		}
		
		void _onWritable(ev::io &w, int revents) {
			server->onClientWritable(shared_from_this());
		}
		
		/** Returns an identifier for this client. */
		string name() const {
			return toString(fd);
		}
	};
	
	typedef shared_ptr<Client> ClientPtr;
	typedef set<ClientPtr> ClientSet;
	
	const ClientSet &getClients() const {
		return clients;
	}
	
	void write(const ClientPtr &client, const char *data) {
		write(client, StaticString(data));
	}
	
	void write(const ClientPtr &client, const StaticString &data) {
		write(client, &data, 1);
	}
	
	/**
	 * Sends data to the given client. This method will try to send the data
	 * immediately (in which no intermediate copies of the data will be made),
	 * but if the client is not yet ready to receive data then the data will
	 * be buffered and scheduled for sending later.
	 *
	 * If an I/O error was encountered then the client connection will be closed.
	 *
	 * If the client connection has already been closed then this method
	 * does nothing.
	 */
	void write(const ClientPtr &client, const StaticString data[], unsigned int count) {
		if (client->state == Client::ES_DISCONNECTED) {
			return;
		}
		
		ssize_t ret;
		this_thread::disable_syscall_interruption dsi;
		<commit_msg>Add EventedServer method for disabling Nagle's algorithm.<commit_after>/*
 *  Phusion Passenger - http://www.modrails.com/
 *  Copyright (c) 2010 Phusion
 *
 *  "Phusion Passenger" is a trademark of <NAME> & <NAME>.
 *
 *  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_EVENTED_SERVER_H_
#define _PASSENGER_EVENTED_SERVER_H_

#include <ev++.h>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <oxt/system_calls.hpp>
#include <oxt/macros.hpp>
#include <algorithm>
#include <string>
#include <set>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cerrno>
#include "FileDescriptor.h"
#include "StaticString.h"
#include "Logging.h"
#include "Utils/IOUtils.h"

namespace Passenger {

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


/**
 * A base class for writing single-threaded, evented servers that use non-blocking I/O.
 * It uses libev for its event loop. EventedServer handles much of the situps regarding
 * client connection management and output buffering and tries to make it easy to
 * implement a zero-copy architecture.
 *
 * <h2>Basic usage</h2>
 * Derived classes can override the onClientReadable() method, which is called every time
 * a specific client becomes readable. It is passed a Client object which contains information
 * about the client, such as its file descriptor. One can use the read() system call in
 * that method to receive data from the client. Please note that client file descriptors
 * are always set to non-blocking mode so you need to handle this gracefully.
 *
 * EventedServer provides the write() method for sending data to the client. This method
 * will attempt to send the data to the client immediately; if it fails with EAGAIN then
 * EventedServer will take care of scheduling the send at a later time when the client
 * is ready again to receive data.
 *
 * To disconnect the client, call disconnect(). The connection might not be actually
 * closed until all pending outgoing data have been sent out, but all the gory details
 * is taken care of for you.
 *
 * <h2>Keeping per-client information</h2>
 * If you need to keep per-client information then you can override the createClient()
 * method and make it return an object that's a subclass of EventedServer::Client. This
 * object is passed to onClientReadable(), so in there you can just cast the client object
 * to your subclass.
 */
class EventedServer {
protected:
	/** Contains various client information. */
	struct Client: public enable_shared_from_this<Client> {
		enum {
			/**
			 * This is the initial state for a client. It means we're
			 * connected to the client, ready to receive data and
			 * there's no pending outgoing data.
			 */
			ES_CONNECTED,
			
			/**
			 * This state is entered from ES_CONNECTED when the write()
			 * method fails to send all data immediately and EventedServer
			 * schedules some data to be sent later, when the socket becomes
			 * readable again. In here we might be watching for read events,
			 * but not if there's too much pending data, in which case
			 * EventedServer will concentrate on sending out all
			 * pending data before watching read events again.
			 */
			ES_WRITES_PENDING,
			
			/**
			 * This state is entered from ES_WRITES_PENDING state when
			 * disconnect() is called. It means that we want to close
			 * the connection as soon as all pending outgoing data has
			 * been sent. As soon as that happens it'll transition
			 * to ES_DISCONNECTED.
			 */
			ES_DISCONNECTING_WITH_WRITES_PENDING,
			
			/**
			 * Final state. Client connection has been closed. No
			 * I/O with the client is possible.
			 */
			ES_DISCONNECTED
		} state;
		
		EventedServer *server;
		FileDescriptor fd;
		ev::io readWatcher;
		ev::io writeWatcher;
		bool readWatcherStarted;
		bool writeWatcherStarted;
		/** Pending outgoing data is stored here. */
		string outbox;
		
		Client(EventedServer *_server)
			: server(_server),
			  readWatcher(_server->loop),
			  writeWatcher(_server->loop)
		{ }
		
		void _onReadable(ev::io &w, int revents) {
			server->onClientReadable(shared_from_this());
		}
		
		void _onWritable(ev::io &w, int revents) {
			server->onClientWritable(shared_from_this());
		}
		
		/** Returns an identifier for this client. */
		string name() const {
			return toString(fd);
		}
	};
	
	typedef shared_ptr<Client> ClientPtr;
	typedef set<ClientPtr> ClientSet;
	
	const ClientSet &getClients() const {
		return clients;
	}
	
	/**
	 * Disables Nagle's algorithm on the given client's socket. Nagle's algorithm
	 * is enabled by default.
	 */
	void setNoDelay(const ClientPtr &client) const {
		int optval = 1;
		syscalls::setsockopt(clientfd, IPPROTO_TCP, TCP_NODELAY,
			&optval, sizeof(optval));
	}
	
	void write(const ClientPtr &client, const StaticString &data) {
		write(client, &data, 1);
	}
	
	/**
	 * Sends data to the given client. This method will try to send the data
	 * immediately (in which no intermediate copies of the data will be made),
	 * but if the client is not yet ready to receive data then the data will
	 * be buffered and scheduled for sending later.
	 *
	 * If an I/O error was encountered then the client connection will be closed.
	 *
	 * If the client connection has already been closed then this method
	 * does nothing.
	 */
	void write(const ClientPtr &client, const StaticString data[], unsigned int count) {
		if (client->state == Client::ES_DISCONNECTED) {
			return;
		}
		
		ssize_t ret;
		this_thread::disable_syscall_interruption dsi;
		
{'fraction_non_alphanumeric': 0.07632303456714591, 'fraction_numerical': 0.001453043744264301, 'mean_word_length': 3.736327417602318, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5907464', 'n_tokens_mistral': 4032, 'n_tokens_neox': 3610, 'n_words': 1632}