|
// 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, ¶ms);
|
|
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, ¶ms);
|
|
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(¶ms, 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);
|
|
}
|