Rewrite middleware to support epoll

This commit is contained in:
2019-06-29 12:46:29 +02:00
parent 72a6a745ff
commit 4d90d18660
27 changed files with 670 additions and 318 deletions

View File

@@ -1,44 +1,47 @@
#include "configuration.hpp"
int ServerConfiguration::GetMajorVersion() const
namespace Server
{
return 0;
}
int Configuration::GetMajorVersion() const
{
return 0;
}
int ServerConfiguration::GetMinorVersion() const
{
return 1;
}
int Configuration::GetMinorVersion() const
{
return 1;
}
std::string const & ServerConfiguration::GetWwwRoot() const
{
return wwwRoot;
}
std::string const & Configuration::GetWwwRoot() const
{
return wwwRoot;
}
std::string const & ServerConfiguration::GetServerName() const
{
return serverName;
}
std::string const & Configuration::GetServerName() const
{
return serverName;
}
int ServerConfiguration::GetPort() const
{
return port;
}
int Configuration::GetPort() const
{
return port;
}
bool ServerConfiguration::IsValid() const
{
return isValid;
}
bool Configuration::IsValid() const
{
return isValid;
}
bool ServerConfiguration::LoadFromFile(std::string const & filePath)
{
// TODO implement
return false;
}
bool Configuration::LoadFromFile(std::string const & filePath)
{
// TODO implement
return false;
}
ServerConfiguration::ServerConfiguration()
: wwwRoot("./www"),
serverName("http-server"),
port(8080)
{
Configuration::Configuration()
: wwwRoot("./www"),
serverName("http-server"),
port(8080)
{
}
}

View File

@@ -1,27 +1,30 @@
#pragma once
#include <string>
class ServerConfiguration
namespace Server
{
private:
std::string wwwRoot;
std::string serverName;
int port;
bool isValid;
class Configuration
{
private:
std::string wwwRoot;
std::string serverName;
int port;
bool isValid;
public:
int GetMajorVersion() const;
int GetMinorVersion() const;
std::string const & GetWwwRoot() const;
std::string const & GetServerName() const;
int GetPort() const;
bool IsValid() const;
public:
int GetMajorVersion() const;
int GetMinorVersion() const;
std::string const & GetWwwRoot() const;
std::string const & GetServerName() const;
int GetPort() const;
bool IsValid() const;
bool LoadFromFile(std::string const & filePath);
bool LoadFromFile(std::string const & filePath);
ServerConfiguration();
~ServerConfiguration() = default;
Configuration();
~Configuration() = default;
ServerConfiguration(ServerConfiguration & other) = delete;
ServerConfiguration(ServerConfiguration && other) = delete;
};
Configuration(Configuration & other) = delete;
Configuration(Configuration && other) = delete;
};
}

View File

@@ -1,66 +0,0 @@
#include "../middleware/notfound.hpp"
#include "../middleware/staticcontent.hpp"
#include "../logger.hpp"
#include "configuration.hpp"
#include "connectionoperator.hpp"
#include <cstdio>
#include <sstream>
std::vector<char> ConnectionOperator::HandleNewConnection(int fd)
{
auto requestBytes = Socket::ReadBytes(fd, 512);
Http::Request request;
Http::Response response;
try
{
request = Http::Request::Deserialize(requestBytes);
}
catch (std::runtime_error & e)
{
std::stringstream ss;
ss << "Error during parsing of request <";
ss << e.what();
ss << '>';
logger.Error(ss.str());
response.code = HttpResponse::Code::BAD_REQUEST;
return response.Serialize();
}
for(size_t i = 0; i < middlewares.size(); ++i)
{
middlewares[i]->HandleRequest(request, response);
}
if (response.code == HttpResponse::Code::UNKNOWN)
{
std::stringstream ss;
ss << "Unhandled ";
ss << HttpRequest::typeStrings[static_cast<int>(request.type)];
ss << " request for file <";
ss << request.url.GetPath();
ss << '>';
logger.Error(ss.str());
response.code = HttpResponse::Code::NOT_IMPLEMENTED;
return response.Serialize();
}
return response.Serialize();
}
ConnectionOperator::ConnectionOperator(Logger & _logger, ServerConfiguration const & serverConfiguration)
: logger(_logger)
{
// Base static file server
auto const & staticFileRoot = serverConfiguration.GetWwwRoot();
if (staticFileRoot.size() > 0)
{
middlewares.emplace_back(std::make_unique<Middleware::StaticContent>(_logger, staticFileRoot));
}
// ALWAYS LAST!
middlewares.emplace_back(std::make_unique<Middleware::NotFound>(_logger));
}

