feat: response bodies from char buffers; generated responses

This commit is contained in:
Jef Roosens 2023-05-29 11:36:57 +02:00
parent 62348c14f5
commit 2fb3e2bb00
Signed by: Jef Roosens
GPG key ID: B75D4F293C7052DB
12 changed files with 163 additions and 108 deletions

View file

@ -21,22 +21,22 @@ bool http_loop_handle_request(event_loop_conn *conn) {
return false;
}
// It's fun to respond with extremely specific error messages
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501
else if (res == http_parse_error_unknown_method) {
http_loop_write_standard_response(conn, 501);
return false;
}
conn->rbuf_read += res;
// If the routing fails, we exit the handler
if (!http_loop_route_request(conn)) {
return false;
// It's fun to respond with extremely specific error messages
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501
if (res == http_parse_error_unknown_method) {
ctx->res.type = http_method_not_implemented;
conn->state = event_loop_conn_state_res;
} else {
http_loop_route_request(conn);
}
}
http_loop_process_request(conn);
if (conn->state == event_loop_conn_state_req) {
http_loop_process_request(conn);
}
// TODO in highly concurrent situations, it might actually be better to always
// return false here, as this allows cycling better through all connections
@ -49,6 +49,7 @@ event_loop *http_loop_init(http_loop_gctx *gctx) {
el->ctx_init = (void *(*)(void *))http_loop_ctx_init;
el->ctx_free = (void (*)(void *))http_loop_ctx_free;
el->handle_data = http_loop_handle_request;
el->write_data = http_loop_write_response;
el->gctx = gctx;
return el;

View file

@ -1,21 +0,0 @@
#include "http.h"
const char http_404[] = "HTTP/1.1 404 Not Found\n"
"Content-Length: 0\n\n";
const size_t http_404_len = sizeof(http_404) - 1;
const char http_405[] = "HTTP/1.1 405 Method Not Allowed\n"
"Content-Length: 0\n\n";
const size_t http_405_len = sizeof(http_405) - 1;
const char http_500[] = "HTTP/1.1 500 Internal Server Error\n"
"Content-Length: 0\n\n";
const size_t http_500_len = sizeof(http_500) - 1;
const char http_501[] = "HTTP/1.1 501 Not Implemented\n"
"Content-Length: 0\n\n";
const size_t http_501_len = sizeof(http_501) - 1;
// Very important that this is in the same order as http_request_method
const char *request_method_names[] = {"GET", "POST", "PUT", "PATCH", "DELETE"};
const size_t request_method_names_len =
sizeof(request_method_names) / sizeof(request_method_names[0]);

View file

@ -14,10 +14,30 @@ http_loop_ctx *http_loop_ctx_init(http_loop_gctx *g) {
return ctx;
}
void http_loop_ctx_free(http_loop_ctx *ctx) { free(ctx); }
void http_loop_ctx_free(http_loop_ctx *ctx) {
http_loop_ctx_reset(ctx);
free(ctx);
}
void http_loop_ctx_reset(http_loop_ctx *ctx) {
ctx->route = NULL;
ctx->current_step = 0;
ctx->flush = false;
if (ctx->res.head != NULL) {
free((void *)ctx->res.head);
ctx->res.head = NULL;
}
if (ctx->res.owns_body && ctx->res.body != NULL) {
free((void *)ctx->res.body);
}
ctx->res.body = NULL;
ctx->res.head_len = 0;
ctx->res.head_written = 0;
ctx->res.body_len = 0;
ctx->res.body_written = 0;
ctx->res.owns_body = false;
}

View file

@ -71,24 +71,7 @@ http_parse_error http_loop_parse_request(event_loop_conn *conn) {
return http_parse_error_ok;
}
void http_loop_process_request(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
// We keep processing step functions as long as they don't need to wait for
// I/O
while ((ctx->route->steps[ctx->current_step] != NULL) &&
ctx->route->steps[ctx->current_step](conn)) {
ctx->current_step++;
}
// If we've reached the end of the list of step functions, we report the
// request as finished by clearing its route
if (ctx->route->steps[ctx->current_step] == NULL) {
http_loop_ctx_reset(ctx);
}
}
bool http_loop_route_request(event_loop_conn *conn) {
void http_loop_route_request(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
http_loop_gctx *gctx = ctx->g;
@ -104,7 +87,7 @@ bool http_loop_route_request(event_loop_conn *conn) {
case http_route_literal:
if (strncmp(route->path, ctx->req.path, ctx->req.path_len) == 0) {
ctx->route = route;
return true;
return;
}
// TODO
case http_route_regex:;
@ -112,7 +95,28 @@ bool http_loop_route_request(event_loop_conn *conn) {
}
// Fallthrough is to write a 404
http_loop_write_standard_response(conn, http_not_found);
return false;
ctx->res.type = http_not_found;
conn->state = event_loop_conn_state_res;
}
void http_loop_process_request(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
// We keep processing step functions as long as they don't need to wait for
// I/O
while ((conn->state == event_loop_conn_state_req) &&
(ctx->route->steps[ctx->current_step] != NULL) &&
ctx->route->steps[ctx->current_step](conn)) {
ctx->current_step++;
}
if (conn->state != event_loop_conn_state_req) {
return;
}
// If we've reached the end of the list of step functions, we report the
// request as finished by clearing its route
if (ctx->route->steps[ctx->current_step] == NULL) {
http_loop_ctx_reset(ctx);
}
}

View file

@ -0,0 +1,58 @@
#include <stdio.h>
#include "http_loop.h"
#include "log.h"
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
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) {
const char *response_type_name =
http_response_type_names[res->type / 100 - 1][res->type % 100];
char *format = "HTTP/1.1 %i %s\n"
"Content-Length: %lu\n\n";
// Running snprintf with size 0 prevents it from writing any bytes, while
// still letting it calculate how many bytes it would have written
int buf_size =
snprintf(NULL, 0, format, res->type, response_type_name, res->body_len);
char *buf = malloc(buf_size + 1);
sprintf(buf, format, res->type, response_type_name, res->body_len);
res->head = buf;
res->head_len = buf_size;
}
// 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_written == 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_written < res->body_len) {
size_t bytes_to_write = MIN(res->body_len - res->body_written,
EVENT_LOOP_BUFFER_SIZE - conn->wbuf_size);
memcpy(&conn->wbuf[conn->wbuf_size], &res->body[res->body_written],
bytes_to_write);
conn->wbuf_size += bytes_to_write;
res->body_written += bytes_to_write;
}
}

View file

@ -1,27 +0,0 @@
#include "http_loop.h"
#include "string.h"
void http_loop_write_standard_response(event_loop_conn *conn,
http_response_type type) {
const char *s;
size_t len;
switch (type) {
case 404:
s = http_404;
len = http_404_len;
break;
case 405:
s = http_405;
len = http_405_len;
case 501:
s = http_501;
len = http_501_len;
}
memcpy(conn->wbuf, s, len);
conn->state = event_loop_conn_state_res;
conn->wbuf_size = len;
conn->wbuf_sent = 0;
}