Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ctest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:
shell: bash

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Configure CMake
run: CXX=clang++-19 CC=clang-19 cmake -S ${{github.workspace}}/ -B ${{runner.workspace}}/build -DCLANG_TIDY_NAME=clang-tidy-19
run: CXX=clang++-22 CC=clang-22 cmake -S ${{github.workspace}}/ -B ${{runner.workspace}}/build -DCLANG_TIDY_NAME=clang-tidy-22

- name: Build
working-directory: ${{runner.workspace}}/build
Expand All @@ -40,10 +40,10 @@ jobs:
shell: pwsh

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Configure CMake
run: cmake -S ${{github.workspace}}\ -B ${{runner.workspace}}\build -G "Ninja"
run: cmake -S ${{github.workspace}}\ -B ${{runner.workspace}}\build -G "MinGW Makefiles"

- name: Build
working-directory: ${{runner.workspace}}\build
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.vscode/
**/.DS_Store
**/*build*/
Testing/
12 changes: 6 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.25)
project(simple-http-server)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 23)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

Expand All @@ -19,28 +19,28 @@ if(CMAKE_BUILD_TYPE AND NOT CMAKE_BUILD_TYPE IN_LIST CMAKE_CONFIGURATION_TYPES)
endif()

if(CMAKE_C_COMPILER_ID STREQUAL GNU OR CMAKE_C_COMPILER_ID MATCHES Clang)
set(CMAKE_C_FLAGS "-std=20 -pedantic -Wall -Werror -ggdb")
set(CMAKE_C_FLAGS "-std=23 -pedantic -Wall -Werror -ggdb")
set(CMAKE_C_FLAGS_ASAN "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address -fno-optimize-sibling-calls -fno-omit-frame-pointer")
set(CMAKE_C_FLAGS_LSAN "${CMAKE_C_FLAGS_DEBUG} -fsanitize=leak")
set(CMAKE_C_FLAGS_MSAN "${CMAKE_C_FLAGS_DEBUG} -fsanitize=memory -fno-optimize-sibling-calls -fno-omit-frame-pointer")
set(CMAKE_C_FLAGS_UBSAN "${CMAKE_C_FLAGS_DEBUG} -fsanitize=undefined")
set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_DEBUG} --coverage")
elseif(MSVC)
set(CMAKE_C_FLAGS "/std:c20 /W4 /WX")
set(CMAKE_C_FLAGS "/std:c23 /W4 /WX")
set(CMAKE_C_FLAGS_ASAN "${CMAKE_C_FLAGS_DEBUG} /fsanitize=address")
set(CMAKE_EXE_LINKER_FLAGS_ASAN "/debug /INCREMENTAL:NO")
endif()


