Initial commit

This commit is contained in:
2019-06-15 12:00:21 +02:00
commit eda5d9df6b
31 changed files with 1328 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
#include "configuration.hpp"
bool ServerConfiguration::LoadFromFile(std::string const & filePath)
{
// TODO implement
return false;
}
ServerConfiguration::ServerConfiguration()
: wwwRoot("/home/tijmen/project/http-server/bin/www"),
serverName("http-server"),
port(8080)
{
}
int ServerConfiguration::GetMajorVersion() const
{
return 0;
}
int ServerConfiguration::GetMinorVersion() const
{
return 1;
}
std::string const & ServerConfiguration::GetWwwRoot() const
{
return wwwRoot;
}
std::string const & ServerConfiguration::GetServerName() const
{
return serverName;
}
int ServerConfiguration::GetPort() const
{
return port;
}
bool ServerConfiguration::IsValid() const
{
return isValid;
}
ServerConfiguration const & ServerConfiguration::GetInstance()
{
static ServerConfiguration config;
return config;
}

View File

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

68
src/server/connection.cpp Normal file
View File

@@ -0,0 +1,68 @@
#include "connection.hpp"
#include <stdexcept>
#include <unistd.h>
std::vector<char> Connection::ReadBytes(size_t limit) const
{
size_t const readChunkSize = 128;
std::vector<char> buffer;
ssize_t totalBytesRead = 0;
ssize_t bytesRead = 0;
do
{
buffer.resize(buffer.size() + readChunkSize);
bytesRead = read(fileDescriptor, &buffer[totalBytesRead], readChunkSize);
if (bytesRead < 0)
{
throw std::runtime_error("Error reading from filedescriptor");
}
totalBytesRead += bytesRead;
} while (bytesRead == readChunkSize && bytesRead < limit);
buffer.resize(totalBytesRead);
return buffer;
}
size_t Connection::WriteBytes(std::vector<char> const & bytes) const
{
ssize_t totalBytesWritten = 0;
size_t const sizeToWrite = bytes.size();
while (totalBytesWritten < sizeToWrite)
{
ssize_t bytesWritten = write(fileDescriptor, &bytes[totalBytesWritten], sizeToWrite - totalBytesWritten);
if (bytesWritten <= 0)
{
throw std::runtime_error("Error writing to filedescriptor");
}
totalBytesWritten += bytesWritten;
}
return totalBytesWritten;
}
Connection::Connection(int _fileDescriptor)
: fileDescriptor(_fileDescriptor)
{
if (_fileDescriptor < 0)
{
throw std::runtime_error("connection created with invalid file descriptor");
}
}
Connection::~Connection()
{
if (fileDescriptor >= 0)
{
close(fileDescriptor);
}
}
Connection::Connection(Connection && other)
{
fileDescriptor = other.fileDescriptor;
other.fileDescriptor = -1;
}

23
src/server/connection.hpp Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include <cstdint>
#include <vector>
class Connection
{
private:
int fileDescriptor;
public:
// Parameter limit is a multiple of 128
std::vector<char> ReadBytes(size_t limit = 512) const;
size_t WriteBytes(std::vector<char> const & bytes) const;
Connection(int _fileDescriptor);
~Connection();
Connection(Connection && other);
Connection(Connection & other) = delete;
Connection & operator=(Connection & other) = delete;
};

View File

