cpp_server/response.hpp

133 lines
3.2 KiB
C++

#pragma once
#include "cookie.hpp"
#include <string>
#include <string_view>
#include <unordered_map>
#include <array>
using std::string_view;
struct Response {
int status = 200;
std::string body;
std::string content_type = "text/plain";
std::unordered_map<std::string, std::string> headers;
void set_json(const std::string& json) {
body = json;
content_type = "application/json";
}
void set_text(const std::string& text) {
body = text;
content_type = "text/plain";
}
void set_html(const std::string& html) {
body = html;
content_type = "text/html";
}
// Cookie helper methods
void set_cookie(string_view name, string_view value, int max_age = -1,
string_view path = "", string_view domain = "", bool secure = false, bool http_only = false) {
std::string cookie_header = CookieHelpers::build_set_cookie(name, value, max_age, path, domain, secure, http_only);
headers["Set-Cookie"] = cookie_header;
}
void delete_cookie(string_view name, string_view path = "") {
std::string cookie_header = CookieHelpers::build_delete_cookie(name, path);
headers["Set-Cookie"] = cookie_header;
}
};
class ResponseBuilder {
private:
static constexpr size_t BUFFER_SIZE = 4096;
static constexpr const char* STATUS_LINES[] = {
"HTTP/1.1 200 OK\r\n",
"HTTP/1.1 201 Created\r\n",
"HTTP/1.1 400 Bad Request\r\n",
"HTTP/1.1 404 Not Found\r\n",
"HTTP/1.1 500 Internal Server Error\r\n"
};
static int get_status_index(int status) {
switch (status) {
case 200: return 0;
case 201: return 1;
case 400: return 2;
case 404: return 3;
case 500: return 4;
default: return -1;
}
}
public:
static std::string build_response(const Response& response, string_view version = "HTTP/1.1") {
std::string result;
result.reserve(BUFFER_SIZE);
// Status line
int status_idx = get_status_index(response.status);
if (status_idx >= 0) {
result += STATUS_LINES[status_idx];
} else {
result += version;
result += " ";
result += std::to_string(response.status);
result += " Unknown\r\n";
}
// Content headers
result += "Content-Type: ";
result += response.content_type;
result += "\r\nContent-Length: ";
result += std::to_string(response.body.size());
result += "\r\n";
// Custom headers
for (const auto& [key, value] : response.headers) {
result += key;
result += ": ";
result += value;
result += "\r\n";
}
// Connection handling
bool keep_alive = version != "HTTP/1.0";
if (response.headers.find("Connection") == response.headers.end()) {
result += keep_alive ? "Connection: keep-alive\r\n" : "Connection: close\r\n";
}
result += "\r\n";
result += response.body;
return result;
}
// Fast path for common responses
static std::string build_error_response(int status, string_view message, string_view version = "HTTP/1.1") {
std::string result;
result.reserve(256);
int status_idx = get_status_index(status);
if (status_idx >= 0) {
result += STATUS_LINES[status_idx];
} else {
result += version;
result += " ";
result += std::to_string(status);
result += " Error\r\n";
}
result += "Content-Type: text/plain\r\nContent-Length: ";
result += std::to_string(message.size());
result += "\r\nConnection: close\r\n\r\n";
result += message;
return result;
}
};