// retoor <retoor@molodetz.nl>
#include "http_response.h"
#include "time_utils.h"
#include "epoll_utils.h"
#include "buffer.h"
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
int http_response_build(const http_response_params_t *params, char *buf, size_t buf_size) {
if (!params || !buf || buf_size == 0) return -1;
char date_buf[64];
time_format_http_date_now(date_buf, sizeof(date_buf));
const char *content_type = params->content_type ? params->content_type : "text/plain; charset=utf-8";
const char *body = params->body ? params->body : "";
size_t body_len = params->body_len > 0 ? params->body_len : strlen(body);
const char *connection = params->keep_alive ? "keep-alive" : "close";
const char *extra = params->extra_headers ? params->extra_headers : "";
int len = snprintf(buf, buf_size,
"HTTP/1.1 %d %s\r\n"
"Content-Type: %s\r\n"
"Content-Length: %zu\r\n"
"Connection: %s\r\n"
"Date: %s\r\n"
"Server: ReverseProxy/4.0\r\n"
"%s"
"\r\n",
params->code, params->status,
content_type, body_len, connection, date_buf, extra);
if (len < 0 || (size_t)len >= buf_size) return -1;
if (body_len > 0 && (size_t)len + body_len < buf_size) {
memcpy(buf + len, body, body_len);
len += (int)body_len;
}
return len;
}
void http_response_send(connection_t *conn, const http_response_params_t *params) {
if (!conn || !params) return;
char response[ERROR_RESPONSE_SIZE];
int len = http_response_build(params, response, sizeof(response));
if (len <= 0) return;
if (buffer_ensure_capacity(&conn->write_buf, conn->write_buf.tail + (size_t)len) == 0) {
memcpy(conn->write_buf.data + conn->write_buf.tail, response, (size_t)len);
conn->write_buf.tail += (size_t)len;
struct epoll_event event = { .data.fd = conn->fd, .events = EPOLLIN | EPOLLOUT };
epoll_ctl(epoll_fd, EPOLL_CTL_MOD, conn->fd, &event);
}
}
void http_response_send_error(connection_t *conn, int code, const char *status, const char *body) {
if (!conn || !status || !body) return;
http_response_params_t params = {
.code = code,
.status = status,
.body = body,
.keep_alive = 0
};
http_response_send(conn, &params);
conn->state = CLIENT_STATE_ERROR;
conn->request.keep_alive = 0;
}
void http_response_send_auth_required(connection_t *conn, const char *realm) {
if (!conn) return;
char extra_headers[256];
snprintf(extra_headers, sizeof(extra_headers),
"WWW-Authenticate: Basic realm=\"%s\"\r\n",
realm ? realm : "Protected Area");
http_response_params_t params = {
.code = 401,
.status = "Unauthorized",
.body = "401 Unauthorized - Authentication required",
.extra_headers = extra_headers,
.keep_alive = 0
};
http_response_send(conn, &params);
conn->state = CLIENT_STATE_ERROR;
conn->request.keep_alive = 0;
}
void http_response_send_pipeline_rejected(connection_t *conn) {
if (!conn) return;
http_response_params_t params = {
.code = 400,
.status = "Bad Request",
.body = "400 Bad Request - Request pipelining is not supported",
.keep_alive = 0
};
char response[ERROR_RESPONSE_SIZE];
int len = http_response_build(&params, response, sizeof(response));
if (len <= 0) return;
if (buffer_ensure_capacity(&conn->write_buf, conn->write_buf.tail + (size_t)len) == 0) {
memcpy(conn->write_buf.data + conn->write_buf.tail, response, (size_t)len);
conn->write_buf.tail += (size_t)len;
}
conn->state = CLIENT_STATE_CLOSING;
conn->request.keep_alive = 0;
struct epoll_event event = { .data.fd = conn->fd, .events = EPOLLOUT };
epoll_ctl(epoll_fd, EPOLL_CTL_MOD, conn->fd, &event);
}