feat: add step to receive request body in buffer
parent
ff0795bb55
commit
16103f9b24
2
Makefile
2
Makefile
|
@ -50,7 +50,7 @@ objs: $(OBJS)
|
||||||
.PHONY: bin
|
.PHONY: bin
|
||||||
bin: $(BIN)
|
bin: $(BIN)
|
||||||
$(BIN): $(OBJS)
|
$(BIN): $(OBJS)
|
||||||
$(CC) $(INTERNALCFLAGS) $(LDFLAGS) -o $@ $^
|
$(CC) $(INTERNALCFLAGS) $(LDFLAGS) -lm -o $@ $^
|
||||||
|
|
||||||
$(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c
|
$(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c
|
||||||
mkdir -p $(dir $@)
|
mkdir -p $(dir $@)
|
||||||
|
|
|
@ -35,8 +35,12 @@ typedef struct http_request {
|
||||||
size_t path_len;
|
size_t path_len;
|
||||||
const char *query;
|
const char *query;
|
||||||
size_t query_len;
|
size_t query_len;
|
||||||
|
char *body;
|
||||||
|
size_t body_len;
|
||||||
|
size_t body_received;
|
||||||
regmatch_t regex_groups[HTTP_MAX_REGEX_GROUPS];
|
regmatch_t regex_groups[HTTP_MAX_REGEX_GROUPS];
|
||||||
struct phr_header headers[HTTP_MAX_ALLOWED_HEADERS];
|
struct phr_header headers[HTTP_MAX_ALLOWED_HEADERS];
|
||||||
|
size_t num_headers;
|
||||||
} http_request;
|
} http_request;
|
||||||
|
|
||||||
typedef enum http_parse_error {
|
typedef enum http_parse_error {
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "trie.h"
|
#include "trie.h"
|
||||||
|
|
||||||
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||||
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
|
|
||||||
typedef enum http_route_type {
|
typedef enum http_route_type {
|
||||||
http_route_literal = 0,
|
http_route_literal = 0,
|
||||||
http_route_regex = 1,
|
http_route_regex = 1,
|
||||||
|
@ -102,6 +105,11 @@ void http_loop_res_set_body_file(http_loop_ctx *ctx, const char *filename);
|
||||||
void http_loop_res_add_header(http_loop_ctx *ctx, http_header type,
|
void http_loop_res_add_header(http_loop_ctx *ctx, http_header type,
|
||||||
const char *value, bool owned);
|
const char *value, bool owned);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Request step that consumes the request body and stores it in a buffer
|
||||||
|
*/
|
||||||
|
bool http_loop_step_body_to_buf(event_loop_conn *conn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a new http loop
|
* Initialize a new http loop
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include "http_loop.h"
|
#include "http_loop.h"
|
||||||
|
|
||||||
extern http_route lander_routes[2];
|
extern http_route lander_routes[3];
|
||||||
|
|
||||||
|
bool lander_post_redirect(event_loop_conn *conn);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,6 +26,11 @@ void http_loop_ctx_reset(http_loop_ctx *ctx) {
|
||||||
ctx->route = NULL;
|
ctx->route = NULL;
|
||||||
ctx->current_step = 0;
|
ctx->current_step = 0;
|
||||||
|
|
||||||
|
if (ctx->req.body != NULL) {
|
||||||
|
free((void *)ctx->req.body);
|
||||||
|
ctx->req.body = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->res.head != NULL) {
|
if (ctx->res.head != NULL) {
|
||||||
free((void *)ctx->res.head);
|
free((void *)ctx->res.head);
|
||||||
ctx->res.head = NULL;
|
ctx->res.head = NULL;
|
||||||
|
|
|
@ -27,6 +27,7 @@ http_parse_error http_loop_parse_request(event_loop_conn *conn) {
|
||||||
return http_parse_error_incomplete;
|
return http_parse_error_incomplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req->num_headers = num_headers;
|
||||||
req->len = res;
|
req->len = res;
|
||||||
|
|
||||||
// Try to parse the method type
|
// Try to parse the method type
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#include "http_loop.h"
|
#include "http_loop.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
|
||||||
|
|
||||||
static const char *http_response_format = "HTTP/1.1 %i %s\n"
|
static const char *http_response_format = "HTTP/1.1 %i %s\n"
|
||||||
"Server: lander/" LANDER_VERSION "\n"
|
"Server: lander/" LANDER_VERSION "\n"
|
||||||
"Content-Length: %lu\n";
|
"Content-Length: %lu\n";
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
@ -31,3 +32,68 @@ void http_loop_res_add_header(http_loop_ctx *ctx, http_header type,
|
||||||
|
|
||||||
ctx->res.header_count++;
|
ctx->res.header_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts a string to a number, returning true if the string contained a valid
|
||||||
|
* positive number.
|
||||||
|
*/
|
||||||
|
static bool string_to_num(size_t *res, const char *s, size_t len) {
|
||||||
|
*res = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
int val = s[i] - '0';
|
||||||
|
|
||||||
|
if (val < 0 || val > 9) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*res += val * (int)pow(10, (len - 1) - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool http_loop_step_body_to_buf(event_loop_conn *conn) {
|
||||||
|
http_loop_ctx *ctx = conn->ctx;
|
||||||
|
|
||||||
|
if (ctx->req.body_len == 0) {
|
||||||
|
for (size_t i = 0; i < ctx->req.num_headers; i++) {
|
||||||
|
struct phr_header *header = &ctx->req.headers[i];
|
||||||
|
|
||||||
|
if (strncmp(header->name, "Content-Length", header->name_len) == 0) {
|
||||||
|
// If the content length header is present but contains an invalid
|
||||||
|
// number, we return a bad request error
|
||||||
|
if (!string_to_num(&ctx->req.body_len, header->value,
|
||||||
|
header->value_len)) {
|
||||||
|
ctx->res.status = http_bad_request;
|
||||||
|
conn->state = event_loop_conn_state_res;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// The content length was actually 0, so we can instantly return here
|
||||||
|
else if (ctx->req.body_len == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A zero here means there's no content length header
|
||||||
|
if (ctx->req.body_len == 0) {
|
||||||
|
ctx->res.status = http_length_required;
|
||||||
|
conn->state = event_loop_conn_state_res;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->req.body = malloc(ctx->req.body_len * sizeof(uint8_t));
|
||||||
|
ctx->req.body_received = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bytes_to_copy = MIN(conn->rbuf_size - conn->rbuf_read,
|
||||||
|
ctx->req.body_len - ctx->req.body_received);
|
||||||
|
memcpy(&ctx->req.body[ctx->req.body_received], &conn->rbuf[conn->rbuf_read],
|
||||||
|
bytes_to_copy);
|
||||||
|
ctx->req.body_received += bytes_to_copy;
|
||||||
|
|
||||||
|
return ctx->req.body_received == ctx->req.body_len;
|
||||||
|
}
|
||||||
|
|
|
@ -47,11 +47,16 @@ bool lander_get_entry(event_loop_conn *conn) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
http_route lander_routes[] = {{.type = http_route_literal,
|
http_route lander_routes[] = {
|
||||||
|
{.type = http_route_literal,
|
||||||
.method = http_get,
|
.method = http_get,
|
||||||
.path = "/",
|
.path = "/",
|
||||||
.steps = {lander_get_index, NULL}},
|
.steps = {lander_get_index, NULL}},
|
||||||
{.type = http_route_regex,
|
{.type = http_route_regex,
|
||||||
.method = http_get,
|
.method = http_get,
|
||||||
.path = "^/\\([^/]\\+\\)$",
|
.path = "^/\\([^/]\\+\\)$",
|
||||||
.steps = {lander_get_entry, NULL}}};
|
.steps = {lander_get_entry, NULL}},
|
||||||
|
{.type = http_route_literal,
|
||||||
|
.method = http_post,
|
||||||
|
.path = "/s/",
|
||||||
|
.steps = {http_loop_step_body_to_buf, lander_post_redirect, NULL}}};
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include "lander.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
bool lander_post_redirect(event_loop_conn *conn) {
|
||||||
|
http_loop_ctx *ctx = conn->ctx;
|
||||||
|
|
||||||
|
info("%.*s", ctx->req.body_len, ctx->req.body);
|
||||||
|
|
||||||
|
conn->state = event_loop_conn_state_res;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
Loading…
Reference in New Issue