View File

@@ -1,19 +0,0 @@
#pragma once
#include "../logger.hpp"
#include "../middleware/base.hpp"
#include <memory>
#include "socket.hpp"
#include <string>
#include <vector>
class ConnectionOperator
{
private:
Logger & logger;
std::vector<std::unique_ptr<Middleware::BaseMiddleware>> middlewares;
public:
std::vector<char> HandleNewConnection(int fd);
ConnectionOperator(Logger & logger, ServerConfiguration const & serverConfiguration);
};

12
src/server/eventtype.hpp Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
namespace Server
{
enum class EventType
{
Unknown = -1,
NewConnection = 0,
ReadRequest = 1,
WriteResponse
};
}

View File

@@ -2,119 +2,163 @@
#include "configuration.hpp"
#include "server.hpp"
#include <set>
#include "socket.hpp"
#include <sstream>
#include <stdexcept>
#include <sys/epoll.h>
#include <unistd.h>
void HttpServer::HandleEpollInEvent(int fd)
namespace Server
{
if (fd != listeningSocketFileDescriptor)
void HttpServer::RegisterNewConnection(int const listeningSocketFd)
{
logger.Info("EPOLLIN Attempted to handle a non registered file descriptor");
return;
}
unsigned sockaddrSize = sizeof(sockaddr_in);
int const connectionFileDescriptor = accept(
listeningSocketFileDescriptor,
reinterpret_cast<sockaddr *>(&socketAddress),
&sockaddrSize);
socketWriteMap[connectionFileDescriptor] = connectionOperator.HandleNewConnection(connectionFileDescriptor);
epoll_event event;
event.data.fd = connectionFileDescriptor;
event.events = EPOLLOUT | EPOLLONESHOT;
if (epoll_ctl(pollFileDescriptor, EPOLL_CTL_ADD, connectionFileDescriptor, &event) < 0)
{
logger.Error("Error registering file descriptor for EPOLLOUT");
}
}
void HttpServer::HandleEpollOutEvent(int fd)
{
auto result = socketWriteMap.find(fd);
if (result == socketWriteMap.end())
{
logger.Error("EPOLLOUT Event for unexpected fd");
return;
}
Socket::WriteBytes(fd, result->second);
close(fd);
socketWriteMap.erase(result);
}
void HttpServer::Execute()
{
std::vector<epoll_event> epollEvents;
epollEvents.resize(1000);
while(isOpen)
{
int eventsHappened = epoll_wait(pollFileDescriptor, epollEvents.data(), static_cast<int>(epollEvents.size()), -1);
for (int i = 0; i < eventsHappened; ++i)
unsigned sockaddrSize = sizeof(sockaddr_in);
int const clientFd = accept(
listeningSocketFd,
reinterpret_cast<sockaddr *>(&socketAddress),
&sockaddrSize);
if (clientFd < 0)
{
int const fd = epollEvents[i].data.fd;
if (epollEvents[i].events & EPOLLIN)
logger.Info("Connection dropped on accepting it");
return;
}
scheduleHelper.ScheduleReadFileDescriptor(clientFd);
fileDescriptorEventTypeMap[clientFd] = EventType::ReadRequest;
}
void HttpServer::ReadRequest(int const socketFd)
{
auto bytes = Socket::ReadBytes(socketFd, 512);
if (bytes.size() < 1)
{
logger.Info("Could not read anything from socket");
return;
}
auto & request = socketRequestMap[socketFd];
request.Deserialize(bytes, logger);
auto & response = socketResponseMap[socketFd];
auto middlewareResult = middleware.Start(socketFd, scheduleHelper, request, response);
HandleMiddlewareResponse(middlewareResult);
}
void HttpServer::HandleMiddlewareEvent(int const eventFd)
{
auto middlewareResult = middleware.Continue(eventFd, scheduleHelper);
HandleMiddlewareResponse(middlewareResult);
}
void HttpServer::HandleMiddlewareResponse(Middleware::MiddlewareResult const & middlewareResult)
{
if (middlewareResult.writeResult)
{
fileDescriptorEventTypeMap[middlewareResult.socketFd] = EventType::WriteResponse;
scheduleHelper.ScheduleWriteFileDescriptor(middlewareResult.socketFd, true, true);
}
}
void HttpServer::WriteResponse(int const socketFd)
{
auto requestResult = socketRequestMap.find(socketFd);
if (requestResult != socketRequestMap.end())
{
socketRequestMap.erase(requestResult);
}
auto responseResult = socketResponseMap.find(socketFd);
Socket::WriteBytes(socketFd, responseResult->second.Serialize());
if (responseResult != socketResponseMap.end())
{
socketResponseMap.erase(responseResult);
}
}
void HttpServer::Execute()
{
while(isOpen)
{
auto events = scheduler.WaitForEvents();
for (size_t i = 0; i < events.size(); ++i)
{
HandleEpollInEvent(fd);
}
else
{
HandleEpollOutEvent(fd);
int const fd = events[i];
auto eventTypeMapIter = fileDescriptorEventTypeMap.find(fd);
if (eventTypeMapIter == fileDescriptorEventTypeMap.end())
{
HandleMiddlewareEvent(fd);
continue;
}
if (eventTypeMapIter->second != EventType::NewConnection)
{
fileDescriptorEventTypeMap.erase(eventTypeMapIter);
}
switch(eventTypeMapIter->second)
{
case EventType::NewConnection:
RegisterNewConnection(fd);
break;
case EventType::ReadRequest:
ReadRequest(fd);
break;
case EventType::WriteResponse:
WriteResponse(fd);
close(fd);
break;
case EventType::Unknown:
default:
close(fd);
logger.Error("FD with unknown EventType fired and was closed");
break;
}
}
}
}
HttpServer::HttpServer(Logger & _logger, Configuration const & serverConfiguration)
: logger(_logger),
scheduler(_logger),
scheduleHelper(scheduler),
listeningSocketFileDescriptor(-1),
middleware(Middleware::Middleware::CreateWithStaticFile(_logger, serverConfiguration.GetWwwRoot())),
isOpen(true)
{
socketAddress.sin_family = AF_INET;
socketAddress.sin_addr.s_addr = INADDR_ANY;
socketAddress.sin_port = htons(serverConfiguration.GetPort());
listeningSocketFileDescriptor = ListeningSocket::Create(socketAddress, 1000);
if (listeningSocketFileDescriptor < 0)
{
throw std::runtime_error("Error creating listening socket");
}
scheduleHelper.ScheduleReadFileDescriptor(listeningSocketFileDescriptor, false);
fileDescriptorEventTypeMap[listeningSocketFileDescriptor] = EventType::NewConnection;
std::stringstream ss;
ss << "Listening on port " << serverConfiguration.GetPort();
logger.Info(ss.str());
}
HttpServer::~HttpServer()
{
if (listeningSocketFileDescriptor >= 0)
{
close(listeningSocketFileDescriptor);
}
for (auto & item : fileDescriptorEventTypeMap)
{
close(item.first);
}
}
}
HttpServer::HttpServer(Logger & _logger, ServerConfiguration const & serverConfiguration)
: logger(_logger),
pollFileDescriptor(-1),
listeningSocketFileDescriptor(-1),
connectionOperator(_logger, serverConfiguration),
isOpen(true)
{
pollFileDescriptor = epoll_create1(0);
if (pollFileDescriptor < 0)
{
throw std::runtime_error("Error creating epoll file descriptor");
}
socketAddress.sin_family = AF_INET;
socketAddress.sin_addr.s_addr = INADDR_ANY;
socketAddress.sin_port = htons(serverConfiguration.GetPort());
listeningSocketFileDescriptor = ListeningSocket::Create(socketAddress, 10000);
if (listeningSocketFileDescriptor < 0)
{
throw std::runtime_error("Error creating listening socket");
}
epoll_event event;
event.data.fd = listeningSocketFileDescriptor;
event.events = EPOLLIN;
if (epoll_ctl(pollFileDescriptor, EPOLL_CTL_ADD, listeningSocketFileDescriptor, &event) < 0)
{
throw std::runtime_error("Error registering listening socket with epoll facilities");
}
std::stringstream ss;
ss << "Listening on port " << serverConfiguration.GetPort();
logger.Info(ss.str());
}
HttpServer::~HttpServer()
{
if (listeningSocketFileDescriptor >= 0)
{
close(listeningSocketFileDescriptor);
}
if (pollFileDescriptor >= 0)
{
close(pollFileDescriptor);
}
}

