commit f5a58368b7ea9bf99e95ae24fcb235babc80dafc
parent e98a4ddab58d4e8dce21a4dc512f05cc278685d8
Author: Joris Vink <joris@coders.se>
Date: Wed, 15 Sep 2021 22:16:22 +0200
HTTP improvements.
Introduce an on_headers callback for routes, allowing one to inspect
the headers before the request is processed further.
Additionall,
Add a new way of obtaining HTTP headers. Much like http_argument_get_*()
functions, these new APIs allow you to fetch the data of an HTTP header
as a specified C type.
The new APIs are:
* http_request_header_int16()
* http_request_header_uint16()
* http_request_header_int32()
* http_request_header_uint32()
* http_request_header_int64()
* http_request_header_uint64()
* http_request_header_float()
* http_request_header_double()
Should make it easier to operate in HTTP header data in a safe way.
No need to always roll your own string to int conversion functions.
Diffstat:
5 files changed, 172 insertions(+), 75 deletions(-)
diff --git a/include/kore/http.h b/include/kore/http.h
@@ -119,7 +119,7 @@ struct http_arg {
do { \
int err; \
type nval; \
- nval = (type)kore_strtonum64(q->s_value, sign, &err); \
+ nval = (type)kore_strtonum64(data, sign, &err); \
if (err != KORE_RESULT_OK) \
return (KORE_RESULT_ERROR); \
COPY_ARG_TYPE(nval, type); \
@@ -129,7 +129,7 @@ struct http_arg {
do { \
int err; \
type nval; \
- nval = kore_strtodouble(q->s_value, min, max, &err); \
+ nval = kore_strtodouble(data, min, max, &err); \
if (err != KORE_RESULT_OK) \
return (KORE_RESULT_ERROR); \
COPY_ARG_TYPE(nval, type); \
@@ -139,7 +139,7 @@ struct http_arg {
do { \
int err; \
int64_t nval; \
- nval = kore_strtonum(q->s_value, 10, min, max, &err); \
+ nval = kore_strtonum(data, 10, min, max, &err); \
if (err != KORE_RESULT_OK) \
return (KORE_RESULT_ERROR); \
COPY_ARG_TYPE(nval, type); \
@@ -159,38 +159,62 @@ struct http_arg {
COPY_ARG_INT(min, max, type); \
} while (0)
-#define http_argument_type(r, n, so, no, t) \
- http_argument_get(r, n, so, no, t)
-
#define http_argument_get_string(r, n, o) \
- http_argument_type(r, n, (void **)o, NULL, HTTP_ARG_TYPE_STRING)
+ http_argument_get(r, n, (void **)o, NULL, HTTP_ARG_TYPE_STRING)
-#define http_argument_get_byte(r, n, o) \
- http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_BYTE)
+#define http_argument_get_byte(r, n, o) \
+ http_argument_get(r, n, NULL, o, HTTP_ARG_TYPE_BYTE)
#define http_argument_get_uint16(r, n, o) \
- http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_UINT16)
+ http_argument_get(r, n, NULL, o, HTTP_ARG_TYPE_UINT16)
#define http_argument_get_int16(r, n, o) \
- http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_INT16)
+ http_argument_get(r, n, NULL, o, HTTP_ARG_TYPE_INT16)
#define http_argument_get_uint32(r, n, o) \
- http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_UINT32)
+ http_argument_get(r, n, NULL, o, HTTP_ARG_TYPE_UINT32)
#define http_argument_get_int32(r, n, o) \
- http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_INT32)
+ http_argument_get(r, n, NULL, o, HTTP_ARG_TYPE_INT32)
#define http_argument_get_uint64(r, n, o) \
- http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_UINT64)
+ http_argument_get(r, n, NULL, o, HTTP_ARG_TYPE_UINT64)
#define http_argument_get_int64(r, n, o) \
- http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_INT64)
+ http_argument_get(r, n, NULL, o, HTTP_ARG_TYPE_INT64)
#define http_argument_get_float(r, n, o) \
- http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_FLOAT)
+ http_argument_get(r, n, NULL, o, HTTP_ARG_TYPE_FLOAT)
#define http_argument_get_double(r, n, o) \
- http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_DOUBLE)
+ http_argument_get(r, n, NULL, o, HTTP_ARG_TYPE_DOUBLE)
+
+#define http_request_header_byte(r, n, o) \
+ http_request_header_get(r, n, NULL, o, HTTP_ARG_TYPE_BYTE)
+
+#define http_request_header_uint16(r, n, o) \
+ http_request_header_get(r, n, NULL, o, HTTP_ARG_TYPE_UINT16)
+
+#define http_request_header_int16(r, n, o) \
+ http_request_header_get(r, n, NULL, o, HTTP_ARG_TYPE_INT16)
+
+#define http_request_header_uint32(r, n, o) \
+ http_request_header_get(r, n, NULL, o, HTTP_ARG_TYPE_UINT32)
+
+#define http_request_header_int32(r, n, o) \
+ http_request_header_get(r, n, NULL, o, HTTP_ARG_TYPE_INT32)
+
+#define http_request_header_uint64(r, n, o) \
+ http_request_header_get(r, n, NULL, o, HTTP_ARG_TYPE_UINT64)
+
+#define http_request_header_int64(r, n, o) \
+ http_request_header_get(r, n, NULL, o, HTTP_ARG_TYPE_INT64)
+
+#define http_request_header_float(r, n, o) \
+ http_request_header_get(r, n, NULL, o, HTTP_ARG_TYPE_FLOAT)
+
+#define http_request_header_double(r, n, o) \
+ http_request_header_get(r, n, NULL, o, HTTP_ARG_TYPE_DOUBLE)
struct http_file {
char *name;
@@ -262,9 +286,9 @@ struct http_request {
struct kore_buf *http_body;
int http_body_fd;
char *http_body_path;
- size_t http_body_length;
- size_t http_body_offset;
- size_t content_length;
+ u_int64_t http_body_length;
+ u_int64_t http_body_offset;
+ u_int64_t content_length;
void *hdlr_extra;
size_t state_len;
char *query_string;
@@ -400,6 +424,8 @@ void http_populate_multipart_form(struct http_request *);
void http_populate_cookies(struct http_request *);
int http_argument_get(struct http_request *,
const char *, void **, void *, int);
+int http_request_header_get(struct http_request *,
+ const char *, void **, void *, int);
void http_file_rewind(struct http_file *);
ssize_t http_file_read(struct http_file *, void *, size_t);
diff --git a/include/kore/kore.h b/include/kore/kore.h
@@ -320,11 +320,14 @@ struct kore_route {
char *func;
int type;
int errors;
+ int methods;
regex_t rctx;
struct kore_domain *dom;
- struct kore_runtime_call *rcall;
struct kore_auth *auth;
- int methods;
+ struct kore_runtime_call *rcall;
+ struct kore_runtime_call *on_headers;
+ struct kore_runtime_call *on_body_chunk;
+
TAILQ_HEAD(, kore_route_params) params;
TAILQ_ENTRY(kore_route) list;
};
diff --git a/src/accesslog.c b/src/accesslog.c
@@ -18,6 +18,7 @@
#include <sys/socket.h>
#include <time.h>
+#include <inttypes.h>
#include <signal.h>
#include "kore.h"
@@ -174,7 +175,7 @@ kore_accesslog(struct http_request *req)
worker->lb.offset += sizeof(*hdr);
len = snprintf(worker->lb.buf + worker->lb.offset, avail,
- "%s - %s [%s] \"%s %s %s\" %d %zu \"%s\" \"%s\"\n",
+ "%s - %s [%s] \"%s %s %s\" %d %" PRIu64" \"%s\" \"%s\"\n",
addr, cn, tbuf, method, req->path, http_version,
req->status, req->content_length, referer, req->agent);
if (len == -1)
diff --git a/src/config.c b/src/config.c
@@ -109,8 +109,10 @@ static int configure_client_verify_depth(char *);
#if !defined(KORE_NO_HTTP)
static int configure_route(char *);
-static int configure_route_handler(char *);
static int configure_route_methods(char *);
+static int configure_route_handler(char *);
+static int configure_route_on_headers(char *);
+static int configure_route_on_body_chunk(char *);
static int configure_filemap(char *);
static int configure_return(char *);
static int configure_redirect(char *);
@@ -191,6 +193,8 @@ static struct {
#if !defined(KORE_NO_HTTP)
{ "route", configure_route },
{ "handler", configure_route_handler },
+ { "on_headers", configure_route_on_headers },
+ { "on_body_chunk", configure_route_on_body_chunk },
{ "methods", configure_route_methods },
{ "filemap", configure_filemap },
{ "redirect", configure_redirect },
@@ -1156,6 +1160,44 @@ configure_route_handler(char *name)
}
static int
+configure_route_on_headers(char *name)
+{
+ if (current_route == NULL) {
+ kore_log(LOG_ERR,
+ "on_header keyword not inside of route context");
+ return (KORE_RESULT_ERROR);
+ }
+
+ if ((current_route->on_headers = kore_runtime_getcall(name)) == NULL) {
+ kore_log(LOG_ERR, "on_headers callback '%s' for '%s' not found",
+ name, current_route->path);
+ return (KORE_RESULT_ERROR);
+ }
+
+ return (KORE_RESULT_OK);
+}
+
+static int
+configure_route_on_body_chunk(char *name)
+{
+ if (current_route == NULL) {
+ kore_log(LOG_ERR,
+ "on_body_chunk keyword not inside of route context");
+ return (KORE_RESULT_ERROR);
+ }
+
+ current_route->on_body_chunk = kore_runtime_getcall(name);
+ if (current_route->on_body_chunk == NULL) {
+ kore_log(LOG_ERR,
+ "on_body_chunk callback '%s' for '%s' not found",
+ name, current_route->path);
+ return (KORE_RESULT_ERROR);
+ }
+
+ return (KORE_RESULT_OK);
+}
+
+static int
configure_route_methods(char *options)
{
int i, cnt;
diff --git a/src/http.c b/src/http.c
@@ -130,6 +130,7 @@ static const char *pretty_error_fmt =
static int http_body_recv(struct netbuf *);
static int http_release_buffer(struct netbuf *);
static void http_error_response(struct connection *, int);
+static int http_data_convert(void *, void **, void *, int);
static void http_write_response_cookie(struct http_cookie *);
static void http_argument_add(struct http_request *, char *, char *,
int, int);
@@ -764,6 +765,28 @@ http_request_header(struct http_request *req, const char *header,
}
int
+http_request_header_get(struct http_request *req, const char *header,
+ void **out, void *nout, int type)
+{
+ struct http_header *hdr;
+
+ if (type == HTTP_ARG_TYPE_STRING)
+ fatal("%s: cannot be called with type string", __func__);
+
+ TAILQ_FOREACH(hdr, &req->req_headers, list) {
+ if (strcasecmp(hdr->header, header))
+ continue;
+
+ if (http_data_convert(hdr->value, out, nout, type))
+ return (KORE_RESULT_OK);
+
+ return (KORE_RESULT_ERROR);
+ }
+
+ return (KORE_RESULT_ERROR);
+}
+
+int
http_request_cookie(struct http_request *req, const char *cookie, char **out)
{
struct http_cookie *ck;
@@ -786,7 +809,6 @@ http_header_recv(struct netbuf *nb)
ssize_t ret;
struct http_header *hdr;
struct http_request *req;
- const char *clp;
u_int64_t bytes_left;
u_int8_t *end_headers;
int h, i, v, skip, l;
@@ -897,17 +919,8 @@ http_header_recv(struct netbuf *nb)
return (KORE_RESULT_OK);
}
- if (!http_request_header(req, "content-length", &clp)) {
- kore_debug("expected body but no content-length");
- req->flags |= HTTP_REQUEST_DELETE;
- http_error_response(req->owner,
- HTTP_STATUS_LENGTH_REQUIRED);
- return (KORE_RESULT_OK);
- }
-
- req->content_length = kore_strtonum(clp, 10, 0, LONG_MAX, &v);
- if (v == KORE_RESULT_ERROR) {
- kore_debug("content-length invalid: %s", clp);
+ if (!http_request_header_uint64(req, "content-length",
+ &req->content_length)) {
req->flags |= HTTP_REQUEST_DELETE;
http_error_response(req->owner,
HTTP_STATUS_LENGTH_REQUIRED);
@@ -921,8 +934,6 @@ http_header_recv(struct netbuf *nb)
}
if (req->content_length > http_body_max) {
- kore_log(LOG_NOTICE, "body too large (%zu > %zu)",
- req->content_length, http_body_max);
req->flags |= HTTP_REQUEST_DELETE;
http_error_response(req->owner,
HTTP_STATUS_REQUEST_ENTITY_TOO_LARGE);
@@ -997,6 +1008,13 @@ http_header_recv(struct netbuf *nb)
c->http_timeout = 0;
}
+ if (req->rt->on_headers != NULL) {
+ if (!kore_runtime_http_request(req->rt->on_headers, req)) {
+ req->flags |= HTTP_REQUEST_DELETE;
+ return (KORE_RESULT_OK);
+ }
+ }
+
return (KORE_RESULT_OK);
}
@@ -1010,45 +1028,10 @@ http_argument_get(struct http_request *req, const char *name,
if (strcmp(q->name, name))
continue;
- switch (type) {
- case HTTP_ARG_TYPE_RAW:
- *out = q->s_value;
- return (KORE_RESULT_OK);
- case HTTP_ARG_TYPE_BYTE:
- COPY_ARG_TYPE(*(u_int8_t *)q->s_value, u_int8_t);
- return (KORE_RESULT_OK);
- case HTTP_ARG_TYPE_INT16:
- COPY_AS_INTTYPE(SHRT_MIN, SHRT_MAX, int16_t);
- return (KORE_RESULT_OK);
- case HTTP_ARG_TYPE_UINT16:
- COPY_AS_INTTYPE(0, USHRT_MAX, u_int16_t);
- return (KORE_RESULT_OK);
- case HTTP_ARG_TYPE_INT32:
- COPY_AS_INTTYPE(INT_MIN, INT_MAX, int32_t);
+ if (http_data_convert(q->s_value, out, nout, type))
return (KORE_RESULT_OK);
- case HTTP_ARG_TYPE_UINT32:
- COPY_AS_INTTYPE(0, UINT_MAX, u_int32_t);
- return (KORE_RESULT_OK);
- case HTTP_ARG_TYPE_INT64:
- COPY_AS_INTTYPE_64(int64_t, 1);
- return (KORE_RESULT_OK);
- case HTTP_ARG_TYPE_UINT64:
- COPY_AS_INTTYPE_64(u_int64_t, 0);
- return (KORE_RESULT_OK);
- case HTTP_ARG_TYPE_FLOAT:
- COPY_ARG_DOUBLE(-FLT_MAX, FLT_MAX, float);
- return (KORE_RESULT_OK);
- case HTTP_ARG_TYPE_DOUBLE:
- COPY_ARG_DOUBLE(-DBL_MAX, DBL_MAX, double);
- return (KORE_RESULT_OK);
- case HTTP_ARG_TYPE_STRING:
- *out = q->s_value;
- return (KORE_RESULT_OK);
- default:
- break;
- }
- return (KORE_RESULT_ERROR);
+ break;
}
return (KORE_RESULT_ERROR);
@@ -2598,3 +2581,45 @@ http_write_response_cookie(struct http_cookie *ck)
kore_buf_appendf(header_buf, "set-cookie: %s\r\n",
kore_buf_stringify(ckhdr_buf, NULL));
}
+
+static int
+http_data_convert(void *data, void **out, void *nout, int type)
+{
+ switch (type) {
+ case HTTP_ARG_TYPE_RAW:
+ case HTTP_ARG_TYPE_STRING:
+ *out = data;
+ return (KORE_RESULT_OK);
+ case HTTP_ARG_TYPE_BYTE:
+ COPY_ARG_TYPE(*(u_int8_t *)data, u_int8_t);
+ return (KORE_RESULT_OK);
+ case HTTP_ARG_TYPE_INT16:
+ COPY_AS_INTTYPE(SHRT_MIN, SHRT_MAX, int16_t);
+ return (KORE_RESULT_OK);
+ case HTTP_ARG_TYPE_UINT16:
+ COPY_AS_INTTYPE(0, USHRT_MAX, u_int16_t);
+ return (KORE_RESULT_OK);
+ case HTTP_ARG_TYPE_INT32:
+ COPY_AS_INTTYPE(INT_MIN, INT_MAX, int32_t);
+ return (KORE_RESULT_OK);
+ case HTTP_ARG_TYPE_UINT32:
+ COPY_AS_INTTYPE(0, UINT_MAX, u_int32_t);
+ return (KORE_RESULT_OK);
+ case HTTP_ARG_TYPE_INT64:
+ COPY_AS_INTTYPE_64(int64_t, 1);
+ return (KORE_RESULT_OK);
+ case HTTP_ARG_TYPE_UINT64:
+ COPY_AS_INTTYPE_64(u_int64_t, 0);
+ return (KORE_RESULT_OK);
+ case HTTP_ARG_TYPE_FLOAT:
+ COPY_ARG_DOUBLE(-FLT_MAX, FLT_MAX, float);
+ return (KORE_RESULT_OK);
+ case HTTP_ARG_TYPE_DOUBLE:
+ COPY_ARG_DOUBLE(-DBL_MAX, DBL_MAX, double);
+ return (KORE_RESULT_OK);
+ default:
+ break;
+ }
+
+ return (KORE_RESULT_ERROR);
+}