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.
This commit is contained in:
Joris Vink 2021-09-15 22:16:22 +02:00
parent e98a4ddab5
commit f5a58368b7
5 changed files with 172 additions and 75 deletions

View File

@ -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);

View File

@ -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;
};

View File

@ -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)

View File

@ -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 },
@ -1155,6 +1159,44 @@ configure_route_handler(char *name)
return (KORE_RESULT_OK);
}
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)
{

View File

@ -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);
@ -763,6 +764,28 @@ http_request_header(struct http_request *req, const char *header,
return (KORE_RESULT_ERROR);
}
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)
{
@ -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;
if (http_data_convert(q->s_value, out, nout, type))
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);
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);
}