if(CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
set(CMAKE_CXX_FLAGS "-std=c++20 -pedantic -Wall -Werror -ggdb")
set(CMAKE_CXX_FLAGS "-std=c++23 -pedantic -Wall -Werror -ggdb")
set(CMAKE_CXX_FLAGS_ASAN "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fno-optimize-sibling-calls -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS_LSAN "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=leak")
set(CMAKE_CXX_FLAGS_MSAN "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=memory -fno-optimize-sibling-calls -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS_UBSAN "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_DEBUG} --coverage")
elseif(MSVC)
set(CMAKE_CXX_FLAGS "/std:c++20 /W4 /WX")
set(CMAKE_CXX_FLAGS "/std:c++23 /W4 /WX")
set(CMAKE_CXX_FLAGS_ASAN "${CMAKE_CXX_FLAGS_DEBUG} /fsanitize=address")
set(CMAKE_EXE_LINKER_FLAGS_ASAN "/debug /INCREMENTAL:NO")
endif()
Expand Down Expand Up @@ -101,7 +101,7 @@ if(BUILD_TESTING)
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/d144031940543e15423a25ae5a8a74141044862f.zip
URL https://github.com/google/googletest/archive/d72f9c8aea6817cdf1ca0ac10887f328de7f3da2.zip
DOWNLOAD_EXTRACT_TIMESTAMP NEW
)

Expand Down
2 changes: 1 addition & 1 deletion libs/includes/DefineSystem.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#if defined __has_include
#ifdef __has_include
#if __has_include(<winsock2.h>)
#define WINDOWS
#elif __has_include(<unistd.h>)
Expand Down
6 changes: 4 additions & 2 deletions libs/includes/Directory.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace simple_http_server {

class Directory {
public:
enum AllowType : std::uint8_t { BLACKLIST, WHITELIST };
enum class AllowType : std::uint8_t { BLACKLIST, WHITELIST };

private:
std::filesystem::path path_;
Expand All @@ -20,12 +20,14 @@ class Directory {
AllowType type_;

std::vector<std::regex> allow_set_;

// NOLINTNEXTLINE(bugprone-throwing-static-initialization)
inline static const std::vector<std::regex> forced_blacklist_ = {
std::regex("^.*\\.\\..*$")}; // to block path traversal

public:
explicit Directory(std::filesystem::path path, HeadersMap headers = {},
AllowType type = BLACKLIST,
AllowType type = AllowType::BLACKLIST,
std::vector<std::regex> allow_set = {})
: path_(std::move(path)),
headers_(std::move(headers)),
Expand Down
2 changes: 2 additions & 0 deletions libs/includes/Logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace simple_http_server {

// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define X(level, name, i) level = (i),
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum Level : std::uint8_t { LOGGER_LEVELS };
#undef X

Expand All @@ -32,6 +33,7 @@ class Logger {
public:
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define X(level, name, _) name,
// NOLINTNEXTLINE(bugprone-throwing-static-initialization)
inline static const std::vector<char const*> level_name = {LOGGER_LEVELS};
#undef X
private:
Expand Down
4 changes: 2 additions & 2 deletions libs/includes/Request.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace simple_http_server {

class Request {
public:
enum Type : std::uint8_t {
enum class Type : std::uint8_t {
GET,
POST,
// add here new types such as DELETE and etc
Expand All @@ -24,7 +24,7 @@ class Request {
static auto ParseArguments(const std::string& url_with_args) -> ArgumentsMap;
static auto DecodeURL(std::string& url_with_args);

Type type_ = UNKNOWN;
Type type_ = Type::UNKNOWN;
std::string url_;
ArgumentsMap arguments_;
std::string httpVersion_;
Expand Down
20 changes: 15 additions & 5 deletions libs/includes/Response.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Response {

[[nodiscard]] auto Empty() const -> bool { return (statusCode_ == 0); }

// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum HttpStatusCodes : std::uint16_t {
OK = 200,
FORBIDDEN = 403,
Expand All @@ -33,11 +34,20 @@ class Response {
HeadersMap headers_;
std::string body_;

inline static const std::unordered_map<int, std::string> defaultMessages_{
{OK, "OK"},
{NOT_FOUND, "Not Found"},
{FORBIDDEN, "Forbidden"},
{INTERNAL_ERROR, "Internal Server Error"}};
static constexpr auto defaultMessage(int code) noexcept -> std::string_view {
switch (code) {
case HttpStatusCodes::OK:
return "OK";
case HttpStatusCodes::NOT_FOUND:
return "Not Found";
case HttpStatusCodes::FORBIDDEN:
return "Forbidden";
case HttpStatusCodes::INTERNAL_ERROR:
return "Internal Server Error";
default:
return "Unknown";
}
}
};

} // namespace simple_http_server
Expand Down
1 change: 1 addition & 0 deletions libs/includes/Socket/WindowsSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <ws2tcpip.h>

#include <memory>
#include <mutex>
#include <string>
#include <vector>

Expand Down
4 changes: 3 additions & 1 deletion libs/sources/Logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

namespace simple_http_server {

// NOLINTNEXTLINE(bugprone-throwing-static-initialization)
std::filesystem::path Logger::logFilename_ = "./server.log";
// NOLINTNEXTLINE(bugprone-throwing-static-initialization)
std::ofstream Logger::logStream_;
Level Logger::logLevel_ = WARNING;
bool Logger::isInit_ = false;
Expand Down Expand Up @@ -44,7 +46,7 @@ void Logger::Log(Level level, std::ostringstream&& message) {
const time_t now =
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
Logger::logStream_ << std::put_time(std::localtime(&now), "%Y-%m-%d@%H:%M:%S")
<< " " << level_name[level] << " "
<< " " << level_name.at(level) << " "
<< std::move(message).str() << "\n";
Flush();
// TODO(dzen) think about flush(); probably should use Flush() each N lines
Expand Down
9 changes: 5 additions & 4 deletions libs/sources/Request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ auto Request::ParseArguments(const std::string& url_with_args) -> ArgumentsMap {

auto next_arg = args_start;

auto parse_argument = [&ret, &url_with_args](auto current_arg) mutable {
auto parse_argument = [&ret,
&url_with_args](auto current_arg) mutable -> auto {
auto sep = url_with_args.find('=', current_arg);
auto next_arg = url_with_args.find('&', current_arg + 1);

Expand Down Expand Up @@ -55,11 +56,11 @@ auto Request::DecodeURL(std::string& url_with_args) {
res.reserve(url_with_args.length());

for (std::string::size_type it = 0; it < url_with_args.length(); ++it) {
if (url_with_args[it] == '%' && it < url_with_args.length() - 3) {
res += HexToDec({url_with_args[it + 1], url_with_args[it + 2]});
if (url_with_args.at(it) == '%' && it < url_with_args.length() - 3) {
res += HexToDec({url_with_args.at(it + 1), url_with_args.at(it + 2)});
it += 2;
} else {
res += url_with_args[it];
res += url_with_args.at(it);
}
}

Expand Down
9 changes: 2 additions & 7 deletions libs/sources/Response.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "Response.h"

#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>

Expand All @@ -14,12 +13,8 @@ Response::Response(int statusCode, std::string body, HeadersMap headers,
: statusCode_(statusCode),
headers_(std::move(headers)),
body_(std::move(body)) {
if (statusMessage.empty() && !defaultMessages_.contains(statusCode)) {
throw std::runtime_error("no message for error code provided");
}

statusMessage_ = (statusMessage.empty() ? defaultMessages_.at(statusCode_)
: statusMessage);
statusMessage_ =
(statusMessage.empty() ? defaultMessage(statusCode_) : statusMessage);
if (!headers_.contains("Content-Length")) {
headers_.emplace("Content-Length", std::to_string(body_.length()));
}
Expand Down
14 changes: 8 additions & 6 deletions libs/sources/Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ void Server::Start() {
try {
const auto& client_sock = socket_->Accept();

threadPool.enqueue([this, client_sock]() { HandleClient(client_sock); });
threadPool.enqueue(
[this, client_sock]() -> auto { HandleClient(client_sock); });

} catch (const std::exception& exception) {
LOG(ERROR, exception.what());
Expand Down Expand Up @@ -73,18 +74,19 @@ auto Server::TryRenderFile(const Request& request) -> Response {

path /= request.GetUrl().substr(prev_pos + 1);

auto check_path = [](const auto& type, const auto& set, const auto& path) {
bool allowed = (type == Directory::BLACKLIST);
auto check_path = [](const auto& type, const auto& set,
const auto& path) -> auto {
bool allowed = (type == Directory::AllowType::BLACKLIST);
for (const auto& reg : set) {
if (std::regex_match(path.string(), reg)) {
allowed = (type == Directory::WHITELIST);
allowed = (type == Directory::AllowType::WHITELIST);
}
}
return allowed;
};

bool allowed =
check_path(Directory::BLACKLIST, Directory::GetForcedBlacklist(), path);
bool allowed = check_path(Directory::AllowType::BLACKLIST,
Directory::GetForcedBlacklist(), path);

allowed =
(allowed ? check_path(dir.GetType(), dir.GetAllowSet(), path) : false);
Expand Down
3 changes: 2 additions & 1 deletion libs/sources/Socket/WindowsSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

// I have no idea, where SOCKET_ERROR, etc defined, so
Expand Down Expand Up @@ -89,7 +90,7 @@ auto WindowsSocket::SendMessageAndCloseClient(

auto WindowsSocket::Accept() -> WindowsSocket::SocketDescriptor {
auto client_addr = accept(socketDescriptor_, nullptr, nullptr);
if (client_addr == INVALID_SOCKET) {
if (std::cmp_equal(client_addr, INVALID_SOCKET)) {
throw std::runtime_error("could not accept socket");
}

Expand Down
4 changes: 2 additions & 2 deletions libs/sources/SocketFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#ifdef POSIX
#include "Socket/PosixSocket.h"
#elif defined WINDOWS
#elifdef WINDOWS
#include "Socket/WindowsSocket.h"
#else
#error Unknown platform
Expand All @@ -20,7 +20,7 @@ auto SocketFactory::CreateSocket(const std::string& address, int port)
-> std::unique_ptr<ISocket> {
#ifdef POSIX
return std::make_unique<PosixSocket>(address, port);
#elif defined WINDOWS
#elifdef WINDOWS
return std::make_unique<WindowsSocket>(address, port);
#else
#error Unknown platform
Expand Down
5 changes: 3 additions & 2 deletions libs/sources/ThreadPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

ThreadPool::ThreadPool(std::uint64_t numThreads) {
for (decltype(numThreads) i = 0; i < numThreads; ++i) {
workers_.emplace_back([this] { workerThread(); });
workers_.emplace_back([this]() -> void { workerThread(); });
}
}

Expand Down Expand Up @@ -39,7 +39,8 @@ void ThreadPool::workerThread() {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queueMutex_);
condition_.wait(lock, [this] { return stop_ || !tasks_.empty(); });
condition_.wait(lock,
[this]() -> auto { return stop_ || !tasks_.empty(); });

if (stop_ && tasks_.empty()) {
return;
Expand Down
Loading
Loading