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
7 changes: 7 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Cache ccache
uses: actions/cache@v4
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ hashFiles('**/CMakeLists.txt') }}
restore-keys: |
${{ runner.os }}-ccache-
- name: Generate Cmake
run: |
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
Expand Down
9 changes: 6 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.15)
project(my_b LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)


Expand All @@ -12,7 +12,7 @@ FetchContent_Declare(
)
FetchContent_MakeAvailable(googletest)

include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${PROJECT_SOURCE_DIR}/src)

file(GLOB_RECURSE SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp")
file(GLOB TEST_SOURCES "${PROJECT_SOURCE_DIR}/tests/*.cpp")
Expand All @@ -28,6 +28,9 @@ enable_testing()
add_executable(${PROJECT_NAME} src/main.cpp)
add_executable(tests ${TEST_SOURCES})

# Define TEST_DATA_DIR for tests
target_compile_definitions(tests PRIVATE TEST_DATA_DIR="${PROJECT_SOURCE_DIR}/tests")

if (CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Debug ON")
target_compile_definitions(my_b PRIVATE DEBUG=1)
Expand All @@ -40,7 +43,7 @@ find_package(OpenSSL REQUIRED)
target_link_libraries(my_b_lib OpenSSL::SSL OpenSSL::Crypto)

target_link_libraries(my_b my_b_lib GTest::gtest_main)
target_link_libraries(tests my_b_lib GTest::gtest_main)
target_link_libraries(tests my_b_lib GTest::gtest_main gmock)

include(GoogleTest)

Expand Down
17 changes: 0 additions & 17 deletions include/file.h

This file was deleted.

52 changes: 0 additions & 52 deletions include/http_client.h

This file was deleted.

11 changes: 5 additions & 6 deletions src/file.cpp → src/file/File.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
#include "file.h"
#include "File.h"
#include <fstream>
#include <optional>
#include <string>
#include "logger.h"

namespace file {

File::File() { logger = new Logger("FILE"); }
File::~File() {}
static Logger* logger = new Logger("FILE");

std::optional<std::string> File::read(const std::string& path) {
logger->inf("Reading file {}", path);
std::optional<std::string> read(const std::string& path) {
std::ifstream file(path);

if (!file.is_open()) {
logger->err("Could not open the file '{}'", path);
logger->warn("Could not open the file '{}'", path);
return {};
}

Expand Down
7 changes: 7 additions & 0 deletions src/file/File.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once
#include <optional>
#include <string>

namespace file {
std::optional<std::string> read(const std::string& path);
} // namespace file
89 changes: 68 additions & 21 deletions src/http_client.cpp → src/http/HttpClient.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "http_client.h"
#include "HttpClient.h"
#include <arpa/inet.h>
#include <assert.h>
#include <http_client.h>
#include <http/HttpClient.h>
#include <netdb.h>
#include <netinet/in.h>
#include <openssl/bio.h>
Expand All @@ -13,8 +13,9 @@
#include <format>
#include <optional>
#include <unordered_map>
#include "Types.h"
#include "logger.h"
#include "url.h"
#include "url/Url.h"
#include "utils.h"

namespace http {
Expand All @@ -23,38 +24,39 @@ HttpClient::HttpClient() { logger = new Logger("HttpClient"); }

HttpClient::~HttpClient() {}

std::optional<HttpResponse> HttpClient::get(HttpReqParams params) {
std::optional<HttpResponse> HttpClient::get(const std::string& url) const {
std::optional<http::HttpResponse> resp{};
auto params = get_params_from_url(url);

logger->dbg("Host: {}", params.hostname);
logger->dbg("Port: {}", params.port);
logger->dbg("Path: {}", params.path);
logger->dbg("Scheme: {}",
params.scheme == url::Scheme::HTTP ? "HTTP" : "HTTPS");
if (!params.has_value()) {
return {};
}

std::string buffer = std::format("GET {} HTTP/1.0\r\n", params.path);
buffer.append(std::format("Host: {}\r\n", params.hostname));
std::string buffer = std::format("GET {} HTTP/1.0\r\n", params.value().path);
buffer.append(std::format("Host: {}\r\n", params.value().hostname));
buffer.append("User-Agent: mosa\r\n");
buffer.append("Connection: close\r\n");
buffer.append("\r\n");

logger->dbg("Sending:\n{}", buffer);

switch (params.scheme) {
switch (params.value().scheme) {
case url::Scheme::HTTP:
resp = http_req(params, buffer);
resp = http_req(params.value(), buffer);
break;
case url::Scheme::HTTPS:
resp = https_req(params, buffer);
resp = https_req(params.value(), buffer);
break;
default:
logger->err("Unknown scheme");
break;
}

return resp;
}

HttpResponse HttpClient::parse_response(const std::string& response) {
HttpResponse HttpClient::get_response_from_body(
const std::string& response) const {
if (response.empty()) {
return {.code = 404, .body = response};
}
Expand Down Expand Up @@ -105,8 +107,52 @@ HttpResponse HttpClient::parse_response(const std::string& response) {
return {.code = status_line.status, .body = body};
}

std::optional<HttpResponse> HttpClient::http_req(HttpReqParams params,
const std::string& buffer) {
std::optional<http::HttpReqParams> HttpClient::get_params_from_url(
const std::string& url) const {
http::HttpReqParams params{};
auto s1 = url.find("://");
std::string scheme = url.substr(0, s1);

std::string rest = url.substr(s1 + 3);

auto s2 = rest.find("/");

if (scheme == "http") {
params.scheme = url::Scheme::HTTP;
} else if (scheme == "https") {
params.scheme = url::Scheme::HTTPS;
} else {
logger->warn("Unknwon scheme");
return {};
}

if (s2 == std::string::npos) {
params.path = "/";
params.hostname = url.substr(s1 + 3);
} else {
params.hostname = rest.substr(0, s2);
params.path = rest.substr(s2);
}

// Custom port
auto c_port = params.hostname.find_first_of(":");
if (c_port != std::string::npos) {
params.port = std::stoi(params.hostname.substr(c_port + 1));
params.hostname = params.hostname.substr(0, c_port);
} else {
params.port = params.scheme == url::Scheme::HTTP ? 80 : 443;
}

logger->dbg("Host: {}", params.hostname);
logger->dbg("Port: {}", params.port);
logger->dbg("Path: {}", params.path);
logger->dbg("Scheme: {}",
params.scheme == url::Scheme::HTTP ? "HTTP" : "HTTPS");
return params;
}

std::optional<HttpResponse> HttpClient::http_req(
HttpReqParams params, const std::string& buffer) const {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);

if (sockfd < 0) {
Expand Down Expand Up @@ -140,15 +186,16 @@ std::optional<HttpResponse> HttpClient::http_req(HttpReqParams params,
}

std::string response{recv_buf};
auto parsed = parse_response(response);
auto parsed = get_response_from_body(response);

close(sockfd);
freeaddrinfo(res);
return parsed;
}

std::optional<HttpResponse> HttpClient::https_req(HttpReqParams params,
const std::string& buffer) {
// https://stackoverflow.com/questions/1011339/how-do-you-make-a-http-request-with-c
std::optional<HttpResponse> HttpClient::https_req(
HttpReqParams params, const std::string& buffer) const {
// OPENSSL --
BIO* bio;
SSL_CTX* ctx;
Expand Down Expand Up @@ -227,7 +274,7 @@ std::optional<HttpResponse> HttpClient::https_req(HttpReqParams params,

SSL_CTX_free(ctx);

auto parsed = parse_response(response);
auto parsed = get_response_from_body(response);
return parsed;
}

Expand Down
31 changes: 31 additions & 0 deletions src/http/HttpClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once
#include <optional>
#include <string>
#include "IHttpClient.h"
#include "Types.h"
#include "logger.h"
#include "url/Url.h"

namespace http {

class HttpClient : public IHttpClient {
public:
HttpClient();
~HttpClient();

std::optional<HttpResponse> get(const std::string& url) const override;

private:
HttpResponse get_response_from_body(const std::string& response) const;

std::optional<HttpResponse> http_req(HttpReqParams params,
const std::string& buffer) const;
std::optional<HttpResponse> https_req(HttpReqParams params,
const std::string& buffer) const;

std::optional<http::HttpReqParams> get_params_from_url(
const std::string& url) const;
Logger* logger;
};

} // namespace http
14 changes: 14 additions & 0 deletions src/http/IHttpClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once
#include <optional>
#include <string>
#include "Types.h"

namespace http {
class IHttpClient {
public:
virtual ~IHttpClient() {};
virtual std::optional<http::HttpResponse> get(
const std::string& url) const = 0;
};

} // namespace http
30 changes: 30 additions & 0 deletions src/http/Types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once
#include <url/Types.h>
#include <string>

namespace http {

enum class HttpPort {
HTTP = 80,
HTTPS = 443,
};

struct HttpReqParams {
int port;
std::string hostname;
std::string path;
url::Scheme scheme;
};

struct HttpStatusLine {
std::string version;
int status;
std::string explaination;
};

struct HttpResponse {
int code;
std::string body;
};

} // namespace http
File renamed without changes.
Loading