99 lines
3.3 KiB
C
99 lines
3.3 KiB
C
#include "http_loop.h"
|
|
#include "log.h"
|
|
|
|
static const char *http_response_format = "HTTP/1.1 %i %s\n"
|
|
"Server: lander/" LANDER_VERSION "\n"
|
|
"Content-Length: %lu\n";
|
|
|
|
/*
|
|
* This function precalculates the size of the total buffer required using
|
|
* snprintf. When this function is called with a buf size of 0, it never tries
|
|
* to write any data, but it does return the amount of bytes that would be
|
|
* written.
|
|
*/
|
|
void http_loop_init_header(http_response *res) {
|
|
if (res->status == 0) {
|
|
res->status = http_ok;
|
|
}
|
|
|
|
const char *response_type_name =
|
|
http_status_names[res->status / 100 - 1][res->status % 100];
|
|
|
|
// First we calculate the size of the start of the header
|
|
int buf_size = snprintf(NULL, 0, http_response_format, res->status,
|
|
response_type_name, res->body.expected_len);
|
|
|
|
// We add each header's required size
|
|
for (size_t i = 0; i < res->header_count; i++) {
|
|
buf_size +=
|
|
snprintf(NULL, 0, "%s: %s\n", http_header_names[res->headers[i].type],
|
|
res->headers[i].value);
|
|
}
|
|
|
|
// The + 1 is required to store the final null byte, but we will replace it
|
|
// with the required final newline
|
|
char *buf = malloc(buf_size + 1);
|
|
buf_size = sprintf(buf, http_response_format, res->status, response_type_name,
|
|
res->body.expected_len);
|
|
|
|
for (size_t i = 0; i < res->header_count; i++) {
|
|
buf_size +=
|
|
sprintf(&buf[buf_size], "%s: %s\n",
|
|
http_header_names[res->headers[i].type], res->headers[i].value);
|
|
}
|
|
|
|
buf[buf_size] = '\n';
|
|
|
|
res->head = buf;
|
|
res->head_len = buf_size + 1;
|
|
}
|
|
|
|
void http_loop_write_response(event_loop_conn *conn) {
|
|
http_response *res = &((http_loop_ctx *)conn->ctx)->res;
|
|
|
|
// Create head response
|
|
if (res->head == NULL) {
|
|
http_loop_init_header(res);
|
|
}
|
|
|
|
// The final iteration marks the end of the response, after which we reset the
|
|
// context so a next request can be processed
|
|
if (res->head_written == res->head_len &&
|
|
res->body.expected_len == res->body.len) {
|
|
http_loop_ctx_reset(conn->ctx);
|
|
conn->state = event_loop_conn_state_req;
|
|
return;
|
|
}
|
|
|
|
if (res->head_written < res->head_len) {
|
|
size_t bytes_to_write = MIN(res->head_len - res->head_written,
|
|
EVENT_LOOP_BUFFER_SIZE - conn->wbuf_size);
|
|
memcpy(&conn->wbuf[conn->wbuf_size], &res->head[res->head_written],
|
|
bytes_to_write);
|
|
|
|
conn->wbuf_size += bytes_to_write;
|
|
res->head_written += bytes_to_write;
|
|
}
|
|
|
|
if (res->body.len < res->body.expected_len) {
|
|
size_t bytes_to_write = MIN(res->body.expected_len - res->body.len,
|
|
EVENT_LOOP_BUFFER_SIZE - conn->wbuf_size);
|
|
size_t bytes_written;
|
|
|
|
switch (res->body.type) {
|
|
case http_body_buf:
|
|
memcpy(&conn->wbuf[conn->wbuf_size], &(res->body.buf)[res->body.len],
|
|
bytes_to_write);
|
|
conn->wbuf_size += bytes_to_write;
|
|
res->body.len += bytes_to_write;
|
|
break;
|
|
case http_body_file:
|
|
bytes_written = fread(&conn->wbuf[conn->wbuf_size], sizeof(uint8_t),
|
|
bytes_to_write, res->body.file);
|
|
conn->wbuf_size += bytes_written;
|
|
res->body.len += bytes_written;
|
|
break;
|
|
}
|
|
}
|
|
}
|