|
|
|
@ -98,20 +98,13 @@ inline http::Version to_version(const std::string &version) {
|
|
|
|
* Implementation of Server
|
|
|
|
* Implementation of Server
|
|
|
|
* ********************************************************************************************* */
|
|
|
|
* ********************************************************************************************* */
|
|
|
|
Server::Server(uint port)
|
|
|
|
Server::Server(uint port)
|
|
|
|
: _port(port), _socket(-1)
|
|
|
|
: _port(port), _socket(-1), _queue()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// pass...
|
|
|
|
pthread_mutex_init(&_queue_lock, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Server::~Server() {
|
|
|
|
Server::~Server() {
|
|
|
|
_is_running = false;
|
|
|
|
_is_running = false;
|
|
|
|
// Close all connections
|
|
|
|
|
|
|
|
std::set<Connection *>::iterator con = _connections.begin();
|
|
|
|
|
|
|
|
for (; con != _connections.end(); con++) {
|
|
|
|
|
|
|
|
// Wait for the connection to close
|
|
|
|
|
|
|
|
(*con)->close(true);
|
|
|
|
|
|
|
|
delete *con;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free all handler
|
|
|
|
// Free all handler
|
|
|
|
std::list<Handler *>::iterator item = _handler.begin();
|
|
|
|
std::list<Handler *>::iterator item = _handler.begin();
|
|
|
|
for (; item != _handler.end(); item++) { delete *item; }
|
|
|
|
for (; item != _handler.end(); item++) { delete *item; }
|
|
|
|
@ -157,49 +150,70 @@ Server::start(bool wait) {
|
|
|
|
void
|
|
|
|
void
|
|
|
|
Server::stop(bool wait) {
|
|
|
|
Server::stop(bool wait) {
|
|
|
|
_is_running = false;
|
|
|
|
_is_running = false;
|
|
|
|
// Close all open connections
|
|
|
|
|
|
|
|
std::set<Connection *>::iterator con = _connections.begin();
|
|
|
|
|
|
|
|
for (; con != _connections.end(); con++) {
|
|
|
|
|
|
|
|
(*con)->close(wait);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close the socket we listen on
|
|
|
|
// Close the socket we listen on
|
|
|
|
::close(_socket); _socket = -1;
|
|
|
|
::close(_socket); _socket = -1;
|
|
|
|
|
|
|
|
// wait for server to join
|
|
|
|
|
|
|
|
if (wait) { this->wait(); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
void
|
|
|
|
Server::wait() {
|
|
|
|
Server::wait() {
|
|
|
|
void *ret=0;
|
|
|
|
void *ret=0;
|
|
|
|
pthread_join(_thread, &ret);
|
|
|
|
pthread_join(_thread, &ret);
|
|
|
|
|
|
|
|
// wait for all handlers to join
|
|
|
|
|
|
|
|
while (_threads.size()) {
|
|
|
|
|
|
|
|
void *ret = 0;
|
|
|
|
|
|
|
|
pthread_join(*_threads.begin(), &ret);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
void *
|
|
|
|
Server::_listen_main(void *ctx) {
|
|
|
|
Server::_listen_main(void *ctx)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// Get server instance
|
|
|
|
Server *self = (Server *)ctx;
|
|
|
|
Server *self = (Server *)ctx;
|
|
|
|
while (self->_is_running) {
|
|
|
|
// Whil server is running
|
|
|
|
|
|
|
|
while (self->_is_running)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// Wait for incomming connections
|
|
|
|
listen(self->_socket, 5);
|
|
|
|
listen(self->_socket, 5);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create socket to client
|
|
|
|
struct sockaddr_in cli_addr;
|
|
|
|
struct sockaddr_in cli_addr;
|
|
|
|
socklen_t clilen = sizeof(cli_addr);
|
|
|
|
socklen_t clilen = sizeof(cli_addr);
|
|
|
|
int socket = accept(self->_socket, (struct sockaddr *) &cli_addr, &clilen);
|
|
|
|
int socket = accept(self->_socket, (struct sockaddr *) &cli_addr, &clilen);
|
|
|
|
if (socket < 0) { continue; }
|
|
|
|
if (socket < 0) { continue; }
|
|
|
|
try { self->_connections.insert(new Connection(self, socket)); }
|
|
|
|
|
|
|
|
catch (...) { }
|
|
|
|
// Construct connection object from socket
|
|
|
|
// Free closed connections
|
|
|
|
try {
|
|
|
|
std::list<Connection *> closed_connections;
|
|
|
|
pthread_mutex_lock(&self->_queue_lock);
|
|
|
|
std::set<Connection *>::iterator item = self->_connections.begin();
|
|
|
|
self->_queue.push_back(Connection(self, socket));
|
|
|
|
for (; item != self->_connections.end(); item++) {
|
|
|
|
pthread_t thread;
|
|
|
|
if ((*item)->isClosed()) { closed_connections.push_back(*item); }
|
|
|
|
pthread_create(&thread, 0, &Server::_connection_main, self);
|
|
|
|
}
|
|
|
|
self->_threads.insert(thread);
|
|
|
|
std::list<Connection *>::iterator citem = closed_connections.begin();
|
|
|
|
pthread_mutex_unlock(&self->_queue_lock);
|
|
|
|
for (; citem != closed_connections.end(); citem++) {
|
|
|
|
} catch (...) {
|
|
|
|
self->_connections.erase(*citem);
|
|
|
|
pthread_mutex_unlock(&self->_queue_lock);
|
|
|
|
delete *citem;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
|
|
|
Server::_connection_main(void *ctx) {
|
|
|
|
|
|
|
|
Server *self = (Server *)ctx;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&self->_queue_lock);
|
|
|
|
|
|
|
|
Connection con = self->_queue.front(); self->_queue.pop_front();
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&self->_queue_lock);
|
|
|
|
|
|
|
|
con.main();
|
|
|
|
|
|
|
|
// Remove thread from list of running threads
|
|
|
|
|
|
|
|
self->_threads.erase(pthread_self());
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
void
|
|
|
|
Server::dispatch(const Request &request, Response &response) {
|
|
|
|
Server::dispatch(const Request &request, Response &response)
|
|
|
|
|
|
|
|
{
|
|
|
|
LogMessage msg(LOG_DEBUG);
|
|
|
|
LogMessage msg(LOG_DEBUG);
|
|
|
|
msg << "httpd: ";
|
|
|
|
msg << "httpd: ";
|
|
|
|
switch (request.method()) {
|
|
|
|
switch (request.method()) {
|
|
|
|
@ -230,70 +244,120 @@ Server::addHandler(Handler *handler) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ********************************************************************************************* *
|
|
|
|
/* ********************************************************************************************* *
|
|
|
|
* Implementation of HTTPD::Connection
|
|
|
|
* Implementation of http::Connection & http::ConnectionObj
|
|
|
|
* ********************************************************************************************* */
|
|
|
|
* ********************************************************************************************* */
|
|
|
|
Connection::Connection(Server *server, int socket)
|
|
|
|
ConnectionObj::ConnectionObj(Server *server, int cli_socket)
|
|
|
|
: _server(server), _socket(socket)
|
|
|
|
: server(server), socket(cli_socket), refcount(1)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Start new thread to parse requests
|
|
|
|
// pass...
|
|
|
|
if (pthread_create(&_thread, 0, &Connection::_main, (void *)this)) {
|
|
|
|
}
|
|
|
|
::close(_socket); _socket = -1;
|
|
|
|
|
|
|
|
RuntimeError err;
|
|
|
|
ConnectionObj::~ConnectionObj() {
|
|
|
|
err << "httpd: Can not create thread for connection.";
|
|
|
|
::close(socket); socket=-1;
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ConnectionObj *
|
|
|
|
|
|
|
|
ConnectionObj::ref() {
|
|
|
|
|
|
|
|
refcount++; return this;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
|
|
ConnectionObj::unref() {
|
|
|
|
|
|
|
|
refcount--;
|
|
|
|
|
|
|
|
if (0 == refcount) {
|
|
|
|
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Connection::Connection()
|
|
|
|
|
|
|
|
: _object(0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// pass...
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Connection::Connection(Server *server, int socket)
|
|
|
|
|
|
|
|
: _object(new ConnectionObj(server, socket))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// pass...
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Connection::Connection(const Connection &other)
|
|
|
|
|
|
|
|
: _object(other._object)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (_object) { _object->ref(); }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Connection::~Connection() {
|
|
|
|
Connection::~Connection() {
|
|
|
|
// Close the socket
|
|
|
|
// Close the socket
|
|
|
|
this->close(false);
|
|
|
|
if (_object) { _object->unref(); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Connection &
|
|
|
|
|
|
|
|
Connection::operator =(const Connection &other) {
|
|
|
|
|
|
|
|
if (_object) { _object->unref(); }
|
|
|
|
|
|
|
|
_object = other._object;
|
|
|
|
|
|
|
|
if (_object) { _object->ref(); }
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
void
|
|
|
|
Connection::close(bool wait) {
|
|
|
|
Connection::close(bool wait) {
|
|
|
|
if (-1 != _socket) {
|
|
|
|
if (0 == _object) { return; }
|
|
|
|
int socket = _socket; _socket = -1;
|
|
|
|
if (-1 != _object->socket) {
|
|
|
|
|
|
|
|
int socket = _object->socket; _object->socket = -1;
|
|
|
|
LogMessage msg(LOG_DEBUG);
|
|
|
|
LogMessage msg(LOG_DEBUG);
|
|
|
|
msg << "httpd: Close connection " << socket << ".";
|
|
|
|
msg << "httpd: Close connection " << socket << ".";
|
|
|
|
Logger::get().log(msg);
|
|
|
|
Logger::get().log(msg);
|
|
|
|
::close(socket);
|
|
|
|
::close(socket);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (wait) {
|
|
|
|
|
|
|
|
// Wait for the thread to exit.
|
|
|
|
|
|
|
|
void *ret = 0;
|
|
|
|
|
|
|
|
pthread_join(_thread, &ret);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
bool
|
|
|
|
Connection::isClosed() const {
|
|
|
|
Connection::isClosed() const {
|
|
|
|
return (-1 == _socket);
|
|
|
|
if (0 == _object) { return true; }
|
|
|
|
|
|
|
|
return (-1 == _object->socket);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
bool
|
|
|
|
Connection::_main(void *ctx)
|
|
|
|
Connection::send(const std::string &data) const {
|
|
|
|
|
|
|
|
const char *ptr = data.c_str();
|
|
|
|
|
|
|
|
size_t count = data.size();
|
|
|
|
|
|
|
|
while (count) {
|
|
|
|
|
|
|
|
int c = this->write(ptr, count);
|
|
|
|
|
|
|
|
if (c < 0) { return false; }
|
|
|
|
|
|
|
|
count -= c; ptr += c;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
|
|
Connection::main()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Connection *self = (Connection *)ctx;
|
|
|
|
int error = 0; socklen_t errorlen = sizeof(error);
|
|
|
|
// While socket is open
|
|
|
|
// While socket is open
|
|
|
|
int error = 0; socklen_t errorlen = sizeof (error);
|
|
|
|
while (0 == getsockopt(_object->socket, SOL_SOCKET, SO_ERROR, &error, &errorlen)) {
|
|
|
|
while (0 == getsockopt(self->_socket, SOL_SOCKET, SO_ERROR, &error, &errorlen)) {
|
|
|
|
// Contstruct request & reponse instances
|
|
|
|
Request request(self->_socket);
|
|
|
|
Request request(*this);
|
|
|
|
Response response(self->_socket);
|
|
|
|
Response response(*this);
|
|
|
|
// Parse request
|
|
|
|
// try to parse request
|
|
|
|
if (!request.parse()) {
|
|
|
|
if (! request.parse()) {
|
|
|
|
// On parser error or no keep-alive -> exit
|
|
|
|
// On parser error close connection -> exit
|
|
|
|
self->close(); return 0;
|
|
|
|
this->close(false); return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// on success -> dispatch request by server
|
|
|
|
// on success -> dispatch request by server
|
|
|
|
self->_server->dispatch(request, response);
|
|
|
|
_object->server->dispatch(request, response);
|
|
|
|
// If the connection is kept alive -> continue
|
|
|
|
// on protocol update
|
|
|
|
if ((! request.isKeepAlive()) || response.closeConnection()) {
|
|
|
|
if (this->protocolUpgrade()) { return; }
|
|
|
|
// Signal server to close connection
|
|
|
|
// If the connection is keep-alive -> continue
|
|
|
|
self->close(self); return 0;
|
|
|
|
if (! request.isKeepAlive()) {
|
|
|
|
|
|
|
|
// close connection -> exit
|
|
|
|
|
|
|
|
this->close(false); return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Signal server to close connection
|
|
|
|
// Close connection
|
|
|
|
self->close(self); return 0;
|
|
|
|
this->close(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -449,8 +513,8 @@ typedef enum {
|
|
|
|
} HttpRequestParserState;
|
|
|
|
} HttpRequestParserState;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Request::Request(int socket)
|
|
|
|
Request::Request(const Connection &connection)
|
|
|
|
: _socket(socket), _method(HTTP_UNKNOWN)
|
|
|
|
: _connection(connection), _method(HTTP_UNKNOWN)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// pass...
|
|
|
|
// pass...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -463,7 +527,7 @@ Request::parse() {
|
|
|
|
HttpRequestParserState state = READ_METHOD;
|
|
|
|
HttpRequestParserState state = READ_METHOD;
|
|
|
|
|
|
|
|
|
|
|
|
// while getting a char from stream
|
|
|
|
// while getting a char from stream
|
|
|
|
while (::read(_socket, &c, 1)) {
|
|
|
|
while (_connection.read(&c, 1)) {
|
|
|
|
switch (state) {
|
|
|
|
switch (state) {
|
|
|
|
case READ_METHOD:
|
|
|
|
case READ_METHOD:
|
|
|
|
if (is_space(c)) {
|
|
|
|
if (is_space(c)) {
|
|
|
|
@ -626,7 +690,7 @@ Request::readBody(std::string &body) const {
|
|
|
|
size_t N = contentLength(); body.reserve(N);
|
|
|
|
size_t N = contentLength(); body.reserve(N);
|
|
|
|
char buffer[65536];
|
|
|
|
char buffer[65536];
|
|
|
|
while (N>0) {
|
|
|
|
while (N>0) {
|
|
|
|
int res = ::read(_socket, buffer, std::min(N, size_t(65536)));
|
|
|
|
int res = _connection.read(buffer, std::min(N, size_t(65536)));
|
|
|
|
if (res>=0) { body.append(buffer, size_t(res)); N -= res; }
|
|
|
|
if (res>=0) { body.append(buffer, size_t(res)); N -= res; }
|
|
|
|
else { return false; }
|
|
|
|
else { return false; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -637,8 +701,8 @@ Request::readBody(std::string &body) const {
|
|
|
|
/* ********************************************************************************************* *
|
|
|
|
/* ********************************************************************************************* *
|
|
|
|
* Implementation of HTTPD::Response
|
|
|
|
* Implementation of HTTPD::Response
|
|
|
|
* ********************************************************************************************* */
|
|
|
|
* ********************************************************************************************* */
|
|
|
|
Response::Response(int socket)
|
|
|
|
Response::Response(const Connection &connection)
|
|
|
|
: _socket(socket), _status(STATUS_SERVER_ERROR), _close_connection(false)
|
|
|
|
: _connection(connection), _status(STATUS_SERVER_ERROR), _close_connection(false)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// pass...
|
|
|
|
// pass...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -670,18 +734,6 @@ Response::setContentLength(size_t length) {
|
|
|
|
setHeader("Content-Length", buffer.str());
|
|
|
|
setHeader("Content-Length", buffer.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
|
|
Response::send(const std::string &data) const {
|
|
|
|
|
|
|
|
const char *ptr = data.c_str();
|
|
|
|
|
|
|
|
size_t count = data.size();
|
|
|
|
|
|
|
|
while (count) {
|
|
|
|
|
|
|
|
int c = ::write(_socket, ptr, count);
|
|
|
|
|
|
|
|
if (c < 0) { return false; }
|
|
|
|
|
|
|
|
count -= c; ptr += c;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
bool
|
|
|
|
Response::sendHeaders() const {
|
|
|
|
Response::sendHeaders() const {
|
|
|
|
std::stringstream buffer;
|
|
|
|
std::stringstream buffer;
|
|
|
|
@ -699,7 +751,7 @@ Response::sendHeaders() const {
|
|
|
|
buffer << item->first << ": " << item->second << "\r\n";
|
|
|
|
buffer << item->first << ": " << item->second << "\r\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buffer << "\r\n";
|
|
|
|
buffer << "\r\n";
|
|
|
|
return send(buffer.str());
|
|
|
|
return _connection.send(buffer.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -742,7 +794,7 @@ StaticHandler::handle(const Request &request, Response &response) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
response.setContentLength(_text.size());
|
|
|
|
response.setContentLength(_text.size());
|
|
|
|
response.sendHeaders();
|
|
|
|
response.sendHeaders();
|
|
|
|
response.send(_text);
|
|
|
|
response.connection().send(_text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -785,7 +837,7 @@ JSONHandler::handle(const http::Request &request, http::Response &response) {
|
|
|
|
result.serialize(result_string);
|
|
|
|
result.serialize(result_string);
|
|
|
|
response.setContentLength(result_string.size());
|
|
|
|
response.setContentLength(result_string.size());
|
|
|
|
response.sendHeaders();
|
|
|
|
response.sendHeaders();
|
|
|
|
response.send(result_string);
|
|
|
|
response.connection().send(result_string);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
response.setStatus(http::Response::STATUS_BAD_REQUEST);
|
|
|
|
response.setStatus(http::Response::STATUS_BAD_REQUEST);
|
|
|
|
response.setContentLength(0);
|
|
|
|
response.setContentLength(0);
|
|
|
|
|