blob: ec024c0311c481dafc78f98e3dbd8f7d05801adc [file] [log] [blame]
#ifndef _THRIFT_SERVER_TNONBLOCKINGSERVER_H_
#define _THRIFT_SERVER_TNONBLOCKINGSERVER_H_ 1
#include "Thrift.h"
#include "server/TServer.h"
#include "transport/TMemoryBuffer.h"
#include <stack>
#include <event.h>
namespace facebook { namespace thrift { namespace server {
using boost::shared_ptr;
// Forward declaration of class
class TConnection;
/**
* This is a non-blocking server in C++ for high performance that operates a
* single IO thread. It assumes that all incoming requests are framed with a
* 4 byte length indicator and writes out responses using the same framing.
*
* It does not use the TServerTransport framework, but rather has socket
* operations hardcoded for use with select.
*
* @author Mark Slee <mcslee@facebook.com>
*/
class TNonblockingServer : public TServer {
private:
// Listen backlog
static const int LISTEN_BACKLOG = 1024;
// Server socket file descriptor
int serverSocket_;
// Port server runs on
int port_;
// Whether to frame responses
bool frameResponses_;
/**
* This is a stack of all the objects that have been created but that
* are NOT currently in use. When we close a connection, we place it on this
* stack so that the object can be reused later, rather than freeing the
* memory and reallocating a new object later.
*/
std::stack<TConnection*> connectionStack_;
void handleEvent(int fd, short which);
public:
TNonblockingServer(shared_ptr<TProcessor> processor,
shared_ptr<TServerOptions> options,
int port) :
TServer(processor, options),
serverSocket_(0),
port_(port),
frameResponses_(true) {}
~TNonblockingServer() {}
void setFrameResponses(bool frameResponses) {
frameResponses_ = frameResponses;
}
bool getFrameResponses() {
return frameResponses_;
}
TConnection* createConnection(int socket, short flags);
void returnConnection(TConnection* connection);
static void eventHandler(int fd, short which, void* v) {
((TNonblockingServer*)v)->handleEvent(fd, which);
}
void serve();
};
/**
* Two states for sockets, recv and send mode
*/
enum TSocketState {
SOCKET_RECV,
SOCKET_SEND
};
/**
* Four states for the nonblocking servr:
* 1) initialize
* 2) read 4 byte frame size
* 3) read frame of data
* 4) send back data (if any)
*/
enum TAppState {
APP_INIT,
APP_READ_FRAME_SIZE,
APP_READ_REQUEST,
APP_SEND_FRAME_SIZE,
APP_SEND_RESULT
};
/**
* Represents a connection that is handled via libevent. This connection
* essentially encapsulates a socket that has some associated libevent state.
*/
class TConnection {
private:
// Server handle
TNonblockingServer* server_;
// Socket handle
int socket_;
// Libevent object
struct event event_;
// Libevent flags
short eventFlags_;
// Socket mode
TSocketState socketState_;
// Application state
TAppState appState_;
// How much data needed to read
uint32_t readWant_;
// Where in the read buffer are we
uint32_t readBufferPos_;
// Read buffer
uint8_t* readBuffer_;
// Read buffer size
uint32_t readBufferSize_;
// Write buffer
uint8_t* writeBuffer_;
// Write buffer size
uint32_t writeBufferSize_;
// How far through writing are we?
uint32_t writeBufferPos_;
// Frame size
int32_t frameSize_;
// Transport to read from
shared_ptr<TMemoryBuffer> inputTransport_;
// Transport that processor writes to
shared_ptr<TMemoryBuffer> outputTransport_;
// Go into read mode
void setRead() {
setFlags(EV_READ | EV_PERSIST);
}
// Go into write mode
void setWrite() {
setFlags(EV_WRITE | EV_PERSIST);
}
// Set event flags
void setFlags(short eventFlags);
// Libevent handlers
void workSocket();
// Close this client and reset
void close();
public:
// Constructor
TConnection(int socket, short eventFlags, TNonblockingServer *s) {
readBuffer_ = (uint8_t*)malloc(1024);
if (readBuffer_ == NULL) {
throw new facebook::thrift::Exception("Out of memory.");
}
readBufferSize_ = 1024;
// Allocate input and output tranpsorts
inputTransport_ = shared_ptr<TMemoryBuffer>(new TMemoryBuffer(readBuffer_, readBufferSize_));
outputTransport_ = shared_ptr<TMemoryBuffer>(new TMemoryBuffer());
init(socket, eventFlags, s);
}
// Initialize
void init(int socket, short eventFlags, TNonblockingServer *s);
// Transition into a new state
void transition();
// Handler wrapper
static void eventHandler(int fd, short which, void* v) {
assert(fd = ((TConnection*)v)->socket_);
((TConnection*)v)->workSocket();
}
};
}}} // facebook::thrift::server
#endif // #ifndef _THRIFT_SERVER_TSIMPLESERVER_H_