Initial commit
This commit is contained in:
51
src/server/configuration.cpp
Normal file
51
src/server/configuration.cpp
Normal 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;
|
||||
}
|
||||
29
src/server/configuration.hpp
Normal file
29
src/server/configuration.hpp
Normal 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
68
src/server/connection.cpp
Normal 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
23
src/server/connection.hpp
Normal 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;
|
||||
};
|
||||
45
src/server/connectionoperator.cpp
Normal file
45
src/server/connectionoperator.cpp
Normal 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>());
|
||||
}
|
||||
17
src/server/connectionoperator.hpp
Normal file
17
src/server/connectionoperator.hpp
Normal 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();
|
||||
};
|
||||
54
src/server/listeningsocket.cpp
Normal file
54
src/server/listeningsocket.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
20
src/server/listeningsocket.hpp
Normal file
20
src/server/listeningsocket.hpp
Normal 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
31
src/server/server.cpp
Executable 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
18
src/server/server.hpp
Executable 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
33
src/server/url.cpp
Normal 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
19
src/server/url.hpp
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user