feat: implement redirect handling and update configuration
This commit is contained in:
parent
2fce81c2e1
commit
9e773e43fd
@ -54,6 +54,10 @@ server {
|
|||||||
index index.html;
|
index index.html;
|
||||||
allowed_methods GET;
|
allowed_methods GET;
|
||||||
}
|
}
|
||||||
|
location /redirect {
|
||||||
|
redirect 301 http://localhost:8081/;
|
||||||
|
allowed_methods GET POST;
|
||||||
|
}
|
||||||
|
|
||||||
# cgi_enabled yes;
|
# cgi_enabled yes;
|
||||||
# cgi_ext .php /usr/bin/php-cgi;
|
# cgi_ext .php /usr/bin/php-cgi;
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
#include <webserv/client/Client.hpp>
|
#include <webserv/client/Client.hpp>
|
||||||
|
|
||||||
#include <webserv/http/RequestValidator.hpp>
|
|
||||||
|
|
||||||
#include <webserv/handler/CgiHandler.hpp> // for CgiHandler
|
#include <webserv/handler/CgiHandler.hpp> // for CgiHandler
|
||||||
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
|
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
|
||||||
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
|
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
|
||||||
#include <webserv/http/HttpRequest.hpp> // for HttpRequest
|
#include <webserv/http/HttpRequest.hpp> // for HttpRequest
|
||||||
#include <webserv/http/HttpResponse.hpp> // for HttpResponse
|
#include <webserv/http/HttpResponse.hpp> // for HttpResponse
|
||||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
#include <webserv/http/RequestValidator.hpp>
|
||||||
#include <webserv/router/Router.hpp> // for Router
|
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||||
#include <webserv/server/Server.hpp> // for Server
|
#include <webserv/router/Router.hpp> // for Router
|
||||||
#include <webserv/socket/ASocket.hpp> // for ASocket
|
#include <webserv/server/Server.hpp> // for Server
|
||||||
#include <webserv/socket/ClientSocket.hpp> // for ClientSocket
|
#include <webserv/socket/ASocket.hpp> // for ASocket
|
||||||
|
#include <webserv/socket/ClientSocket.hpp> // for ClientSocket
|
||||||
|
|
||||||
#include <cstdint> // for uint8_t
|
#include <cstdint> // for uint8_t
|
||||||
#include <functional> // for function, ref, reference_wrapper
|
#include <functional> // for function, ref, reference_wrapper
|
||||||
@ -84,40 +82,43 @@ void Client::request()
|
|||||||
if (httpRequest_->getState() == HttpRequest::State::Complete
|
if (httpRequest_->getState() == HttpRequest::State::Complete
|
||||||
|| httpRequest_->getState() == HttpRequest::State::ParseError)
|
|| httpRequest_->getState() == HttpRequest::State::ParseError)
|
||||||
{
|
{
|
||||||
Log::info("Received complete request",
|
Log::info("Received request: " + httpRequest_->getHttpVersion() + " " + httpRequest_->getMethod() + " "
|
||||||
{
|
+ httpRequest_->getTarget() + " ");
|
||||||
{"request_method", httpRequest_->getMethod()},
|
|
||||||
{"request_target", httpRequest_->getTarget()},
|
Log::debug("Request details",
|
||||||
{"http_version", httpRequest_->getHttpVersion()},
|
{
|
||||||
{"headers", httpRequest_->getHeaders().toString()},
|
{"request_method", httpRequest_->getMethod()},
|
||||||
{"body", httpRequest_->getBody()},
|
{"request_target", httpRequest_->getTarget()},
|
||||||
{"state", std::to_string(static_cast<uint8_t>(httpRequest_->getState()))},
|
{"http_version", httpRequest_->getHttpVersion()},
|
||||||
});
|
{"headers", httpRequest_->getHeaders().toString()},
|
||||||
|
{"body", httpRequest_->getBody()},
|
||||||
|
{"state", std::to_string(static_cast<uint8_t>(httpRequest_->getState()))},
|
||||||
|
});
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Thoughts: if a handler isn't returned, this could because of the error handler already setting up the
|
// Thoughts: if a handler isn't returned, this could because of the error handler already setting
|
||||||
// response so, maybe we don't need to throw a 500 when no handler. Because that would override the actual
|
// up the response so, maybe we don't need to throw a 500 when no handler. Because that would
|
||||||
// error response. How about the router, or a handler, throws an exception if something goes wrong, and we
|
// override the actual error response. How about the router, or a handler, throws an exception if
|
||||||
// catch it here to make a 500 response?
|
// something goes wrong, and we catch it here to make a 500 response?
|
||||||
handler_ = router_->handleRequest();
|
handler_ = router_->handleRequest();
|
||||||
if (handler_ != nullptr)
|
if (handler_ != nullptr)
|
||||||
{
|
{
|
||||||
handler_->handle();
|
handler_->handle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const RequestValidator::ValidationException &e)
|
catch (const RequestValidator::ValidationException &e)
|
||||||
{
|
{
|
||||||
Log::error("Exception during request handling: " + std::string(e.what()));
|
Log::error("Exception during request handling: " + std::string(e.what()));
|
||||||
ErrorHandler::createErrorResponse(e.code(), *httpResponse_);
|
ErrorHandler::createErrorResponse(e.code(), *httpResponse_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
Log::error("Exception during request handling: " + std::string(e.what()));
|
Log::error("Exception during request handling: " + std::string(e.what()));
|
||||||
ErrorHandler::createErrorResponse(500, *httpResponse_);
|
ErrorHandler::createErrorResponse(500, *httpResponse_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -150,7 +151,7 @@ void Client::poll() const
|
|||||||
// CGI handler polling logic if needed
|
// CGI handler polling logic if needed
|
||||||
cgiHandler->wait();
|
cgiHandler->wait();
|
||||||
}
|
}
|
||||||
if (httpResponse_->isComplete())
|
if (httpResponse_->isComplete() && clientSocket_->getEvent() != ASocket::IoState::WRITE)
|
||||||
{
|
{
|
||||||
Log::info("Response is ready to be sent to client, fd: " + std::to_string(clientSocket_->getFd()));
|
Log::info("Response is ready to be sent to client, fd: " + std::to_string(clientSocket_->getFd()));
|
||||||
clientSocket_->setCallback([this]() { respond(); });
|
clientSocket_->setCallback([this]() { respond(); });
|
||||||
|
|||||||
@ -35,7 +35,7 @@ class DirectiveFactory
|
|||||||
{.name = "cgi_timeout", .type = "IntDirective", .context = "gsl"},
|
{.name = "cgi_timeout", .type = "IntDirective", .context = "gsl"},
|
||||||
{.name = "upload_enabled", .type = "BoolDirective", .context = "gsl"},
|
{.name = "upload_enabled", .type = "BoolDirective", .context = "gsl"},
|
||||||
{.name = "upload_store", .type = "StringDirective", .context = "gsl"},
|
{.name = "upload_store", .type = "StringDirective", .context = "gsl"},
|
||||||
{.name = "redirect", .type = "VectorDirective", .context = "l"},
|
{.name = "redirect", .type = "IntStringDirective", .context = "l"},
|
||||||
{.name = "timeout", .type = "IntDirective", .context = "gsl"},
|
{.name = "timeout", .type = "IntDirective", .context = "gsl"},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|||||||
40
webserv/handler/RedirectHandler.cpp
Normal file
40
webserv/handler/RedirectHandler.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include <webserv/handler/RedirectHandler.hpp> // for RedirectHandler
|
||||||
|
#include <webserv/http/HttpRequest.hpp> // for HttpRequest
|
||||||
|
#include <webserv/http/HttpResponse.hpp> // for HttpResponse
|
||||||
|
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||||
|
#include <webserv/handler/URI.hpp> // for URI
|
||||||
|
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
|
||||||
|
#include <webserv/http/HttpConstants.hpp> // for HttpConstants
|
||||||
|
|
||||||
|
|
||||||
|
RedirectHandler::RedirectHandler(const HttpRequest &request, HttpResponse &response)
|
||||||
|
: AHandler(request, response)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RedirectHandler::handle()
|
||||||
|
{
|
||||||
|
if (request_.getUri().isRedirect())
|
||||||
|
{
|
||||||
|
Log::info("Redirecting request to: " + request_.getUri().getRedirect().second + " with reason: "
|
||||||
|
+ Http::getStatusCodeReason(request_.getUri().getRedirect().first));
|
||||||
|
std::pair<int, std::string> redirect = request_.getUri().getRedirect();
|
||||||
|
response_.setStatus(redirect.first);
|
||||||
|
response_.addHeader(std::string(Http::Header::REDIRECT_LOCATION), redirect.second);
|
||||||
|
response_.addHeader(std::string(Http::Header::CONTENT_TYPE), std::string(Http::MimeType::TEXT_HTML));
|
||||||
|
response_.addHeader(std::string(Http::Header::CACHE_CONTROL), "no-cache");
|
||||||
|
std::string body = "<html><head><title>" + std::to_string(redirect.first) + " " + Http::getStatusCodeReason(redirect.first) + "</title></head>"
|
||||||
|
"<body><a href=\"" +
|
||||||
|
redirect.second + "\">Found</a></body></html>";
|
||||||
|
response_.setBody(body);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RedirectHandler::handleTimeout()
|
||||||
|
{
|
||||||
|
|
||||||
|
Log::warning("Redirect handler timeout occurred for path: " + request_.getUri().getFullPath());
|
||||||
|
ErrorHandler::createErrorResponse(504, response_, request_.getUri().getConfig());
|
||||||
|
}
|
||||||
|
|
||||||
18
webserv/handler/RedirectHandler.hpp
Normal file
18
webserv/handler/RedirectHandler.hpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <webserv/handler/AHandler.hpp> // for AHandler
|
||||||
|
|
||||||
|
class RedirectHandler : public AHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RedirectHandler(const HttpRequest &request, HttpResponse &response);
|
||||||
|
|
||||||
|
|
||||||
|
void handle() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void handleTimeout() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
@ -1,8 +1,7 @@
|
|||||||
#include <webserv/handler/URI.hpp>
|
|
||||||
|
|
||||||
#include <webserv/config/AConfig.hpp> // for AConfig
|
#include <webserv/config/AConfig.hpp> // for AConfig
|
||||||
#include <webserv/config/LocationConfig.hpp> // for LocationConfig
|
#include <webserv/config/LocationConfig.hpp> // for LocationConfig
|
||||||
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
||||||
|
#include <webserv/handler/URI.hpp>
|
||||||
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
|
#include <webserv/http/HttpHeaders.hpp> // for HttpHeaders
|
||||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||||
#include <webserv/utils/FileUtils.hpp> // for joinPath, isDirectory, isFile, getExtension, isValidPath
|
#include <webserv/utils/FileUtils.hpp> // for joinPath, isDirectory, isFile, getExtension, isValidPath
|
||||||
@ -183,6 +182,22 @@ bool URI::isCgi() const noexcept
|
|||||||
return !getCgiPath().empty();
|
return !getCgiPath().empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool URI::isRedirect() const noexcept
|
||||||
|
{
|
||||||
|
auto redirectOpt = config_->get<std::pair<int, std::string>>("redirect");
|
||||||
|
return redirectOpt.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, std::string> URI::getRedirect() const
|
||||||
|
{
|
||||||
|
auto redirectOpt = config_->get<std::pair<int, std::string>>("redirect");
|
||||||
|
if (redirectOpt.has_value())
|
||||||
|
{
|
||||||
|
return redirectOpt.value();
|
||||||
|
}
|
||||||
|
return {0, ""};
|
||||||
|
}
|
||||||
|
|
||||||
std::string URI::getCgiPath() const
|
std::string URI::getCgiPath() const
|
||||||
{
|
{
|
||||||
// Log::debug("BaseName: " + baseName_ + ", FullPath: " + fullPath_ + ", Dir: " + dir_ + ", PathInfo: " + pathInfo_
|
// Log::debug("BaseName: " + baseName_ + ", FullPath: " + fullPath_ + ", Dir: " + dir_ + ", PathInfo: " + pathInfo_
|
||||||
|
|||||||
@ -21,9 +21,12 @@ class URI
|
|||||||
[[nodiscard]] bool isDirectory() const noexcept;
|
[[nodiscard]] bool isDirectory() const noexcept;
|
||||||
[[nodiscard]] bool isValid() const noexcept;
|
[[nodiscard]] bool isValid() const noexcept;
|
||||||
[[nodiscard]] bool isCgi() const noexcept;
|
[[nodiscard]] bool isCgi() const noexcept;
|
||||||
|
[[nodiscard]] bool isRedirect() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] std::string getExtension() const noexcept;
|
[[nodiscard]] std::string getExtension() const noexcept;
|
||||||
[[nodiscard]] std::string getCgiPath() const;
|
[[nodiscard]] std::string getCgiPath() const;
|
||||||
|
[[nodiscard]] std::pair<int, std::string> getRedirect() const;
|
||||||
|
|
||||||
[[nodiscard]] const AConfig *getConfig() const noexcept;
|
[[nodiscard]] const AConfig *getConfig() const noexcept;
|
||||||
[[nodiscard]] const std::string &getBaseName() const noexcept;
|
[[nodiscard]] const std::string &getBaseName() const noexcept;
|
||||||
[[nodiscard]] const std::string &getFullPath() const noexcept;
|
[[nodiscard]] const std::string &getFullPath() const noexcept;
|
||||||
|
|||||||
@ -35,6 +35,13 @@ namespace StatusCode
|
|||||||
constexpr uint16_t OK = 200;
|
constexpr uint16_t OK = 200;
|
||||||
constexpr uint16_t CREATED = 201;
|
constexpr uint16_t CREATED = 201;
|
||||||
constexpr uint16_t NO_CONTENT = 204;
|
constexpr uint16_t NO_CONTENT = 204;
|
||||||
|
|
||||||
|
constexpr uint16_t MOVED_PERMANENTLY = 301;
|
||||||
|
constexpr uint16_t FOUND = 302;
|
||||||
|
constexpr uint16_t SEE_OTHER = 303;
|
||||||
|
constexpr uint16_t TEMPORARY_REDIRECT = 307;
|
||||||
|
constexpr uint16_t PERMANENT_REDIRECT = 308;
|
||||||
|
|
||||||
constexpr uint16_t BAD_REQUEST = 400;
|
constexpr uint16_t BAD_REQUEST = 400;
|
||||||
constexpr uint16_t UNAUTHORIZED = 401;
|
constexpr uint16_t UNAUTHORIZED = 401;
|
||||||
constexpr uint16_t FORBIDDEN = 403;
|
constexpr uint16_t FORBIDDEN = 403;
|
||||||
@ -53,10 +60,15 @@ struct StatusCodeInfo
|
|||||||
std::string_view reason;
|
std::string_view reason;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::array<StatusCodeInfo, 13> statusCodeInfos
|
static constexpr std::array<StatusCodeInfo, 18> statusCodeInfos
|
||||||
= {{{.code = StatusCode::OK, .reason = "OK"},
|
= {{{.code = StatusCode::OK, .reason = "OK"},
|
||||||
{.code = StatusCode::CREATED, .reason = "Created"},
|
{.code = StatusCode::CREATED, .reason = "Created"},
|
||||||
{.code = StatusCode::NO_CONTENT, .reason = "No Content"},
|
{.code = StatusCode::NO_CONTENT, .reason = "No Content"},
|
||||||
|
{.code = StatusCode::MOVED_PERMANENTLY, .reason = "Moved Permanently"},
|
||||||
|
{.code = StatusCode::FOUND, .reason = "Found"},
|
||||||
|
{.code = StatusCode::SEE_OTHER, .reason = "See Other"},
|
||||||
|
{.code = StatusCode::TEMPORARY_REDIRECT, .reason = "Temporary Redirect"},
|
||||||
|
{.code = StatusCode::PERMANENT_REDIRECT, .reason = "Permanent Redirect"},
|
||||||
{.code = StatusCode::BAD_REQUEST, .reason = "Bad Request"},
|
{.code = StatusCode::BAD_REQUEST, .reason = "Bad Request"},
|
||||||
{.code = StatusCode::UNAUTHORIZED, .reason = "Unauthorized"},
|
{.code = StatusCode::UNAUTHORIZED, .reason = "Unauthorized"},
|
||||||
{.code = StatusCode::FORBIDDEN, .reason = "Forbidden"},
|
{.code = StatusCode::FORBIDDEN, .reason = "Forbidden"},
|
||||||
@ -75,6 +87,7 @@ namespace Header
|
|||||||
{
|
{
|
||||||
constexpr std::string_view CONTENT_TYPE = "Content-Type";
|
constexpr std::string_view CONTENT_TYPE = "Content-Type";
|
||||||
constexpr std::string_view CONTENT_LENGTH = "Content-Length";
|
constexpr std::string_view CONTENT_LENGTH = "Content-Length";
|
||||||
|
constexpr std::string_view REDIRECT_LOCATION = "Location";
|
||||||
constexpr std::string_view HOST = "Host";
|
constexpr std::string_view HOST = "Host";
|
||||||
constexpr std::string_view USER_AGENT = "User-Agent";
|
constexpr std::string_view USER_AGENT = "User-Agent";
|
||||||
constexpr std::string_view ACCEPT = "Accept";
|
constexpr std::string_view ACCEPT = "Accept";
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include <webserv/handler/URI.hpp> // for URI
|
#include <webserv/handler/URI.hpp> // for URI
|
||||||
#include <webserv/http/HttpRequest.hpp> // for HttpRequest
|
#include <webserv/http/HttpRequest.hpp> // for HttpRequest
|
||||||
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
#include <webserv/log/Log.hpp> // for Log, LOCATION
|
||||||
|
#include <webserv/handler/RedirectHandler.hpp> // for RedirectHandler
|
||||||
|
|
||||||
#include <exception> // for exception
|
#include <exception> // for exception
|
||||||
#include <format> // for vector
|
#include <format> // for vector
|
||||||
@ -61,6 +62,10 @@ std::unique_ptr<AHandler> Router::handleRequest()
|
|||||||
Log::warning("Request validation failed: " + error->message);
|
Log::warning("Request validation failed: " + error->message);
|
||||||
throw RequestValidator::ValidationException{error->statusCode};
|
throw RequestValidator::ValidationException{error->statusCode};
|
||||||
}
|
}
|
||||||
|
if (request.getUri().isRedirect())
|
||||||
|
{
|
||||||
|
return std::make_unique<RedirectHandler>(request, response);
|
||||||
|
}
|
||||||
if (request.getUri().isCgi())
|
if (request.getUri().isCgi())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user