From c49f947a9bcaaa0dc5c593fbcb05e72ceff1d78d Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Mon, 3 Nov 2025 15:51:13 +0100 Subject: [PATCH] fixes --- .../Crafter.Network-ClientHTTP.cpp | 3 +- implementations/Crafter.Network-ClientTCP.cpp | 53 +++++++++++++------ .../Crafter.Network-ListenerHTTP.cpp | 8 +-- interfaces/Crafter.Network-ClientTCP.cppm | 21 +++++++- project.json | 10 ++++ tests/ShouldSendRecieveLargeHTTP.cpp | 49 +++++++++++++++++ 6 files changed, 123 insertions(+), 21 deletions(-) create mode 100644 tests/ShouldSendRecieveLargeHTTP.cpp diff --git a/implementations/Crafter.Network-ClientHTTP.cpp b/implementations/Crafter.Network-ClientHTTP.cpp index b6218e2..8b68b6f 100644 --- a/implementations/Crafter.Network-ClientHTTP.cpp +++ b/implementations/Crafter.Network-ClientHTTP.cpp @@ -55,7 +55,8 @@ HTTPResponse ClientHTTP::Send(const char* request, std::uint32_t length) { try { buffer = client.RecieveSync(); } catch(const SocketClosedException& e) { - client = ClientTCP(host.c_str(), port); + client.Stop(); + client.Connect(); client.Send(request, length); buffer = client.RecieveSync(); } diff --git a/implementations/Crafter.Network-ClientTCP.cpp b/implementations/Crafter.Network-ClientTCP.cpp index 3c1e813..42fb537 100755 --- a/implementations/Crafter.Network-ClientTCP.cpp +++ b/implementations/Crafter.Network-ClientTCP.cpp @@ -48,22 +48,13 @@ ClientTCP::ClientTCP(int socketid) : socketid(socketid) ClientTCP::ClientTCP(const char* hostName, std::uint16_t port) { - hostent *host; - sockaddr_in serv_addr; - - host=gethostbyname(hostName); - if((socketid = socket(AF_INET, SOCK_STREAM, 0)) == -1){ - std::cerr << "Could not open socket" << std::endl; - } - + host = gethostbyname(hostName); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); serv_addr.sin_addr = *((struct in_addr *)host->h_addr); bzero(&(serv_addr.sin_zero),8); - if(connect(socketid,(sockaddr*)&serv_addr, sizeof(sockaddr)) == -1){ - std::cerr << "Could not connect to server" << std::endl; - } + Connect(); } ClientTCP::ClientTCP(std::string hostName, std::uint16_t port): ClientTCP(hostName.c_str(), port) @@ -71,6 +62,14 @@ ClientTCP::ClientTCP(std::string hostName, std::uint16_t port): ClientTCP(hostNa } +ClientTCP::ClientTCP(ClientTCP&& other) noexcept : socketid(other.socketid) { + if(socketid != 1) { + shutdown(socketid, SHUT_RDWR); + close(socketid); + } + other.socketid = -1; +} + ClientTCP::~ClientTCP() { if(socketid != 1) { @@ -79,6 +78,16 @@ ClientTCP::~ClientTCP() } } +void ClientTCP::Connect() { + if((socketid = socket(AF_INET, SOCK_STREAM, 0)) == -1){ + std::cerr << "Could not open socket" << std::endl; + } + + if(connect(socketid,(sockaddr*)&serv_addr, sizeof(sockaddr)) == -1){ + std::cerr << "Could not connect to server" << std::endl; + } +} + void ClientTCP::Stop() { shutdown(socketid, SHUT_RDWR); close(socketid); @@ -86,9 +95,23 @@ void ClientTCP::Stop() { } void ClientTCP::Send(const void* buffer, std::uint32_t size) const { - send(socketid, reinterpret_cast(buffer), size, 0); -} + const char* data = reinterpret_cast(buffer); + std::uint_fast32_t totalSent = 0; + const std::uint_fast32_t chunkSize = 1024; + while (totalSent < size) { + std::uint_fast32_t bytesToSend = std::min(chunkSize, size - totalSent); + int status = send(socketid, data + totalSent, bytesToSend, 0); + + if (status == 0) { + throw SocketClosedException(); + } else if (status < 0) { + throw std::runtime_error(std::strerror(errno)); + } + + totalSent += status; + } +} std::vector ClientTCP::RecieveSync(std::uint32_t bufferSize) const { std::vector totalBuffer(bufferSize); int read = recv(socketid, totalBuffer.data(), bufferSize, 0); @@ -159,9 +182,9 @@ std::vector ClientTCP::RecieveUntilFullSync(std::uint32_t bufferSize) cons int read = 0; while(read < bufferSize) { int newRead = recv(socketid, buffer.data()+read, bufferSize-read, 0); - if(read < 0) { + if(newRead < 0) { throw std::runtime_error(std::strerror(errno)); - } else if(read == 0) { + } else if(newRead == 0) { throw SocketClosedException(); } read+=newRead; diff --git a/implementations/Crafter.Network-ListenerHTTP.cpp b/implementations/Crafter.Network-ListenerHTTP.cpp index 27fc294..2b2bca6 100644 --- a/implementations/Crafter.Network-ListenerHTTP.cpp +++ b/implementations/Crafter.Network-ListenerHTTP.cpp @@ -123,8 +123,8 @@ void ListenerHTTPClient::ListenRoutes() { std::uint32_t i = 0; std::uint32_t routeStart = 0; while(true) { + buffer = client.RecieveSync(); while(true) { - buffer = client.RecieveSync(); std::string str(buffer.begin(), buffer.end()); for(; i < buffer.size(); i++) { if(buffer[i] == ' ') { @@ -184,7 +184,7 @@ void ListenerHTTPClient::ListenRoutes() { const int lenght = std::stoi(it->second); request.body.resize(lenght, 0); if(lenght > 0 ){ - std::int_fast32_t remaining = lenght+i-buffer.size(); + std::int_fast32_t remaining = lenght-(buffer.size()-i); if(remaining < 0) { std::memcpy(&request.body[0], buffer.data()+i, lenght); std::string response = server->routes.at(route)(request); @@ -196,9 +196,9 @@ void ListenerHTTPClient::ListenRoutes() { client.Send(&response[0], response.size()); break; } else { - std::memcpy(&request.body[0], buffer.data()+i, lenght-remaining); + std::memcpy(&request.body[0], buffer.data()+i, buffer.size()-i); std::vector bodyBuffer = client.RecieveUntilFullSync(remaining); - std::memcpy(&request.body[remaining], bodyBuffer.data(), remaining); + std::memcpy(&request.body[buffer.size()-i], bodyBuffer.data(), remaining); std::string response = server->routes.at(route)(request); client.Send(&response[0], response.size()); break; diff --git a/interfaces/Crafter.Network-ClientTCP.cppm b/interfaces/Crafter.Network-ClientTCP.cppm index e36a54b..0de1e84 100755 --- a/interfaces/Crafter.Network-ClientTCP.cppm +++ b/interfaces/Crafter.Network-ClientTCP.cppm @@ -17,7 +17,20 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - +module; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include export module Crafter.Network:ClientTCP; import std; @@ -36,6 +49,9 @@ namespace Crafter { ClientTCP(const char* host, std::uint16_t port); ClientTCP(std::string host, std::uint16_t port); ~ClientTCP(); + ClientTCP(const ClientTCP&) = delete; + ClientTCP(ClientTCP&& other) noexcept; + void Connect(); void Stop(); void Send(const void* buffer, std::uint32_t size) const; std::vector RecieveSync() const; @@ -48,5 +64,8 @@ namespace Crafter { void RecieveAsync(std::uint32_t bufferSize, std::function)> recieveCallback) const; void RecieveUntilFullAsync(std::uint32_t bufferSize, std::function)> recieveCallback) const; void RecieveAsync(std::uint32_t bufferSize, std::function recieveCallback, char* buffer) const; + private: + hostent* host; + sockaddr_in serv_addr; }; } \ No newline at end of file diff --git a/project.json b/project.json index 37a2743..bc70234 100644 --- a/project.json +++ b/project.json @@ -84,6 +84,16 @@ "configuration":"lib-shared" } ] + }, + { + "name": "should-send-recieve-large-http", + "implementations": ["tests/ShouldSendRecieveLargeHTTP"], + "dependencies": [ + { + "path":"./project.json", + "configuration":"lib-shared" + } + ] } ] } diff --git a/tests/ShouldSendRecieveLargeHTTP.cpp b/tests/ShouldSendRecieveLargeHTTP.cpp new file mode 100644 index 0000000..6e881ae --- /dev/null +++ b/tests/ShouldSendRecieveLargeHTTP.cpp @@ -0,0 +1,49 @@ +/* +Crafter® Build +Copyright (C) 2025 Catcrafts® +Catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License version 3.0 as published by the Free Software Foundation; + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include +#include +import Crafter.Network; +import std; +using namespace Crafter; + +extern "C" { + std::string* RunTest() { + ListenerAsyncHTTP listener(8080, {{ "/", [&](const HTTPRequest& request) { + if (request.body.size() > 1'000'000) { + return CreateResponseHTTP("200 OK", "Large request received: " + std::to_string(request.body.size()) + " bytes"); + } + return CreateResponseHTTP("200 OK", "Small request received"); + } + }}); + + try { + ClientHTTP client("localhost", 8080); + std::string large_body(10 * 1024 * 1024, 'A'); + HTTPResponse response = client.Send(CreateRequestHTTP("POST", "/", "localhost", large_body)); + std::this_thread::sleep_for(std::chrono::seconds(1)); + if (response.status == "200 OK" && response.body.find("Large request received") != std::string::npos) { + return nullptr; + } else { + return new std::string("Unexpected response: " + response.status + " " + response.body); + } + } catch (const std::exception& e) { + return new std::string(e.what()); + } + } +} \ No newline at end of file