--- /dev/null
+BasedOnStyle: Google
+IndentWidth: 2
+UseTab: Always
+NamespaceIndentation: All
+ColumnLimit: 100
+BreakBeforeBraces: Custom
+AccessModifierOffset: -2
+BraceWrapping:
+ AfterNamespace: false
+ AfterClass: false
+ AfterControlStatement: false
+ AfterFunction: false
+ SplitEmptyFunction: false
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: false
+AllowShortLoopsOnASingleLine: true
+PenaltyBreakBeforeFirstCallParameter: 1000
+PenaltyBreakComment: 300
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 1000
+PenaltyExcessCharacter: 1000
+PenaltyReturnTypeOnItsOwnLine: 1000
--- /dev/null
+CompileFlags:
+ Add: [-D__cpp_concepts=202002L]
\ No newline at end of file
--- /dev/null
+cmake_minimum_required(VERSION 3.29)
+project(http CXX)
+
+# Set C++ standard
+set(CMAKE_CXX_STANDARD 23)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "Debug")
+endif()
+
+# Set output directories
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+set(CMAKE_ARCHITE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+
+# Set output directories
+set(OUTPUT_DIR "${PROJECT_SOURCE_DIR}/bin/${CMAKE_BUILD_TYPE}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
+
+file(GLOB SRC "src/*.cpp" "src/*.hpp" "src/*.h")
+add_executable(${PROJECT_NAME} ${SRC})
+
+target_sources(${PROJECT_NAME} PRIVATE ${PLATFORM_SRC})
+target_compile_definitions(${PROJECT_NAME} PRIVATE ROOTDIR="${CMAKE_SOURCE_DIR}")
+
+# Set output directories
+set_target_properties(${PROJECT_NAME} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_DIR}"
+ ARCHITE_OUTPUT_DIRECTORY "${OUTPUT_DIR}"
+ LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_DIR}")
+
+# Configuration-specific settings
+target_compile_definitions(${PROJECT_NAME} PRIVATE
+ $<$<CONFIG:Debug>:DEBUG>
+ $<$<CONFIG:OptimizedDebug>:OPTDEBUG>
+ $<$<CONFIG:Release>:RELEASE>)
+
+# Configuration-specific optimizations
+target_compile_options(${PROJECT_NAME} PRIVATE
+ $<$<CONFIG:Debug>:-g>
+ $<$<CONFIG:OptimizedDebug>:-O3>
+ $<$<CONFIG:Release>:-O3>)
+
+
+
+
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ # Workaround for std::expected not available in clang
+ add_compile_options(
+ -stdlib=libstdc++ -D__cpp_concepts=202002 -Wno-builtin-macro-redefined
+ )
+endif()
--- /dev/null
+{ pkgs ? import <nixpkgs> { } }:
+
+pkgs.mkShell {
+ nativeBuildInputs = with pkgs; [
+ man-pages
+
+ gnumake
+ cmake
+ clang-tools
+ cmake-language-server
+
+ tree-sitter-grammars.tree-sitter-c
+ tree-sitter-grammars.tree-sitter-cpp
+ ];
+
+ shellHook = ''
+ ${pkgs.onefetch}/bin/onefetch
+ '';
+}
--- /dev/null
+#pragma once
+
+namespace http {
+ enum class Error {
+ SocketCreation,
+ SocketConnection,
+ DNSResolution,
+ ServerNotFound,
+ };
+}
--- /dev/null
+#pragma once
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <cstdint>
+#include <expected>
+#include <string>
+
+#include "error.h"
+#include "method.h"
+#include "request.h"
+#include "response.h"
+
+#define ERR(error) std::unexpected(error)
+
+namespace http {
+ std::expected<Response, Error> send(Method, const RequestOpts& req);
+
+ std::expected<int8_t, Error> connect_to(const std::string& domain_name, const uint16_t port = 80);
+} // namespace http
--- /dev/null
+#include <iostream>
+
+#include "http.h"
+
+int main() {
+ auto resp1 = http::send(http::Method::GET, {.domain_name = "google.com"});
+ resp1 = http::send(http::Method::GET, {.domain_name = "example.com", .port = 0});
+
+ if (!resp1.has_value()) {
+ switch (resp1.error()) {
+ case http::Error::SocketCreation: {
+ std::cout << "Socket creation" << std::endl;
+ } break;
+ case http::Error::SocketConnection: {
+ std::cout << "Socket connection" << std::endl;
+ } break;
+ case http::Error::DNSResolution: {
+ std::cout << "DNS resolution" << std::endl;
+ } break;
+ case http::Error::ServerNotFound: {
+ std::cout << "Server not found" << std::endl;
+ } break;
+ }
+ }
+}
--- /dev/null
+#pragma once
+
+namespace http {
+ enum class Method {
+ GET,
+ HEAD,
+ POST,
+ PUT,
+ DELETE,
+ CONNECT,
+ OPTIONS,
+ TRACE,
+ PATCH,
+ UPDATE,
+ };
+}
--- /dev/null
+#pragma once
+
+#include "http.h"
+
+namespace http {
+ struct RequestOpts {
+ const std::string domain_name;
+ const uint16_t port = 80;
+ const std::string path = "/";
+ const std::string body = "";
+ };
+}
--- /dev/null
+#pragma once
+
+namespace http {
+ enum class Status {
+ OK = 200,
+ NOT_FOUND = 404,
+ };
+
+ struct Response {
+ Status status;
+ };
+} // namespace http
--- /dev/null
+#include <chrono>
+#include <iostream>
+#include <unordered_map>
+
+#include "http.h"
+
+static std::unordered_map<std::string, struct addrinfo> ip_map;
+
+namespace http {
+ std::expected<int8_t, Error> connect_to(const std::string &domain_name, const uint16_t port) {
+ struct addrinfo hints = {0}, *addr_list;
+ hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6
+ hints.ai_socktype = SOCK_STREAM; // TCP only
+
+ if (getaddrinfo(domain_name.c_str(), std::to_string(port).c_str(), &hints, &addr_list)) {
+ return ERR(Error::DNSResolution);
+ }
+
+ int8_t remote_socketfd;
+ if (auto it = ip_map.find(domain_name); it != ip_map.end()) {
+ remote_socketfd =
+ socket(it->second.ai_family, it->second.ai_socktype, it->second.ai_protocol);
+ if (remote_socketfd < 0) {
+ close(remote_socketfd);
+ return ERR(Error::SocketConnection);
+ }
+ } else {
+ struct addrinfo *remote = 0;
+ for (struct addrinfo *addr = addr_list; addr; addr = addr->ai_next) {
+ remote_socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+ if (remote_socketfd < 0) {
+ close(remote_socketfd);
+ continue;
+ }
+
+ struct timeval timeout;
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+
+ if (setsockopt(remote_socketfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout) < 0 ||
+ setsockopt(remote_socketfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof timeout) < 0) {
+ close(remote_socketfd);
+ continue;
+ }
+
+ // Connected with remote server
+ if (connect(remote_socketfd, addr->ai_addr, addr->ai_addrlen) < 0) {
+ close(remote_socketfd);
+ } else {
+ remote = addr;
+ break;
+ }
+ }
+
+ if (!remote) {
+ freeaddrinfo(addr_list);
+ return ERR(Error::ServerNotFound);
+ }
+
+ ip_map[domain_name] = *remote;
+ freeaddrinfo(addr_list);
+ }
+
+ return remote_socketfd;
+ }
+
+ std::expected<Response, Error> send(Method method, const RequestOpts &req) {
+ auto start = std::chrono::system_clock::now();
+ auto maybe_socketfd = connect_to(req.domain_name, req.port);
+ auto end = std::chrono::system_clock::now();
+
+ std::chrono::duration<double> elapsed_seconds = end - start;
+ std::cout << "elapsed time: " << elapsed_seconds.count() << "s\t";
+
+ if (!maybe_socketfd.has_value()) {
+ return ERR(maybe_socketfd.error());
+ }
+
+ std::cout << "All good" << std::endl;
+ close(maybe_socketfd.value());
+ return Response();
+ }
+} // namespace http