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