133 lines
3.2 KiB
C++
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;
|
|
}
|
|
};
|