@@ -0,0 +1,45 @@
#include "../middleware/notfound.hpp"
#include "../middleware/staticcontent.hpp"
#include "../logger.hpp"
#include "configuration.hpp"
#include "connectionoperator.hpp"
#include <cstdio>
#include <sstream>
void ConnectionOperator::HandleNewConnection(Connection const & newConnection)
{
auto requestBytes = newConnection.ReadBytes();
Http::Request request = Http::Request::Deserialize(requestBytes);
Http::Response response;
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 request for file <";
ss << request.path;
ss << '>';
Logger::GetInstance().Error(ss.str());
return;
}
auto bytesToSend = response.Serialize();
newConnection.WriteBytes(bytesToSend);
}
ConnectionOperator::ConnectionOperator()
{
// Base static file server
auto const & staticFileRoot = ServerConfiguration::GetInstance().GetWwwRoot();
if (staticFileRoot.size() > 0)
{
middlewares.emplace_back(std::make_unique<Middleware::StaticContent>(staticFileRoot));
}
// ALWAYS LAST!
middlewares.emplace_back(std::make_unique<Middleware::NotFound>());
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "../middleware/base.hpp"
#include "connection.hpp"
#include <memory>
#include <string>
#include <vector>
class ConnectionOperator
{
private:
std::vector<std::unique_ptr<Middleware::BaseMiddleware>> middlewares;
public:
void HandleNewConnection(Connection const & newConnection);
ConnectionOperator();
};

View File

@@ -0,0 +1,54 @@
#include "listeningsocket.hpp"
#include <stdexcept>
#include <unistd.h>
int const connectionLimit = 10;
Connection ListeningSocket::AcceptNextConnection()
{
unsigned sockaddrSize = sizeof(sockaddr_in);
int connectionFileDescriptor = accept(
socketFileDescriptor,
reinterpret_cast<sockaddr *>(&socketAddress),
&sockaddrSize);
return Connection(connectionFileDescriptor);
}
ListeningSocket::ListeningSocket(int const port)
: socketFileDescriptor(-1),
socketAddress()
{
socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if (socketFileDescriptor < 0)
{
throw std::runtime_error("socket creation error");
}
socketAddress.sin_family = AF_INET;
socketAddress.sin_addr.s_addr = INADDR_ANY;
socketAddress.sin_port = htons(port);
int const bindResult = bind(
socketFileDescriptor,
reinterpret_cast<sockaddr*>(&socketAddress),
sizeof(sockaddr_in));
if (bindResult < 0)
{
throw std::runtime_error("socket bind error");
}
int const listenResult = listen(socketFileDescriptor, connectionLimit);
if (listenResult < 0)
{
throw std::runtime_error("socket listening error");
}
}
ListeningSocket::~ListeningSocket()
{
if (socketFileDescriptor >= 0)
{
close(socketFileDescriptor);
}
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "connection.hpp"
#include <sys/socket.h>
#include <netinet/in.h>
class ListeningSocket
{
private:
int socketFileDescriptor;
sockaddr_in socketAddress;
public:
Connection AcceptNextConnection();
ListeningSocket(int const port);
~ListeningSocket();
ListeningSocket(ListeningSocket & other) = delete;
ListeningSocket & operator=(ListeningSocket & other) = delete;
};

31
src/server/server.cpp Executable file
View File

@@ -0,0 +1,31 @@
#include "../logger.hpp"
#include "configuration.hpp"
#include "server.hpp"
#include <sstream>
#include <stdexcept>
void HttpServer::Execute()
{
while(isOpen)
{
try
{
Connection newConnection = listeningSocket.AcceptNextConnection();
connectionOperator.HandleNewConnection(newConnection);
}
catch (std::runtime_error & e)
{
Logger::GetInstance().Info("Connection dropped on accept");
}
}
}
HttpServer::HttpServer()
: listeningSocket(ServerConfiguration::GetInstance().GetPort()),
connectionOperator(),
isOpen(true)
{
std::stringstream ss;
ss << "Listening on port " << ServerConfiguration::GetInstance().GetPort();
Logger::GetInstance().Info(ss.str());
}

18
src/server/server.hpp Executable file
View File

@@ -0,0 +1,18 @@
#pragma once
#include "connectionoperator.hpp"
#include "listeningsocket.hpp"
class HttpServer
{
private:
ListeningSocket listeningSocket;
ConnectionOperator connectionOperator;
bool isOpen;
public:
void Execute();
HttpServer();
HttpServer(HttpServer & other) = delete;
HttpServer & operator=(HttpServer & other) = delete;
};

33
src/server/url.cpp Normal file
View File

@@ -0,0 +1,33 @@
#include "url.hpp"
bool Url::HasPath() const
{
return path.size() > 1;
}
bool Url::HasQuery() const
{
// TODO implement
return false;
}
bool Url::HasFragment() const
{
// TODO implement
return false;
}
std::string const & Url::GetPath() const
{
return path;
}
std::string const & Url::GetQuery() const
{
return query;
}
std::string const & Url::GetFragment() const
{
return fragment;
}

19
src/server/url.hpp Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include <string>
class Url
{
private:
std::string path;
std::string query;
std::string fragment;
public:
bool HasPath() const;
bool HasQuery() const;
bool HasFragment() const;
std::string const & GetPath() const;
std::string const & GetQuery() const;
std::string const & GetFragment() const;
};