feat(request): catch parse errors

This commit is contained in:
Quinten Mennen 2025-09-24 13:34:07 +02:00
parent 36961fc468
commit 64f5c39fb3
4 changed files with 73 additions and 66 deletions

View File

@ -42,17 +42,19 @@ void Client::request()
buffer[bytesRead] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
httpRequest_->receiveData(buffer, static_cast<size_t>(bytesRead));
if (httpRequest_->getState() == HttpRequest::State::Complete)
if (httpRequest_->getState() == HttpRequest::State::Complete ||
httpRequest_->getState() == HttpRequest::State::ParseError)
{
Log::info("Received complete request", {
{"request_method", httpRequest_->getMethod()},
{"request_target", httpRequest_->getTarget()},
{"http_version", httpRequest_->getHttpVersion()},
{"headers", httpRequest_->getHeaders().toString()},
{"body", httpRequest_->getBody()},
});
Log::info("Received complete request",
{
{"request_method", httpRequest_->getMethod()},
{"request_target", httpRequest_->getTarget()},
{"http_version", httpRequest_->getHttpVersion()},
{"headers", httpRequest_->getHeaders().toString()},
{"body", httpRequest_->getBody()},
{"state", std::to_string(static_cast<uint8_t>(httpRequest_->getState()))},
});
server_.responseReady(client_socket_->getFd());
httpRequest_->reset();
}
else
{
@ -66,8 +68,20 @@ void Client::request()
std::string Client::getResponse() const
{
std::string response = "HTTP/1.1 200 OK\r\nContent-Length: 32\r\n\r\nHello, World!";
response += " Server port " + std::to_string(server_config_.getPort()) + "\r\n";
Log::trace(LOCATION);
std::string response = "HTTP/1.1 ";
if (httpRequest_->getState() == HttpRequest::State::ParseError)
{
response += "400 Bad Request\r\n";
}
else
{
response += "200 OK\r\n";
}
// further validation can be added here
response += "Content-Length: 18\r\n\r\n";
response += "Server port " + std::to_string(server_config_.getPort()) + "\r\n";
Log::debug("Sending response:\n" + response);
return response;
}

View File

@ -1,3 +1,4 @@
#include <webserv/config/utils.hpp>
#include <stdexcept>
@ -7,11 +8,15 @@ namespace utils
{
size_t stoul(const std::string &str, int base)
{
if (str.find_first_not_of("0123456789abcdefABCDEF") != std::string::npos)
{
throw std::invalid_argument("Invalid number: " + str);
}
size_t idx = 0;
unsigned long value = std::stoul(str, &idx, base);
if (idx != str.length())
{
throw std::invalid_argument("Invalid characters in number: " + str);
throw std::invalid_argument("Invalid number with extra characters: " + str);
}
return value;
}

View File

@ -16,20 +16,7 @@ std::optional<size_t> HttpHeaders::getContentLength() const
{
return std::nullopt;
}
if (value.find_first_not_of("0123456789") != std::string::npos)
{
Log::warning("Non-numeric Content-Length header value: " + value);
return std::nullopt;
}
try
{
return utils::stoul(value);
}
catch (...)
{
Log::warning("Invalid Content-Length header value: " + value);
return std::nullopt;
}
return utils::stoul(value);
}
std::optional<std::string> HttpHeaders::getContentType() const

View File

@ -1,3 +1,5 @@
#include "webserv/config/utils.hpp"
#include <webserv/http/HttpConstants.hpp> // for CRLF, DOUBLE_CRLF
#include <webserv/http/HttpRequest.hpp>
#include <webserv/log/Log.hpp> // for Log, LOCATION
@ -47,36 +49,45 @@ void HttpRequest::parseBuffer()
{
while (true)
{
switch (state_)
try
{
case State::RequestLine:
if (!parseBufferforRequestLine())
switch (state_)
{
return; // Wait for more data
case State::RequestLine:
if (!parseBufferforRequestLine())
{
return; // Wait for more data
}
break;
case State::Headers:
if (!parseBufferforHeaders())
{
return; // Wait for more data
}
break;
case State::Body:
if (!parseBufferforBody())
{
return; // Wait for more data
}
break;
case State::Chunked:
if (!parseBufferforChunkedBody())
{
return; // Wait for more data
}
break;
case State::Complete:
Log::debug("HttpRequest::parseBuffer() request is complete");
return; // Request is complete
case State::ParseError: Log::warning("Parse error occurred, stopping further processing"); return;
}
break;
case State::Headers:
if (!parseBufferforHeaders())
{
return; // Wait for more data
}
break;
case State::Body:
if (!parseBufferforBody())
{
return; // Wait for more data
}
break;
case State::Chunked:
if (!parseBufferforChunkedBody())
{
return; // Wait for more data
}
break;
case State::Complete:
Log::debug("HttpRequest::parseBuffer() request is complete");
return; // Request is complete
case State::ParseError: Log::warning("Parse error occurred, stopping further processing"); return;
}
catch (...)
{
Log::error("Exception during parsing, marking request as ParseError");
state_ = State::ParseError;
return;
}
}
}
@ -124,10 +135,8 @@ bool HttpRequest::parseBufferforHeaders()
Log::debug("Headers waiting for more data: " + LOCATION);
return false; // Wait for more data
}
// headers_ = buffer_.substr(0, pos + Http::Protocol::CRLF.size()); // Include the last \r\n
headers_.parse(buffer_.substr(0, pos + Http::Protocol::CRLF.size()));
buffer_.erase(0, pos + Http::Protocol::DOUBLE_CRLF.size());
// parseContentLength();
if (this->headers_.getContentLength().value_or(0) > 0)
{
@ -156,17 +165,9 @@ bool HttpRequest::parseBufferforChunkedBody()
return false;
}
std::string chunkSizeStr = buffer_.substr(0, pos);
size_t chunkSize = 0;
try
{
chunkSize = std::stoul(chunkSizeStr, nullptr, 16);
}
catch (const std::exception &e)
{
Log::warning("Invalid chunk size: " + chunkSizeStr + " (" + e.what() + ")");
state_ = State::ParseError; // Mark as complete to avoid further processing
return true;
}
Log::debug("Chunk size string: " + chunkSizeStr);
size_t chunkSize = utils::stoul(chunkSizeStr, 16);
Log::warning("Invalid chunk size: " + chunkSizeStr);
if (chunkSize == 0)
{
state_ = State::Complete; // Last chunk