Compare commits

...

2 Commits

Author SHA1 Message Date
Jef Roosens 13ccfef94d
feat(lnm): implement content-length header parsing
ci/woodpecker/push/build Pipeline was successful Details
2023-12-02 16:28:52 +01:00
Jef Roosens 3c1e62330c
feat(lnm): implement context resetting 2023-12-02 13:24:22 +01:00
8 changed files with 226 additions and 9 deletions

View File

@ -1,6 +1,10 @@
#ifndef LNM_COMMON
#define LNM_COMMON
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#define LNM_RES(x) \
{ \
lnm_err res = x; \
@ -20,13 +24,14 @@
#define LNM_MIN(x, y) ((x) < (y) ? (x) : (y))
#define LNM_MAX(x, y) ((x) > (y) ? (x) : (y))
typedef enum {
typedef enum lnm_err {
lnm_err_ok = 0,
lnm_err_failed_alloc,
lnm_err_failed_network,
lnm_err_failed_poll,
lnm_err_not_setup,
lnm_err_bad_regex
lnm_err_bad_regex,
lnm_err_not_found,
} lnm_err;
typedef struct lnm_loop lnm_http_loop;
@ -37,4 +42,41 @@ typedef struct lnm_http_step lnm_http_step;
typedef struct lnm_http_route lnm_http_route;
/**
* Returns whether the two strings are equal.
*
* @param s1 first string to compare
* @param s1_len length of `s1`
* @param s2 second string to compare
* @param s2_len length of `s2`
*/
bool lnm_strneq(const char *s1, size_t s1_len, const char *s2, size_t s2_len);
/**
* Returns whether the two strings are equal, ignoring capitalisation.
*
* @param s1 first string to compare
* @param s1_len length of `s1`
* @param s2 second string to compare
* @param s2_len length of `s2`
*/
bool lnm_strnieq(const char *s1, size_t s1_len, const char *s2, size_t s2_len);
/**
* Calculate integer exponentation.
*
* @param base
* @param power
*/
uint64_t lnm_ipow(uint64_t base, uint64_t power);
/**
* Parse the given string into a number.
*
* @param s string to parse
* @param len length of s
* @return the parsed number, or 0 if the number is invalid
*/
uint64_t lnm_atoi(const char *s, size_t len);
#endif

View File

@ -2,10 +2,12 @@
#define LNM_HTTP_REQ
#include <regex.h>
#include <stdint.h>
#include <stdlib.h>
#include "picohttpparser.h"
#include "lnm/common.h"
#include "lnm/http/consts.h"
#define LNM_HTTP_MAX_REQ_HEADERS 32
@ -31,6 +33,7 @@ typedef struct lnm_http_req {
struct phr_header arr[LNM_HTTP_MAX_REQ_HEADERS];
size_t len;
} headers;
uint64_t content_length;
} lnm_http_req;
typedef enum lnm_http_parse_err {
@ -49,4 +52,34 @@ typedef enum lnm_http_parse_err {
*/
lnm_http_parse_err lnm_http_req_parse(lnm_http_req *req, char *buf, size_t len);
/**
* Reset the given request object, free'ing all its relevant parts and allowing
* it to be reused as a new object.
*
* @param req object to reset
*/
void lnm_http_req_reset(lnm_http_req *req);
/**
* Retrieve a specific header from the request.
*
* @param out where to write pointer to header value
* @param out_len where to store length of out value
* @param req request to look for header in
* @param type type of header to look for
*/
lnm_err lnm_http_req_header_get(const char **out, size_t *out_len,
lnm_http_req *req, lnm_http_header type);
/**
* Retrieve a specific header from the request by specifying its name.
*
* @param out where to write pointer to header value
* @param out_len where to store length of out value
* @param req request to look for header in
* @param name name of the header; matches case-insensitive
*/
lnm_err lnm_http_req_header_get_s(const char **out, size_t *out_len,
lnm_http_req *req, const char *name);
#endif

View File

@ -108,4 +108,12 @@ void lnm_http_res_body_set_buf(lnm_http_res *res, char *buf, size_t len,
*/
void lnm_http_res_body_set_fn(lnm_http_res *res, data_fn fn, size_t len);
/**
* Reset the given response object, properly free'ing any allocated buffers,
* allowing it to be reused for later connections.
*
* @param res res to reset
*/
void lnm_http_res_reset(lnm_http_res *res);
#endif

View File

@ -38,12 +38,18 @@ lnm_err lnm_http_loop_ctx_init(lnm_http_loop_ctx **out,
void lnm_http_loop_ctx_reset(lnm_http_loop_ctx *ctx) {
ctx->g->ctx_reset(ctx->c);
// TODO actual reset stuff
lnm_http_req_reset(&ctx->req);
lnm_http_res_reset(&ctx->res);
ctx->route = NULL;
ctx->cur_step = NULL;
}
void lnm_http_loop_ctx_free(lnm_http_loop_ctx *ctx) {
lnm_http_loop_ctx_reset(ctx);
ctx->g->ctx_free(ctx->c);
// TODO actual free stuff
free(ctx);
}

View File

@ -91,8 +91,14 @@ void lnm_http_loop_process_route(lnm_http_conn *conn) {
void lnm_http_loop_process_parse_headers(lnm_http_conn *conn) {
lnm_http_loop_ctx *ctx = conn->ctx;
lnm_http_req *req = &ctx->req;
// TODO
const char *value;
size_t value_len;
if (lnm_http_req_header_get(&value, &value_len, req,
lnm_http_header_content_length) == lnm_err_ok) {
req->content_length = lnm_atoi(value, value_len);
}
ctx->state = lnm_http_loop_state_steps;
}
@ -134,6 +140,10 @@ 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;
if (res->status == 0) {
res->status = lnm_http_status_ok;
}
const char *response_type_name =
lnm_http_status_names[res->status / 100 - 1][res->status % 100];
@ -160,7 +170,7 @@ 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;
lnm_http_res_header *header;
// Loop as long as we can still write new data and have headers to write
while ((conn->w.size < LNM_LOOP_BUF_SIZE) &&
@ -212,7 +222,7 @@ void lnm_http_loop_process_write_body(lnm_http_conn *conn) {
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))) {
if ((written == 0) && (ferror(res->body.data.f) != 0)) {
ctx->state = lnm_http_loop_state_finish;
}
break;
@ -232,7 +242,16 @@ void lnm_http_loop_process_write_body(lnm_http_conn *conn) {
}
}
void lnm_http_loop_process_finish(lnm_http_conn *conn) {}
void lnm_http_loop_process_finish(lnm_http_conn *conn) {
// First we ensure the write buffer is fully flushed
if (conn->w.size > 0) {
return;
}
lnm_http_loop_ctx_reset(conn->ctx);
conn->state = lnm_loop_state_req;
}
void (*process_fns[])(lnm_http_conn *conn) = {
lnm_http_loop_process_parse_req,
@ -246,7 +265,7 @@ void (*process_fns[])(lnm_http_conn *conn) = {
};
void lnm_http_loop_process(lnm_http_conn *conn) {
lnm_http_loop_ctx *ctx = conn->ctx;
const lnm_http_loop_ctx *ctx = conn->ctx;
lnm_http_loop_state http_loop_state;
lnm_loop_state loop_state = conn->state;

View File

@ -1,6 +1,7 @@
#include <stdbool.h>
#include <string.h>
#include "lnm/common.h"
#include "lnm/http/consts.h"
#include "lnm/http/loop.h"
#include "lnm/http/req.h"
@ -56,3 +57,31 @@ lnm_http_parse_err lnm_http_req_parse(lnm_http_req *req, char *buf,
return lnm_http_parse_err_ok;
}
void lnm_http_req_reset(lnm_http_req *req) {
memset(req, 0, sizeof(lnm_http_req));
}
lnm_err lnm_http_req_header_get(const char **out, size_t *out_len,
lnm_http_req *req, lnm_http_header type) {
return lnm_http_req_header_get_s(out, out_len, req,
lnm_http_header_names[type]);
}
lnm_err lnm_http_req_header_get_s(const char **out, size_t *out_len,
lnm_http_req *req, const char *name) {
size_t name_len = strlen(name);
for (size_t i = 0; i < req->headers.len; i++) {
const struct phr_header *header = &req->headers.arr[i];
if (lnm_strnieq(header->name, header->name_len, name, name_len)) {
*out = header->value;
*out_len = header->value_len;
return lnm_err_ok;
}
}
return lnm_err_not_found;
}

View File

@ -62,3 +62,38 @@ void lnm_http_res_body_set_fn(lnm_http_res *res, data_fn fn, size_t len) {
res->body.len = len;
res->body.type = lnm_http_res_body_type_fn;
}
void lnm_http_res_reset(lnm_http_res *res) {
lnm_http_res_header *header = res->headers.head;
while (header != NULL) {
lnm_http_res_header *next = header->next;
if (header->name.owned) {
free(header->name.s);
}
if (header->value.owned) {
free(header->value.s);
}
free(header);
header = next;
}
if (res->body.owned) {
switch (res->body.type) {
case lnm_http_res_body_type_file:
fclose(res->body.data.f);
break;
case lnm_http_res_body_type_buf:
free(res->body.data.buf);
break;
case lnm_http_res_body_type_fn:
break;
}
}
memset(res, 0, sizeof(lnm_http_res));
}

View File

@ -0,0 +1,45 @@
#include <stdint.h>
#include <string.h>
#include "lnm/common.h"
bool lnm_strneq(const char *s1, size_t s1_len, const char *s2, size_t s2_len) {
return (s1_len == s2_len) && (memcmp(s1, s2, s1_len) == 0);
}
bool lnm_strnieq(const char *s1, size_t s1_len, const char *s2, size_t s2_len) {
bool equal = s1_len == s2_len;
for (size_t i = 0; i < s1_len && equal; i++) {
equal = s1[i] == s2[i] ||
(('a' <= s1[i]) && (s1[i] <= 'z') && (s1[i] - 32 == s2[i]));
}
return equal;
}
uint64_t lnm_ipow(uint64_t base, uint64_t power) {
uint64_t res = 1;
while (power > 0) {
res *= base;
power--;
}
return res;
}
uint64_t lnm_atoi(const char *s, size_t len) {
uint64_t res = 0;
for (size_t i = 0; i < len; i++) {
if (s[i] < '0' || '9' < s[i]) {
return 0;
}
uint64_t val = s[i] - '0';
res += val * lnm_ipow(10, (len - 1) - i);
}
return res;
}