View File

@@ -1,28 +1,47 @@
#pragma once
#include "../http/request.hpp"
#include "../http/response.hpp"
#include "../logger.hpp"
#include "connectionoperator.hpp"
#include "../middleware/middleware.hpp"
#include "../scheduler.hpp"
#include "eventtype.hpp"
#include <map>
#include <netinet/in.h>
class HttpServer
namespace Server
{
private:
Logger & logger;
int pollFileDescriptor;
sockaddr_in socketAddress;
int listeningSocketFileDescriptor;
std::map<int, std::vector<char>> socketWriteMap;
ConnectionOperator connectionOperator;
bool isOpen;
class HttpServer
{
private:
Logger & logger;
Scheduler scheduler;
ScheduleHelper scheduleHelper;
void HandleEpollInEvent(int fd);
sockaddr_in socketAddress;
int listeningSocketFileDescriptor;
void HandleEpollOutEvent(int fd);
std::map<int, EventType> fileDescriptorEventTypeMap;
std::map<int, Http::Request> socketRequestMap;
std::map<int, Http::Response> socketResponseMap;
public:
void Execute();
HttpServer(Logger & logger, ServerConfiguration const & serverConfiguration);
~HttpServer();
HttpServer(HttpServer & other) = delete;
HttpServer & operator=(HttpServer & other) = delete;
};
Middleware::Middleware middleware;
bool isOpen;
void RegisterNewConnection(int const listeningSocketFd);
void ReadRequest(int const socketFd);
void HandleMiddlewareEvent(int const eventFd);
void HandleMiddlewareResponse(Middleware::MiddlewareResult const & middlewareResult);
void WriteResponse(int const socketFd);
public:
void Execute();
HttpServer(Logger & logger, Configuration const & serverConfiguration);
~HttpServer();
};
}

View File

@@ -2,7 +2,7 @@
#include <stdexcept>
#include <unistd.h>
namespace ListeningSocket
namespace Server::ListeningSocket
{
int Create(sockaddr_in & socketAddress, int const connectionLimit)
{
@@ -40,7 +40,7 @@ namespace ListeningSocket
}
}
namespace Socket
namespace Server::Socket
{
std::vector<char> ReadBytes(int fd, size_t limit)
{

View File

@@ -3,12 +3,12 @@
#include <sys/socket.h>
#include <vector>
namespace ListeningSocket
namespace Server::ListeningSocket
{
int Create(sockaddr_in & socketAddress, int const connectionLimit);
}
namespace Socket
namespace Server::Socket
{
std::vector<char> ReadBytes(int fd, size_t limit);