feat: (better) http validation
This commit is contained in:
parent
a09ff469ee
commit
1b123d84e3
@ -81,8 +81,7 @@ void Client::request()
|
||||
buffer[bytesRead] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
|
||||
httpRequest_->receiveData(static_cast<const char *>(buffer), static_cast<size_t>(bytesRead));
|
||||
|
||||
if (httpRequest_->getState() == HttpRequest::State::Complete
|
||||
|| httpRequest_->getState() == HttpRequest::State::ParseError)
|
||||
if (httpRequest_->getState() == HttpRequest::State::Complete)
|
||||
{
|
||||
Log::info("Received request: " + httpRequest_->getHttpVersion() + " " + httpRequest_->getMethod() + " "
|
||||
+ httpRequest_->getTarget() + " ");
|
||||
|
||||
@ -34,10 +34,16 @@ void HttpRequest::setState(State state)
|
||||
{
|
||||
if (state == State::Complete)
|
||||
{
|
||||
if (! headers_.getHost().has_value())
|
||||
{
|
||||
client_->getHttpResponse().setError(Http::StatusCode::BAD_REQUEST);
|
||||
state_ = State::ParseError;
|
||||
return;
|
||||
}
|
||||
ServerConfig *serverConfig = getServerConfig();
|
||||
if (serverConfig == nullptr)
|
||||
{
|
||||
Log::error("No matching server config found for host");
|
||||
client_->getHttpResponse().setError(Http::StatusCode::NOT_FOUND);
|
||||
state_ = State::ParseError;
|
||||
return;
|
||||
}
|
||||
@ -135,6 +141,7 @@ bool HttpRequest::parseBufferforRequestLine()
|
||||
if (parts.size() != 3)
|
||||
{
|
||||
Log::warning("Invalid request line: " + requestLine_);
|
||||
client_->getHttpResponse().setError(Http::StatusCode::BAD_REQUEST);
|
||||
state_ = State::ParseError; // Mark as complete to avoid further processing
|
||||
return true;
|
||||
}
|
||||
@ -194,6 +201,7 @@ bool HttpRequest::parseBufferforChunkedBody()
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Log::warning("Invalid chunk size: " + chunkSizeStr + " - " + e.what());
|
||||
client_->getHttpResponse().setError(400);
|
||||
setState(State::ParseError);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/http/HttpResponse.hpp"
|
||||
#include <webserv/config/ServerConfig.hpp>
|
||||
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
|
||||
|
||||
|
||||
@ -13,16 +13,31 @@ void HttpResponse::addHeader(const std::string &key, const std::string &value)
|
||||
|
||||
void HttpResponse::appendBody(const std::vector<uint8_t> &data)
|
||||
{
|
||||
if (complete_)
|
||||
{
|
||||
Log::warning("Attempt to set body on a completed HttpResponse");
|
||||
return;
|
||||
}
|
||||
body_.insert(body_.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
void HttpResponse::appendBody(const std::string &body)
|
||||
{
|
||||
if (complete_)
|
||||
{
|
||||
Log::warning("Attempt to set body on a completed HttpResponse");
|
||||
return;
|
||||
}
|
||||
body_.insert(body_.end(), body.begin(), body.end());
|
||||
}
|
||||
|
||||
void HttpResponse::setBody(const std::vector<uint8_t> &data) // TODO: validate headers
|
||||
{
|
||||
if (complete_)
|
||||
{
|
||||
Log::warning("Attempt to set body on a completed HttpResponse");
|
||||
return;
|
||||
}
|
||||
body_ = data;
|
||||
setComplete();
|
||||
}
|
||||
@ -43,6 +58,12 @@ void HttpResponse::setComplete()
|
||||
complete_ = true;
|
||||
}
|
||||
|
||||
void HttpResponse::setError(uint16_t statusCode)
|
||||
{
|
||||
statusCode_ = statusCode;
|
||||
complete_ = true;
|
||||
}
|
||||
|
||||
bool HttpResponse::isComplete() const noexcept
|
||||
{
|
||||
return complete_;
|
||||
|
||||
@ -31,6 +31,7 @@ class HttpResponse
|
||||
void setBody(const std::string &body);
|
||||
|
||||
void setComplete();
|
||||
void setError(uint16_t statusCode);
|
||||
|
||||
void setStatus(uint16_t statusCode);
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
#include "webserv/log/Log.hpp"
|
||||
#include <webserv/config/AConfig.hpp>
|
||||
#include <webserv/http/RequestValidator.hpp>
|
||||
|
||||
#include <webserv/config/AConfig.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -20,9 +21,43 @@ std::optional<RequestValidator::ValidationError> RequestValidator::validate() co
|
||||
{
|
||||
return error;
|
||||
}
|
||||
if (auto error = validateHttpVersion())
|
||||
{
|
||||
return error;
|
||||
}
|
||||
if (auto error = validateHostHeader())
|
||||
{
|
||||
return error;
|
||||
}
|
||||
return std::nullopt; // No validation errors
|
||||
}
|
||||
|
||||
std::optional<RequestValidator::ValidationError> RequestValidator::validateHostHeader() const
|
||||
{
|
||||
if (!request->getHeaders().has("Host"))
|
||||
{
|
||||
return ValidationError{400, "Bad Request: Missing Host header"};
|
||||
}
|
||||
std::string hostHeader = request->getHeaders().get("Host");
|
||||
// Basic validation: check if host header is not empty
|
||||
if (hostHeader.empty())
|
||||
{
|
||||
return ValidationError{400, "Bad Request: Empty Host header"};
|
||||
}
|
||||
Log::debug("Host header validated: " + hostHeader);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<RequestValidator::ValidationError> RequestValidator::validateHttpVersion() const
|
||||
{
|
||||
std::string httpVersion = request->getHttpVersion();
|
||||
if (httpVersion != "HTTP/1.1" && httpVersion != "HTTP/1.0")
|
||||
{
|
||||
return ValidationError{505, "HTTP Version Not Supported"};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<RequestValidator::ValidationError> RequestValidator::validateContentLength() const
|
||||
{
|
||||
size_t bodySize = request->getBody().size();
|
||||
@ -48,6 +83,10 @@ std::optional<RequestValidator::ValidationError> RequestValidator::validateMetho
|
||||
auto allowedMethodsOpt = config->get<std::vector<std::string>>("allowed_methods");
|
||||
std::vector<std::string> allowedMethods;
|
||||
|
||||
if (request->getMethod().empty())
|
||||
{
|
||||
return ValidationError{400, "Bad Request: Empty HTTP Method"};
|
||||
}
|
||||
if (allowedMethodsOpt.has_value())
|
||||
{
|
||||
allowedMethods = std::move(*allowedMethodsOpt);
|
||||
|
||||
@ -39,4 +39,6 @@ class RequestValidator
|
||||
|
||||
[[nodiscard]] std::optional<ValidationError> validateContentLength() const;
|
||||
[[nodiscard]] std::optional<ValidationError> validateMethod() const;
|
||||
[[nodiscard]] std::optional<ValidationError> validateHttpVersion() const;
|
||||
[[nodiscard]] std::optional<ValidationError> validateHostHeader() const;
|
||||
};
|
||||
@ -45,13 +45,8 @@ std::unique_ptr<AHandler> Router::handleRequest()
|
||||
Log::trace(LOCATION);
|
||||
|
||||
HttpRequest &request = client_->getHttpRequest();
|
||||
if (request.getState() == HttpRequest::State::ParseError)
|
||||
{
|
||||
Log::error("Router::handleRequest() called with incomplete request");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
HttpResponse &response = client_->getHttpResponse();
|
||||
|
||||
const AConfig *config = request.getUri().getConfig();
|
||||
|
||||
auto validator = std::make_unique<RequestValidator>(config, &request);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user