From d5cc1991ff6775e4ebf0ab74e0bb889b29b563a7 Mon Sep 17 00:00:00 2001 From: LeonardoBizzoni Date: Tue, 6 Aug 2024 16:48:56 +0200 Subject: [PATCH] Send formatted HTTP req and receive raw HTTP response --- .gitignore | 4 ++++ shell.nix | 1 + src/http.h | 3 ++- src/main.cpp | 4 ++-- src/method.cpp | 38 ++++++++++++++++++++++++++++++++++++++ src/method.h | 5 ++++- src/request.h | 17 ++++++++++++----- src/send.cpp | 42 ++++++++++++++++++++++++++++++++++-------- 8 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 .gitignore create mode 100644 src/method.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90efbf4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build +bin +.envrc +.cache \ No newline at end of file diff --git a/shell.nix b/shell.nix index 5c24fb8..334e56b 100644 --- a/shell.nix +++ b/shell.nix @@ -4,6 +4,7 @@ pkgs.mkShell { nativeBuildInputs = with pkgs; [ man-pages + valgrind gnumake cmake clang-tools diff --git a/src/http.h b/src/http.h index 5ba6b74..8219975 100644 --- a/src/http.h +++ b/src/http.h @@ -16,9 +16,10 @@ #include "response.h" #define ERR(error) std::unexpected(error) +#define NEW_LINE "\r\n" namespace http { - std::expected send(Method, const RequestOpts& req); + std::expected sendreq(Method, const RequestOpts& req); std::expected connect_to(const std::string& domain_name, const uint16_t port = 80); } // namespace http diff --git a/src/main.cpp b/src/main.cpp index cf38174..24f0f21 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,8 +3,8 @@ #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}); + auto resp1 = http::sendreq(http::Method::GET, {.domain_name = "example.com"}); + // auto resp1 = http::sendreq(http::Method::POST, {.domain_name = "example.com", .body = "Hello, World!"}); if (!resp1.has_value()) { switch (resp1.error()) { diff --git a/src/method.cpp b/src/method.cpp new file mode 100644 index 0000000..afd1ac6 --- /dev/null +++ b/src/method.cpp @@ -0,0 +1,38 @@ +#include "method.h" + +std::ostream &operator<<(std::ostream &os, const http::Method &method) { + using namespace http; + + switch (method) { + case Method::GET: { + return os << "GET"; + } break; + case Method::HEAD: { + return os << "HEAD"; + } break; + case Method::POST: { + return os << "POST"; + } break; + case Method::PUT: { + return os << "PUT"; + } break; + case Method::DELETE: { + return os << "DELETE"; + } break; + case Method::CONNECT: { + return os << "CONNECT"; + } break; + case Method::OPTIONS: { + return os << "OPTIONS"; + } break; + case Method::TRACE: { + return os << "TRACE"; + } break; + case Method::PATCH: { + return os << "PATCH"; + } break; + case Method::UPDATE: { + return os << "UPDATE"; + } break; + } +} diff --git a/src/method.h b/src/method.h index df21a81..3b787c1 100644 --- a/src/method.h +++ b/src/method.h @@ -1,5 +1,6 @@ #pragma once +#include namespace http { enum class Method { GET, @@ -13,4 +14,6 @@ namespace http { PATCH, UPDATE, }; -} +} // namespace http + +std::ostream &operator<<(std::ostream &os, const http::Method &method); diff --git a/src/request.h b/src/request.h index 76d80cf..284ab95 100644 --- a/src/request.h +++ b/src/request.h @@ -4,9 +4,16 @@ namespace http { struct RequestOpts { - const std::string domain_name; - const uint16_t port = 80; - const std::string path = "/"; - const std::string body = ""; + uint16_t port = 80; + std::string domain_name; + std::string host = domain_name; + std::string query = "/"; + std::string accept = "*/*"; + std::string body = ""; + + struct { + uint8_t major = 1; + uint8_t minor = 1; + } http_version; }; -} +} // namespace http diff --git a/src/send.cpp b/src/send.cpp index 8ec3c13..934cf0d 100644 --- a/src/send.cpp +++ b/src/send.cpp @@ -1,9 +1,11 @@ -#include #include +#include #include #include "http.h" +#define BUFFSIZE 1024 + static std::unordered_map ip_map; namespace http { @@ -64,19 +66,43 @@ namespace http { return remote_socketfd; } - std::expected send(Method method, const RequestOpts &req) { - auto start = std::chrono::system_clock::now(); + std::expected sendreq(Method method, const RequestOpts &req) { auto maybe_socketfd = connect_to(req.domain_name, req.port); - auto end = std::chrono::system_clock::now(); - - std::chrono::duration 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; + std::stringstream ss; + ss << method << " " << req.query << " HTTP/" << (int)req.http_version.major << "." + << (int)req.http_version.minor << NEW_LINE; + ss << "Host: " << req.host << NEW_LINE; + ss << "Accept: " << req.accept << NEW_LINE; + + if (!req.body.empty()) { + ss << "Content-Length: " << req.body.length() << NEW_LINE << NEW_LINE << req.body; + } else { + ss << NEW_LINE; + } + + std::string msg = ss.str(); + std::cout << "Request:\n===================\n" << msg << "\n===================\n" << std::endl; + send((int)maybe_socketfd.value(), msg.c_str(), msg.size(), 0); + + msg = ""; + char buffer[BUFFSIZE] = ""; + ssize_t bytes_read = 0; + while ((bytes_read = read((int)maybe_socketfd.value(), buffer, BUFFSIZE - 1)) > 0) { + if (bytes_read == -1) { + std::cerr << "\t\tError while reading!" << std::endl; + } + + buffer[bytes_read] = '\0'; + msg += buffer; + } + + std::cout << "Response:\n===================\n" << msg << "\n===================" << std::endl; + close(maybe_socketfd.value()); return Response(); } -- 2.52.0