feat(request): catch parse errors
This commit is contained in:
parent
36961fc468
commit
64f5c39fb3
@ -42,17 +42,19 @@ void Client::request()
|
|||||||
buffer[bytesRead] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
|
buffer[bytesRead] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
|
||||||
httpRequest_->receiveData(buffer, static_cast<size_t>(bytesRead));
|
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", {
|
Log::info("Received complete request",
|
||||||
{"request_method", httpRequest_->getMethod()},
|
{
|
||||||
{"request_target", httpRequest_->getTarget()},
|
{"request_method", httpRequest_->getMethod()},
|
||||||
{"http_version", httpRequest_->getHttpVersion()},
|
{"request_target", httpRequest_->getTarget()},
|
||||||
{"headers", httpRequest_->getHeaders().toString()},
|
{"http_version", httpRequest_->getHttpVersion()},
|
||||||
{"body", httpRequest_->getBody()},
|
{"headers", httpRequest_->getHeaders().toString()},
|
||||||
});
|
{"body", httpRequest_->getBody()},
|
||||||
|
{"state", std::to_string(static_cast<uint8_t>(httpRequest_->getState()))},
|
||||||
|
});
|
||||||
server_.responseReady(client_socket_->getFd());
|
server_.responseReady(client_socket_->getFd());
|
||||||
httpRequest_->reset();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -66,8 +68,20 @@ void Client::request()
|
|||||||
|
|
||||||
std::string Client::getResponse() const
|
std::string Client::getResponse() const
|
||||||
{
|
{
|
||||||
std::string response = "HTTP/1.1 200 OK\r\nContent-Length: 32\r\n\r\nHello, World!";
|
Log::trace(LOCATION);
|
||||||
response += " Server port " + std::to_string(server_config_.getPort()) + "\r\n";
|
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);
|
Log::debug("Sending response:\n" + response);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#include <webserv/config/utils.hpp>
|
#include <webserv/config/utils.hpp>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@ -7,11 +8,15 @@ namespace utils
|
|||||||
{
|
{
|
||||||
size_t stoul(const std::string &str, int base)
|
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;
|
size_t idx = 0;
|
||||||
unsigned long value = std::stoul(str, &idx, base);
|
unsigned long value = std::stoul(str, &idx, base);
|
||||||
if (idx != str.length())
|
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;
|
return value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,20 +16,7 @@ std::optional<size_t> HttpHeaders::getContentLength() const
|
|||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
if (value.find_first_not_of("0123456789") != std::string::npos)
|
return utils::stoul(value);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> HttpHeaders::getContentType() const
|
std::optional<std::string> HttpHeaders::getContentType() const
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
#include "webserv/config/utils.hpp"
|
||||||
|
|
||||||
#include <webserv/http/HttpConstants.hpp> // for CRLF, DOUBLE_CRLF
|
#include <webserv/http/HttpConstants.hpp> // for CRLF, DOUBLE_CRLF
|
||||||
#include <webserv/http/HttpRequest.hpp>
|
#include <webserv/http/HttpRequest.hpp>
|
||||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||||
@ -47,36 +49,45 @@ void HttpRequest::parseBuffer()
|
|||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
switch (state_)
|
try
|
||||||
{
|
{
|
||||||
case State::RequestLine:
|
switch (state_)
|
||||||
if (!parseBufferforRequestLine())
|
|
||||||
{
|
{
|
||||||
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:
|
catch (...)
|
||||||
if (!parseBufferforHeaders())
|
{
|
||||||
{
|
Log::error("Exception during parsing, marking request as ParseError");
|
||||||
return; // Wait for more data
|
state_ = State::ParseError;
|
||||||
}
|
return;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,10 +135,8 @@ bool HttpRequest::parseBufferforHeaders()
|
|||||||
Log::debug("Headers waiting for more data: " + LOCATION);
|
Log::debug("Headers waiting for more data: " + LOCATION);
|
||||||
return false; // Wait for more data
|
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()));
|
headers_.parse(buffer_.substr(0, pos + Http::Protocol::CRLF.size()));
|
||||||
buffer_.erase(0, pos + Http::Protocol::DOUBLE_CRLF.size());
|
buffer_.erase(0, pos + Http::Protocol::DOUBLE_CRLF.size());
|
||||||
// parseContentLength();
|
|
||||||
|
|
||||||
if (this->headers_.getContentLength().value_or(0) > 0)
|
if (this->headers_.getContentLength().value_or(0) > 0)
|
||||||
{
|
{
|
||||||
@ -156,17 +165,9 @@ bool HttpRequest::parseBufferforChunkedBody()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::string chunkSizeStr = buffer_.substr(0, pos);
|
std::string chunkSizeStr = buffer_.substr(0, pos);
|
||||||
size_t chunkSize = 0;
|
Log::debug("Chunk size string: " + chunkSizeStr);
|
||||||
try
|
size_t chunkSize = utils::stoul(chunkSizeStr, 16);
|
||||||
{
|
Log::warning("Invalid chunk size: " + chunkSizeStr);
|
||||||
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;
|
|
||||||
}
|
|
||||||
if (chunkSize == 0)
|
if (chunkSize == 0)
|
||||||
{
|
{
|
||||||
state_ = State::Complete; // Last chunk
|
state_ = State::Complete; // Last chunk
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user