kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

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:
include/kore/http.h | 66++++++++++++++++++++++++++++++++++++++++++++++--------------------
include/kore/kore.h | 7+++++--
src/accesslog.c | 3++-
src/config.c | 44+++++++++++++++++++++++++++++++++++++++++++-
src/http.c | 127+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
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); +}