feat(lnm): add most of the writing code
All checks were successful
ci/woodpecker/push/build Pipeline was successful
All checks were successful
ci/woodpecker/push/build Pipeline was successful
This commit is contained in:
parent
77b62825a6
commit
e8bb089f5c
5 changed files with 273 additions and 11 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lnm/http/consts.h"
|
||||
|
|
@ -116,25 +117,139 @@ void lnm_http_loop_process_steps(lnm_http_conn *conn) {
|
|||
ctx->state = lnm_http_loop_state_first_res;
|
||||
break;
|
||||
}
|
||||
} while ((step != ctx->cur_step) && (ctx->cur_step != NULL));
|
||||
}
|
||||
// Loop until we either:
|
||||
// - reach the end of the chain of steps, indicated by NULL
|
||||
// - have a step that's waiting for I/O
|
||||
while ((ctx->cur_step != NULL) && (step != ctx->cur_step));
|
||||
|
||||
if (ctx->cur_step == NULL) {
|
||||
ctx->state = lnm_http_loop_state_write_headers;
|
||||
}
|
||||
}
|
||||
|
||||
// This function is intentionally written inefficiently for now, as it will most
|
||||
// likely only have to run once for each response
|
||||
void lnm_http_loop_process_write_status_line(lnm_http_conn *conn) {
|
||||
lnm_http_loop_ctx *ctx = conn->ctx;
|
||||
lnm_http_res *res = &ctx->res;
|
||||
|
||||
const char *response_type_name =
|
||||
lnm_http_status_names[res->status / 100 - 1][res->status % 100];
|
||||
|
||||
// First we calculate the size of the start of the header
|
||||
size_t buf_size =
|
||||
snprintf(NULL, 0, "HTTP/1.1 %i %s\n", res->status, response_type_name);
|
||||
char buf[buf_size + 1];
|
||||
sprintf(buf, "HTTP/1.1 %i %s\n", res->status, response_type_name);
|
||||
|
||||
size_t to_write =
|
||||
LNM_MIN(buf_size - res->written, LNM_LOOP_BUF_SIZE - conn->w.size);
|
||||
memcpy(&conn->w.buf[conn->w.size], &buf[res->written], to_write);
|
||||
|
||||
conn->w.size += to_write;
|
||||
res->written += to_write;
|
||||
|
||||
if (res->written == buf_size) {
|
||||
res->written = 0;
|
||||
ctx->state = lnm_http_loop_state_write_headers;
|
||||
}
|
||||
}
|
||||
|
||||
void lnm_http_loop_process_write_headers(lnm_http_conn *conn) {
|
||||
lnm_http_loop_ctx *ctx = conn->ctx;
|
||||
lnm_http_res *res = &ctx->res;
|
||||
|
||||
lnm_http_res_header *header = res->headers.current;
|
||||
|
||||
// Loop as long as we can still write new data and have headers to write
|
||||
while ((conn->w.size < LNM_LOOP_BUF_SIZE) &&
|
||||
((header = res->headers.current) != NULL)) {
|
||||
size_t buf_len = header->name.len + 2 + header->value.len + 1;
|
||||
|
||||
// Here, we also constantly calculate the entire buffer as we assume each
|
||||
// header will be written in one go
|
||||
char buf[buf_len];
|
||||
memcpy(buf, header->name.s, header->name.len);
|
||||
memcpy(&buf[header->name.len + 2], header->value.s, header->value.len);
|
||||
buf[header->name.len] = ':';
|
||||
buf[header->name.len + 1] = ' ';
|
||||
buf[buf_len - 1] = '\n';
|
||||
|
||||
size_t to_write =
|
||||
LNM_MIN(buf_len - res->written, LNM_LOOP_BUF_SIZE - conn->w.size);
|
||||
memcpy(&conn->w.buf[conn->w.size], &buf[res->written], to_write);
|
||||
|
||||
conn->w.size += to_write;
|
||||
res->written += to_write;
|
||||
|
||||
if (res->written == buf_len) {
|
||||
res->written = 0;
|
||||
res->headers.current = res->headers.current->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (res->headers.current == NULL) {
|
||||
ctx->state = ctx->res.body.len > 0 ? lnm_http_loop_state_write_body
|
||||
: lnm_http_loop_state_finish;
|
||||
}
|
||||
}
|
||||
|
||||
void lnm_http_loop_process_write_body(lnm_http_conn *conn) {
|
||||
lnm_http_loop_ctx *ctx = conn->ctx;
|
||||
lnm_http_res *res = &ctx->res;
|
||||
|
||||
size_t to_write =
|
||||
LNM_MIN(res->body.len - res->written, LNM_LOOP_BUF_SIZE - conn->w.size);
|
||||
size_t written;
|
||||
|
||||
switch (res->body.type) {
|
||||
case lnm_http_res_body_type_buf:
|
||||
memcpy(&conn->w.buf[conn->w.size], &res->body.data.buf[res->written],
|
||||
to_write);
|
||||
written = to_write;
|
||||
break;
|
||||
case lnm_http_res_body_type_file:
|
||||
written = fread(&conn->w.buf[conn->w.size], 1, to_write, res->body.data.f);
|
||||
|
||||
if ((written == 0) && (!ferror(res->body.data.f))) {
|
||||
ctx->state = lnm_http_loop_state_finish;
|
||||
}
|
||||
break;
|
||||
case lnm_http_res_body_type_fn:
|
||||
if (res->body.data.fn(&written, &conn->w.buf[conn->w.size], conn,
|
||||
res->written, to_write) != lnm_err_ok) {
|
||||
ctx->state = lnm_http_loop_state_finish;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
conn->w.size += written;
|
||||
res->written += written;
|
||||
|
||||
if (res->written == res->body.len) {
|
||||
ctx->state = lnm_http_loop_state_finish;
|
||||
}
|
||||
}
|
||||
|
||||
void lnm_http_loop_process_finish(lnm_http_conn *conn) {}
|
||||
|
||||
void (*process_fns[])(lnm_http_conn *conn) = {
|
||||
lnm_http_loop_process_parse_req,
|
||||
lnm_http_loop_process_route,
|
||||
lnm_http_loop_process_parse_headers,
|
||||
lnm_http_loop_process_steps,
|
||||
lnm_http_loop_process_write_status_line,
|
||||
lnm_http_loop_process_write_headers,
|
||||
lnm_http_loop_process_write_body,
|
||||
lnm_http_loop_process_finish,
|
||||
};
|
||||
|
||||
void lnm_http_loop_process(lnm_http_conn *conn) {
|
||||
lnm_http_loop_ctx *ctx = conn->ctx;
|
||||
|
||||
lnm_http_loop_state http_loop_state;
|
||||
lnm_loop_state loop_state;
|
||||
lnm_loop_state loop_state = conn->state;
|
||||
|
||||
// We stop processing if:
|
||||
// - the event loop state has changed, as we need to switch to the other I/O
|
||||
|
|
@ -143,7 +258,6 @@ void lnm_http_loop_process(lnm_http_conn *conn) {
|
|||
// it's waiting for I/O
|
||||
do {
|
||||
http_loop_state = ctx->state;
|
||||
loop_state = conn->state;
|
||||
|
||||
process_fns[http_loop_state](conn);
|
||||
} while ((conn->state == loop_state) && (http_loop_state != ctx->state));
|
||||
|
|
|
|||
64
lnm/src/http/lnm_http_res.c
Normal file
64
lnm/src/http/lnm_http_res.c
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "lnm/http/res.h"
|
||||
|
||||
lnm_err lnm_http_res_add_header(lnm_http_res *res, lnm_http_header type,
|
||||
char *value, bool value_owned) {
|
||||
return lnm_http_res_add_header_len(res, type, value, strlen(value),
|
||||
value_owned);
|
||||
}
|
||||
|
||||
lnm_err lnm_http_res_add_header_len(lnm_http_res *res, lnm_http_header type,
|
||||
char *value, size_t value_len,
|
||||
bool value_owned) {
|
||||
lnm_http_res_header *header = calloc(1, sizeof(lnm_http_res_header));
|
||||
|
||||
if (header == NULL) {
|
||||
return lnm_err_failed_alloc;
|
||||
}
|
||||
|
||||
lnm_http_res_header **next_ptr = &res->headers.head;
|
||||
|
||||
while ((*next_ptr) != NULL) {
|
||||
next_ptr = &(*next_ptr)->next;
|
||||
}
|
||||
|
||||
*next_ptr = header;
|
||||
|
||||
// Initialize the current pointer to the head of the linked list
|
||||
if (res->headers.current == NULL) {
|
||||
res->headers.current = header;
|
||||
}
|
||||
|
||||
header->name.s = (char *)lnm_http_header_names[type];
|
||||
header->name.len = strlen(lnm_http_header_names[type]);
|
||||
header->name.owned = false;
|
||||
|
||||
header->value.s = value;
|
||||
header->value.len = value_len;
|
||||
header->value.owned = value_owned;
|
||||
|
||||
return lnm_err_ok;
|
||||
}
|
||||
|
||||
void lnm_http_res_body_set_file(lnm_http_res *res, FILE *f, size_t len,
|
||||
bool owned) {
|
||||
res->body.data.f = f;
|
||||
res->body.len = len;
|
||||
res->body.owned = owned;
|
||||
res->body.type = lnm_http_res_body_type_file;
|
||||
}
|
||||
|
||||
void lnm_http_res_body_set_buf(lnm_http_res *res, char *buf, size_t len,
|
||||
bool owned) {
|
||||
res->body.data.buf = buf;
|
||||
res->body.len = len;
|
||||
res->body.owned = owned;
|
||||
res->body.type = lnm_http_res_body_type_buf;
|
||||
}
|
||||
|
||||
void lnm_http_res_body_set_fn(lnm_http_res *res, data_fn fn, size_t len) {
|
||||
res->body.data.fn = fn;
|
||||
res->body.len = len;
|
||||
res->body.type = lnm_http_res_body_type_fn;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue