feat(http): implement http response class
This commit is contained in:
parent
4d6ad6b1d5
commit
be8c5a80b4
@ -1,5 +1,4 @@
|
|||||||
#include <webserv/client/Client.hpp>
|
#include <webserv/client/Client.hpp>
|
||||||
|
|
||||||
#include <webserv/config/ConfigManager.hpp> // for ConfigManager
|
#include <webserv/config/ConfigManager.hpp> // for ConfigManager
|
||||||
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
||||||
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
|
#include <webserv/handler/ErrorHandler.hpp> // for ErrorHandler
|
||||||
@ -16,7 +15,8 @@
|
|||||||
#include <sys/types.h> // for ssize_t
|
#include <sys/types.h> // for ssize_t
|
||||||
|
|
||||||
Client::Client(std::unique_ptr<Socket> socket, Server &server)
|
Client::Client(std::unique_ptr<Socket> socket, Server &server)
|
||||||
: client_socket_(std::move(socket)), server_(std::ref(server)), httpRequest_(std::make_unique<HttpRequest>(this))
|
: client_socket_(std::move(socket)), server_(std::ref(server)), httpRequest_(std::make_unique<HttpRequest>(this)),
|
||||||
|
httpResponse_(std::make_unique<HttpResponse>(this))
|
||||||
{
|
{
|
||||||
Log::info("New client connected, fd: " + std::to_string(client_socket_->getFd()));
|
Log::info("New client connected, fd: " + std::to_string(client_socket_->getFd()));
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ void Client::request()
|
|||||||
}
|
}
|
||||||
if (bytesRead == 0)
|
if (bytesRead == 0)
|
||||||
{
|
{
|
||||||
Log::info("Client disconnected, fd: " + std::to_string(client_socket_->getFd())); //TODO weird
|
Log::info("Client disconnected, fd: " + std::to_string(client_socket_->getFd())); // TODO weird
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,12 +68,15 @@ void Client::request()
|
|||||||
{"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()))},
|
||||||
});
|
});
|
||||||
server_config_ = ConfigManager::getInstance().getMatchingServerConfig(httpRequest_->getHeaders().get("Host"));
|
server_config_ =
|
||||||
|
ConfigManager::getInstance().getMatchingServerConfig(httpRequest_->getHeaders().getHost().value_or(""));
|
||||||
if (server_config_ == nullptr)
|
if (server_config_ == nullptr)
|
||||||
{
|
{
|
||||||
Log::warning("No matching server config found for Host: " + httpRequest_->getHeaders().get("Host"));
|
Log::warning("No matching server config found for Host: " +
|
||||||
|
httpRequest_->getHeaders().getHost().value_or("unknown host"));
|
||||||
httpRequest_->setState(HttpRequest::State::ParseError);
|
httpRequest_->setState(HttpRequest::State::ParseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example usage, replace with actual host and port extraction from request
|
// Example usage, replace with actual host and port extraction from request
|
||||||
server_.responseReady(client_socket_->getFd());
|
server_.responseReady(client_socket_->getFd());
|
||||||
}
|
}
|
||||||
@ -87,23 +90,31 @@ void Client::request()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Client::getResponse() const
|
bool Client::isResponseReady() const
|
||||||
|
{
|
||||||
|
// todo: poll the httpResponse_ object
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> Client::getResponse() const
|
||||||
{
|
{
|
||||||
Log::trace(LOCATION);
|
Log::trace(LOCATION);
|
||||||
if (httpRequest_->getState() == HttpRequest::State::ParseError)
|
// if (httpRequest_->getState() == HttpRequest::State::ParseError)
|
||||||
{
|
// {
|
||||||
return ErrorHandler::generateErrorPage(Http::StatusCode::BAD_REQUEST);
|
// return ErrorHandler::generateErrorPage(Http::StatusCode::BAD_REQUEST);
|
||||||
}
|
// }
|
||||||
std::string response = "HTTP/1.1 ";
|
// std::string response = "HTTP/1.1 ";
|
||||||
response += "200 OK\r\n";
|
// response += "200 OK\r\n";
|
||||||
|
|
||||||
auto serverName = server_config_->getDirectiveValue<std::string>("server_name");
|
// auto serverName = server_config_->getDirectiveValue<std::string>("server_name");
|
||||||
auto port = server_config_->getDirectiveValue<int>("listen");
|
// auto port = server_config_->getDirectiveValue<int>("listen");
|
||||||
std::string body = "Server Name " + serverName + "\r\n";
|
// std::string body = "Server Name " + serverName + "\r\n";
|
||||||
body += "Server port " + std::to_string(port) + "\r\n";
|
// body += "Server port " + std::to_string(port) + "\r\n";
|
||||||
response += "Content-Length: " + std::to_string(body.size()) + "\r\n\r\n";
|
// response += "Content-Length: " + std::to_string(body.size()) + "\r\n\r\n";
|
||||||
response += body;
|
// response += body;
|
||||||
Log::info("Prepared response for client fd: " + std::to_string(client_socket_->getFd()));
|
// Log::info("Prepared response for client fd: " + std::to_string(client_socket_->getFd()));
|
||||||
Log::debug("Sending response:\n" + response);
|
// Log::debug("Sending response:\n" + response);
|
||||||
return response;
|
httpResponse_->setStatus(200);
|
||||||
|
httpResponse_->addHeader("Content-Type", "text/plain");
|
||||||
|
httpResponse_->appendBody("Hello, World!\n");
|
||||||
|
return httpResponse_->toBytes();
|
||||||
}
|
}
|
||||||
@ -1,5 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// #include "webserv/http/HttpResponse.hpp"
|
||||||
|
|
||||||
|
#include "webserv/http/HttpResponse.hpp"
|
||||||
|
|
||||||
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
||||||
#include <webserv/http/HttpConstants.hpp> // for OK
|
#include <webserv/http/HttpConstants.hpp> // for OK
|
||||||
#include <webserv/http/HttpRequest.hpp> // for HttpRequest
|
#include <webserv/http/HttpRequest.hpp> // for HttpRequest
|
||||||
@ -7,12 +11,15 @@
|
|||||||
#include <webserv/socket/Socket.hpp>
|
#include <webserv/socket/Socket.hpp>
|
||||||
|
|
||||||
#include <cstddef> // for size_t
|
#include <cstddef> // for size_t
|
||||||
|
#include <cstdint>
|
||||||
#include <memory> // for unique_ptr
|
#include <memory> // for unique_ptr
|
||||||
#include <string> // for string
|
#include <string> // for string
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class Server;
|
class Server;
|
||||||
class Socket;
|
class Socket;
|
||||||
class ServerConfig;
|
class ServerConfig;
|
||||||
|
class HttpResponse;
|
||||||
|
|
||||||
class Client
|
class Client
|
||||||
{
|
{
|
||||||
@ -27,15 +34,20 @@ class Client
|
|||||||
~Client();
|
~Client();
|
||||||
|
|
||||||
void request();
|
void request();
|
||||||
[[nodiscard]] std::string getResponse() const;
|
[[nodiscard]] std::vector<uint8_t> getResponse() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isResponseReady() const;
|
||||||
[[nodiscard]] int getStatusCode() const;
|
[[nodiscard]] int getStatusCode() const;
|
||||||
|
|
||||||
|
[[nodiscard]] Socket &getSocket() const { return *client_socket_; }
|
||||||
|
|
||||||
void setStatusCode(int code);
|
void setStatusCode(int code);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int statusCode_ = Http::StatusCode::OK;
|
int statusCode_ = Http::StatusCode::OK;
|
||||||
constexpr static size_t bufferSize_ = 4096;
|
constexpr static size_t bufferSize_ = 4096;
|
||||||
std::unique_ptr<HttpRequest> httpRequest_ = nullptr;
|
std::unique_ptr<HttpRequest> httpRequest_ = nullptr;
|
||||||
|
std::unique_ptr<HttpResponse> httpResponse_ = nullptr;
|
||||||
std::unique_ptr<Socket> client_socket_;
|
std::unique_ptr<Socket> client_socket_;
|
||||||
Server &server_;
|
Server &server_;
|
||||||
mutable const ServerConfig *server_config_ = nullptr;
|
mutable const ServerConfig *server_config_ = nullptr;
|
||||||
|
|||||||
18
webserv/http/HttpConstants.cpp
Normal file
18
webserv/http/HttpConstants.cpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include "HttpConstants.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Http
|
||||||
|
{
|
||||||
|
std::string getStatusCodeReason(uint16_t statusCode)
|
||||||
|
{
|
||||||
|
for (const auto &info : statusCodeInfos)
|
||||||
|
{
|
||||||
|
if (info.code == statusCode)
|
||||||
|
{
|
||||||
|
return std::string(info.reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "Unknown Status";
|
||||||
|
}
|
||||||
|
} // namespace Http
|
||||||
@ -63,6 +63,8 @@ static constexpr std::array<StatusCodeInfo, 12> statusCodeInfos = {
|
|||||||
{.code = StatusCode::BAD_GATEWAY, .reason = "Bad Gateway"},
|
{.code = StatusCode::BAD_GATEWAY, .reason = "Bad Gateway"},
|
||||||
{.code = StatusCode::SERVICE_UNAVAILABLE, .reason = "Service Unavailable"}}};
|
{.code = StatusCode::SERVICE_UNAVAILABLE, .reason = "Service Unavailable"}}};
|
||||||
|
|
||||||
|
std::string getStatusCodeReason(uint16_t statusCode);
|
||||||
|
|
||||||
// Header Names
|
// Header Names
|
||||||
namespace Header
|
namespace Header
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,3 +1,55 @@
|
|||||||
|
#include "webserv/http/HttpConstants.hpp"
|
||||||
|
|
||||||
#include <webserv/http/HttpResponse.hpp>
|
#include <webserv/http/HttpResponse.hpp>
|
||||||
|
|
||||||
HttpResponse::HttpResponse() {}
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
HttpResponse::HttpResponse(Client *client) : client_(client), headers_(std::make_unique<HttpHeaders>()) {}
|
||||||
|
|
||||||
|
void HttpResponse::addHeader(const std::string &key, const std::string &value)
|
||||||
|
{
|
||||||
|
headers_->add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpResponse::appendBody(const std::vector<uint8_t> &data)
|
||||||
|
{
|
||||||
|
body_.insert(body_.end(), data.begin(), data.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpResponse::appendBody(const std::string &body)
|
||||||
|
{
|
||||||
|
body_.insert(body_.end(), body.begin(), body.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpResponse::setStatus(int statusCode)
|
||||||
|
{
|
||||||
|
statusCode_ = statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HttpHeaders &HttpResponse::getHeaders() const
|
||||||
|
{
|
||||||
|
return *headers_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string HttpResponse::getContentLength() const
|
||||||
|
{
|
||||||
|
return "Content-Length: " + std::to_string(body_.size()) + "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> HttpResponse::toBytes() const
|
||||||
|
{
|
||||||
|
std::string headerStr;
|
||||||
|
std::string reason;
|
||||||
|
|
||||||
|
reason = Http::getStatusCodeReason(statusCode_);
|
||||||
|
|
||||||
|
headerStr = "HTTP/1.1 " + std::to_string(statusCode_) + " " + reason + "\r\n"; // todo: status line
|
||||||
|
headerStr += getContentLength();
|
||||||
|
headerStr += headers_->toString();
|
||||||
|
|
||||||
|
std::vector<uint8_t> responseData(headerStr.begin(), headerStr.end());
|
||||||
|
responseData.insert(responseData.end(), body_.begin(), body_.end());
|
||||||
|
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
@ -1,7 +1,48 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "webserv/http/HttpHeaders.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Client;
|
||||||
|
|
||||||
class HttpResponse
|
class HttpResponse
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HttpResponse();
|
HttpResponse(Client *client);
|
||||||
|
|
||||||
|
HttpResponse(const HttpResponse &other) = delete; // Disable copy constructor
|
||||||
|
HttpResponse &operator=(const HttpResponse &other) = delete; // Disable copy assignment
|
||||||
|
HttpResponse(HttpResponse &&other) noexcept = delete; // Move constructor
|
||||||
|
HttpResponse &operator=(HttpResponse &&other) noexcept = delete; // Move assignment
|
||||||
|
|
||||||
|
~HttpResponse() = default;
|
||||||
|
|
||||||
|
void addHeader(const std::string &key, const std::string &value);
|
||||||
|
|
||||||
|
void appendBody(const std::vector<uint8_t> &data);
|
||||||
|
void appendBody(const std::string &body);
|
||||||
|
|
||||||
|
void setComplete();
|
||||||
|
|
||||||
|
void setStatus(int statusCode);
|
||||||
|
|
||||||
|
[[nodiscard]] bool isComplete() const;
|
||||||
|
|
||||||
|
[[nodiscard]] const HttpHeaders &getHeaders() const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<uint8_t> toBytes() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
[[nodiscard]] std::string getStatusLine() const;
|
||||||
|
[[nodiscard]] std::string getContentLength() const;
|
||||||
|
|
||||||
|
Client *client_;
|
||||||
|
std::vector<uint8_t> body_;
|
||||||
|
std::unique_ptr<HttpHeaders> headers_;
|
||||||
|
bool complete_ = false;
|
||||||
|
int statusCode_ = 200;
|
||||||
};
|
};
|
||||||
@ -1,9 +1,8 @@
|
|||||||
#include <webserv/server/Server.hpp>
|
|
||||||
|
|
||||||
#include <webserv/client/Client.hpp> // for Client
|
#include <webserv/client/Client.hpp> // for Client
|
||||||
#include <webserv/config/ConfigManager.hpp> // for ConfigManager
|
#include <webserv/config/ConfigManager.hpp> // for ConfigManager
|
||||||
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
#include <webserv/config/ServerConfig.hpp> // for ServerConfig
|
||||||
#include <webserv/log/Log.hpp> // for Log
|
#include <webserv/log/Log.hpp> // for Log
|
||||||
|
#include <webserv/server/Server.hpp>
|
||||||
#include <webserv/socket/Socket.hpp> // for Socket
|
#include <webserv/socket/Socket.hpp> // for Socket
|
||||||
|
|
||||||
#include <cerrno> // for errno
|
#include <cerrno> // for errno
|
||||||
@ -12,9 +11,9 @@
|
|||||||
#include <memory> // for unique_ptr, allocator, make_unique
|
#include <memory> // for unique_ptr, allocator, make_unique
|
||||||
#include <stdexcept> // for runtime_error
|
#include <stdexcept> // for runtime_error
|
||||||
#include <string> // for basic_string, operator+, to_string, char_traits, string
|
#include <string> // for basic_string, operator+, to_string, char_traits, string
|
||||||
|
#include <unordered_map> // for unordered_map, unordered_map<>::container_type
|
||||||
#include <utility> // for move, pair
|
#include <utility> // for move, pair
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
#include <unordered_map> // for unordered_map, unordered_map<>::container_type
|
|
||||||
|
|
||||||
#include <sys/epoll.h> // for epoll_event, epoll_ctl, EPOLLIN, EPOLLOUT, epoll_create1, epoll_wait, EPOLLERR, EPOLLHUP, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD
|
#include <sys/epoll.h> // for epoll_event, epoll_ctl, EPOLLIN, EPOLLOUT, epoll_create1, epoll_wait, EPOLLERR, EPOLLHUP, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD
|
||||||
#include <sys/socket.h> // for send, SOMAXCONN
|
#include <sys/socket.h> // for send, SOMAXCONN
|
||||||
@ -212,9 +211,8 @@ void Server::eventLoop()
|
|||||||
{
|
{
|
||||||
Log::debug("Attempting to send data to fd: " + std::to_string(event.data.fd));
|
Log::debug("Attempting to send data to fd: " + std::to_string(event.data.fd));
|
||||||
Client &client = getClient(event.data.fd);
|
Client &client = getClient(event.data.fd);
|
||||||
std::string response = client.getResponse();
|
auto httpResponse = client.getResponse();
|
||||||
const char *httpResponse = response.c_str();
|
ssize_t bytesSent = send(event.data.fd, httpResponse.data(), httpResponse.size(), 0);
|
||||||
ssize_t bytesSent = send(event.data.fd, httpResponse, strlen(httpResponse), 0);
|
|
||||||
if (bytesSent < 0)
|
if (bytesSent < 0)
|
||||||
{
|
{
|
||||||
Log::error("Send failed for fd: " + std::to_string(event.data.fd) +
|
Log::error("Send failed for fd: " + std::to_string(event.data.fd) +
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user