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 a603b77e24dd6b8394e25e32941ccc4185080a22
parent 3741e8ca3ffd2d16cc5ae899781174ad645c37f7
Author: Joris Vink <joris@coders.se>
Date:   Wed,  8 Oct 2014 11:03:14 +0200

Add PUT/DELETE/HEAD methods (finally).

This commit renames certain POST centric variable and configuration
naming to the correct HTTP body stuff.

API changes include http_postbody_text() and http_postbody_bytes() to
have become http_body_text() and http_body_bytes().

The developer is still responsible for validating the method their
page handler is called with. Hopefully this becomes a configuration
option soon enough.

Diffstat:
conf/kore.conf.example | 4++--
examples/json_yajl/src/json_yajl.c | 10++++++----
includes/http.h | 14+++++++++-----
src/accesslog.c | 9+++++++++
src/config.c | 14+++++++-------
src/http.c | 92+++++++++++++++++++++++++++++++++++++++++++------------------------------------
src/pgsql.c | 1-
src/spdy.c | 22++++++++++++----------
8 files changed, 95 insertions(+), 71 deletions(-)

diff --git a/conf/kore.conf.example b/conf/kore.conf.example @@ -26,7 +26,7 @@ workers 4 # HTTP specific settings. # http_header_max Maximum size of HTTP headers (in bytes). # -# http_postbody_max Maximum size of an HTTP POST body (in bytes). +# http_body_max Maximum size of an HTTP body (in bytes). # # http_keepalive_time Maximum seconds an HTTP connection can be # kept alive by the browser. @@ -36,7 +36,7 @@ workers 4 # all responses. Parameter is the age. # (Set to 0 to disable sending this header). #http_header_max 4096 -#http_postbody_max 10240000 +#http_body_max 10240000 #http_keepalive_time 0 #http_hsts_enable 31536000 diff --git a/examples/json_yajl/src/json_yajl.c b/examples/json_yajl/src/json_yajl.c @@ -14,9 +14,11 @@ page(struct http_request *req) char eb[1024]; const char *path[] = { "foo", "bar", NULL }; - /* We only allow POST methods. */ - if (req->method != HTTP_METHOD_POST) { - http_response(req, 400, NULL, 0); + /* We only allow POST/PUT methods. */ + if (req->method != HTTP_METHOD_POST && + req->method != HTTP_METHOD_PUT) { + http_response_header(req, "allow", "POST, PUT"); + http_response(req, HTTP_STATUS_METHOD_NOT_ALLOWED, NULL, 0); return (KORE_RESULT_OK); } @@ -24,7 +26,7 @@ page(struct http_request *req) * Grab the entire body we received as text (NUL-terminated). * Note: this can return NULL and the result MUST be freed. */ - if ((body = http_post_data_text(req)) == NULL) { + if ((body = http_body_text(req)) == NULL) { http_response(req, 400, NULL, 0); return (KORE_RESULT_OK); } diff --git a/includes/http.h b/includes/http.h @@ -20,7 +20,7 @@ #define HTTP_KEEPALIVE_TIME 20 #define HTTP_HSTS_ENABLE 31536000 #define HTTP_HEADER_MAX_LEN 4096 -#define HTTP_POSTBODY_MAX_LEN 10240000 +#define HTTP_BODY_MAX_LEN 10240000 #define HTTP_URI_LEN 2000 #define HTTP_USERAGENT_LEN 256 #define HTTP_REQ_HEADER_MAX 25 @@ -152,11 +152,15 @@ struct http_file { #define HTTP_METHOD_GET 0 #define HTTP_METHOD_POST 1 +#define HTTP_METHOD_PUT 2 +#define HTTP_METHOD_DELETE 3 +#define HTTP_METHOD_HEAD 4 #define HTTP_REQUEST_COMPLETE 0x01 #define HTTP_REQUEST_DELETE 0x02 #define HTTP_REQUEST_SLEEPING 0x04 #define HTTP_REQUEST_PGSQL_QUEUE 0x10 +#define HTTP_REQUEST_EXPECT_BODY 0x20 struct kore_task; @@ -173,7 +177,7 @@ struct http_request { char *agent; struct connection *owner; struct spdy_stream *stream; - struct kore_buf *post_data; + struct kore_buf *http_body; void *hdlr_extra; char *query_string; u_int8_t *multipart_body; @@ -197,7 +201,7 @@ struct http_state { extern int http_request_count; extern u_int16_t http_header_max; -extern u_int64_t http_postbody_max; +extern u_int64_t http_body_max; extern u_int64_t http_hsts_enable; extern u_int16_t http_keepalive_time; @@ -208,9 +212,9 @@ time_t http_date_to_time(char *); void http_request_free(struct http_request *); void http_request_sleep(struct http_request *); void http_request_wakeup(struct http_request *); -char *http_post_data_text(struct http_request *); +char *http_body_text(struct http_request *); void http_process_request(struct http_request *, int); -u_int8_t *http_post_data_bytes(struct http_request *, u_int32_t *); +u_int8_t *http_body_bytes(struct http_request *, u_int32_t *); void http_response(struct http_request *, int, void *, u_int32_t); void http_response_stream(struct http_request *, int, void *, u_int64_t, int (*cb)(struct netbuf *), void *); diff --git a/src/accesslog.c b/src/accesslog.c @@ -101,6 +101,15 @@ kore_accesslog_wait(void) case HTTP_METHOD_POST: method = "POST"; break; + case HTTP_METHOD_PUT: + method = "PUT"; + break; + case HTTP_METHOD_DELETE: + method = "DELETE"; + break; + case HTTP_METHOD_HEAD: + method = "HEAD"; + break; default: method = "UNKNOWN"; break; diff --git a/src/config.c b/src/config.c @@ -49,7 +49,7 @@ static int configure_ssl_dhparam(char **); static int configure_ssl_no_compression(char **); static int configure_spdy_idle_time(char **); static int configure_http_header_max(char **); -static int configure_http_postbody_max(char **); +static int configure_http_body_max(char **); static int configure_http_hsts_enable(char **); static int configure_http_keepalive_time(char **); static int configure_validator(char **); @@ -94,7 +94,7 @@ static struct { { "certkey", configure_certkey }, { "require_client_cert", configure_require_client_cert }, { "http_header_max", configure_http_header_max }, - { "http_postbody_max", configure_http_postbody_max }, + { "http_body_max", configure_http_body_max }, { "http_hsts_enable", configure_http_hsts_enable }, { "http_keepalive_time", configure_http_keepalive_time }, { "validator", configure_validator }, @@ -584,21 +584,21 @@ configure_http_header_max(char **argv) } static int -configure_http_postbody_max(char **argv) +configure_http_body_max(char **argv) { int err; if (argv[1] == NULL) return (KORE_RESULT_ERROR); - if (http_postbody_max != HTTP_POSTBODY_MAX_LEN) { - kore_debug("http_postbody_max already set"); + if (http_body_max != HTTP_BODY_MAX_LEN) { + kore_debug("http_body_max already set"); return (KORE_RESULT_ERROR); } - http_postbody_max = kore_strtonum(argv[1], 10, 1, LONG_MAX, &err); + http_body_max = kore_strtonum(argv[1], 10, 1, LONG_MAX, &err); if (err != KORE_RESULT_OK) { - printf("bad http_postbody_max value: %s\n", argv[1]); + printf("bad http_body_max value: %s\n", argv[1]); return (KORE_RESULT_ERROR); } diff --git a/src/http.c b/src/http.c @@ -31,7 +31,7 @@ #include "tasks.h" #endif -static int http_post_data_recv(struct netbuf *); +static int http_body_recv(struct netbuf *); static void http_error_response(struct connection *, struct spdy_stream *, int); static void http_argument_add(struct http_request *, const char *, @@ -54,7 +54,7 @@ int http_request_count; u_int64_t http_hsts_enable = HTTP_HSTS_ENABLE; u_int16_t http_header_max = HTTP_HEADER_MAX_LEN; u_int16_t http_keepalive_time = HTTP_KEEPALIVE_TIME; -u_int64_t http_postbody_max = HTTP_POSTBODY_MAX_LEN; +u_int64_t http_body_max = HTTP_BODY_MAX_LEN; void http_init(void) @@ -104,11 +104,20 @@ http_request_new(struct connection *c, struct spdy_stream *s, const char *host, if (!strcasecmp(method, "get")) { m = HTTP_METHOD_GET; flags = HTTP_REQUEST_COMPLETE; + } else if (!strcasecmp(method, "delete")) { + m = HTTP_METHOD_DELETE; + flags = HTTP_REQUEST_COMPLETE; } else if (!strcasecmp(method, "post")) { - flags = 0; m = HTTP_METHOD_POST; + flags = HTTP_REQUEST_EXPECT_BODY; + } else if (!strcasecmp(method, "put")) { + m = HTTP_METHOD_PUT; + flags = HTTP_REQUEST_EXPECT_BODY; + } else if (!strcasecmp(method, "head")) { + m = HTTP_METHOD_HEAD; + flags = HTTP_REQUEST_COMPLETE; } else { - http_error_response(c, s, 405); + http_error_response(c, s, 400); return (KORE_RESULT_ERROR); } @@ -124,7 +133,7 @@ http_request_new(struct connection *c, struct spdy_stream *s, const char *host, req->agent = NULL; req->flags = flags; req->fsm_state = 0; - req->post_data = NULL; + req->http_body = NULL; req->hdlr_extra = NULL; req->query_string = NULL; req->multipart_body = NULL; @@ -389,9 +398,9 @@ http_request_free(struct http_request *req) kore_mem_free(f); } - if (req->method == HTTP_METHOD_POST && req->post_data != NULL) - kore_buf_free(req->post_data); - if (req->method == HTTP_METHOD_POST && req->multipart_body != NULL) + if (req->http_body != NULL) + kore_buf_free(req->http_body); + if (req->multipart_body != NULL) kore_mem_free(req->multipart_body); if (req->agent != NULL) @@ -438,8 +447,10 @@ http_response_stream(struct http_request *req, int status, void *base, break; } - net_send_stream(req->owner, base, len, req->stream, cb, &nb); - nb->extra = arg; + if (req->method != HTTP_METHOD_HEAD) { + net_send_stream(req->owner, base, len, req->stream, cb, &nb); + nb->extra = arg; + } } int @@ -568,9 +579,9 @@ http_header_recv(struct netbuf *nb) req->agent = kore_strdup(hdr->value); } - if (req->method == HTTP_METHOD_POST) { + if (req->flags & HTTP_REQUEST_EXPECT_BODY) { if (!http_request_header(req, "content-length", &p)) { - kore_debug("POST but no content-length"); + kore_debug("expected body but no content-length"); req->flags |= HTTP_REQUEST_DELETE; http_error_response(req->owner, NULL, 411); return (KORE_RESULT_OK); @@ -589,30 +600,31 @@ http_header_recv(struct netbuf *nb) if (clen == 0) { req->flags |= HTTP_REQUEST_COMPLETE; + req->flags &= ~HTTP_REQUEST_EXPECT_BODY; return (KORE_RESULT_OK); } - if (clen > http_postbody_max) { - kore_log(LOG_NOTICE, "POST data too large (%ld > %ld)", - clen, http_postbody_max); + if (clen > http_body_max) { + kore_log(LOG_NOTICE, "body too large (%ld > %ld)", + clen, http_body_max); req->flags |= HTTP_REQUEST_DELETE; http_error_response(req->owner, NULL, 411); return (KORE_RESULT_OK); } - req->post_data = kore_buf_create(clen); - kore_buf_append(req->post_data, end_headers, + req->http_body = kore_buf_create(clen); + kore_buf_append(req->http_body, end_headers, (nb->s_off - len)); bytes_left = clen - (nb->s_off - len); if (bytes_left > 0) { - kore_debug("%ld/%ld (%ld - %ld) more bytes for POST", + kore_debug("%ld/%ld (%ld - %ld) more bytes for body", bytes_left, clen, nb->s_off, len); - net_recv_queue(c, bytes_left, - 0, &nnb, http_post_data_recv); + net_recv_queue(c, bytes_left, 0, &nnb, http_body_recv); nnb->extra = req; } else if (bytes_left == 0) { req->flags |= HTTP_REQUEST_COMPLETE; + req->flags &= ~HTTP_REQUEST_EXPECT_BODY; } else { kore_debug("bytes_left would become zero (%ld)", clen); http_error_response(req->owner, NULL, 500); @@ -630,9 +642,9 @@ http_populate_arguments(struct http_request *req) char *query, *args[HTTP_MAX_QUERY_ARGS], *val[3]; if (req->method == HTTP_METHOD_POST) { - if (req->post_data == NULL) + if (req->http_body == NULL) return (0); - query = http_post_data_text(req); + query = http_body_text(req); } else { if (req->query_string == NULL) return (0); @@ -825,7 +837,7 @@ http_populate_multipart_form(struct http_request *req, int *v) slen = l; kore_mem_free(type); - req->multipart_body = http_post_data_bytes(req, &blen); + req->multipart_body = http_body_bytes(req, &blen); if (slen < 3 || blen < (slen * 2)) { kore_mem_free(boundary); return (KORE_RESULT_ERROR); @@ -943,17 +955,17 @@ http_generic_404(struct http_request *req) } char * -http_post_data_text(struct http_request *req) +http_body_text(struct http_request *req) { u_int32_t len; u_int8_t *data; char *text; - if (req->post_data == NULL) + if (req->http_body == NULL) return (NULL); - data = kore_buf_release(req->post_data, &len); - req->post_data = NULL; + data = kore_buf_release(req->http_body, &len); + req->http_body = NULL; len++; text = kore_malloc(len); @@ -964,15 +976,15 @@ http_post_data_text(struct http_request *req) } u_int8_t * -http_post_data_bytes(struct http_request *req, u_int32_t *len) +http_body_bytes(struct http_request *req, u_int32_t *len) { u_int8_t *data; - if (req->post_data == NULL) + if (req->http_body == NULL) return (NULL); - data = kore_buf_release(req->post_data, len); - req->post_data = NULL; + data = kore_buf_release(req->http_body, len); + req->http_body = NULL; return (data); } @@ -1073,14 +1085,16 @@ http_file_add(struct http_request *req, const char *name, const char *filename, } static int -http_post_data_recv(struct netbuf *nb) +http_body_recv(struct netbuf *nb) { struct http_request *req = (struct http_request *)nb->extra; - kore_buf_append(req->post_data, nb->buf, nb->s_off); + kore_buf_append(req->http_body, nb->buf, nb->s_off); + req->flags |= HTTP_REQUEST_COMPLETE; + req->flags &= ~HTTP_REQUEST_EXPECT_BODY; - kore_debug("post complete for request %p", req); + kore_debug("received all body data for request %p", req); return (KORE_RESULT_OK); } @@ -1128,9 +1142,6 @@ http_response_spdy(struct http_request *req, struct connection *c, KORE_VERSION_STATE); spdy_header_block_add(hblock, ":server", sbuf); - if (status == HTTP_STATUS_METHOD_NOT_ALLOWED) - spdy_header_block_add(hblock, ":allow", "get, post"); - if (http_hsts_enable) { (void)snprintf(sbuf, sizeof(sbuf), "max-age=%" PRIu64, http_hsts_enable); @@ -1153,7 +1164,7 @@ http_response_spdy(struct http_request *req, struct connection *c, net_send_queue(c, htext, hlen, NULL, NETBUF_LAST_CHAIN); kore_mem_free(htext); - if (len > 0) { + if (len > 0 && req != NULL && req->method != HTTP_METHOD_HEAD) { s->send_size += len; s->flags |= SPDY_DATAFRAME_PRELUDE; @@ -1181,9 +1192,6 @@ http_response_normal(struct http_request *req, struct connection *c, KORE_NAME_STRING, KORE_VERSION_MAJOR, KORE_VERSION_MINOR, KORE_VERSION_STATE); - if (status == HTTP_STATUS_METHOD_NOT_ALLOWED) - kore_buf_appendf(header_buf, "allow: GET, POST\r\n"); - if (c->flags & CONN_CLOSE_EMPTY) connection_close = 1; else @@ -1227,7 +1235,7 @@ http_response_normal(struct http_request *req, struct connection *c, net_send_queue(c, header_buf->data, header_buf->offset, NULL, NETBUF_LAST_CHAIN); - if (d != NULL) + if (d != NULL && req != NULL && req->method != HTTP_METHOD_HEAD) net_send_queue(c, d, len, NULL, NETBUF_LAST_CHAIN); if (!(c->flags & CONN_CLOSE_EMPTY)) { diff --git a/src/pgsql.c b/src/pgsql.c @@ -306,7 +306,6 @@ pgsql_schedule(struct kore_pgsql *pgsql, struct http_request *req) kore_platform_schedule_read(fd, pgsql->conn); pgsql->state = KORE_PGSQL_STATE_WAIT; - kore_debug("query '%s' for %p sent on %p", query, req, pgsql->conn); } static void diff --git a/src/spdy.c b/src/spdy.c @@ -781,16 +781,16 @@ spdy_data_frame_recv(struct netbuf *nb) } req = (struct http_request *)s->httpreq; - if (req == NULL || req->method != HTTP_METHOD_POST) { + if (req == NULL || !(req->flags & HTTP_REQUEST_EXPECT_BODY)) { kore_debug("data frame for non post received"); /* stream error */ return (KORE_RESULT_ERROR); } - if (req->post_data == NULL) { + if (req->http_body == NULL) { if (!spdy_stream_get_header(s->hblock, "content-length", &content)) { - kore_debug("no content-length found for post"); + kore_debug("no content-length found for body"); return (KORE_RESULT_ERROR); } @@ -805,28 +805,29 @@ spdy_data_frame_recv(struct netbuf *nb) if (s->post_size == 0) { req->flags |= HTTP_REQUEST_COMPLETE; + req->flags &= ~HTTP_REQUEST_EXPECT_BODY; return (KORE_RESULT_OK); } - if (s->post_size > http_postbody_max) { - kore_log(LOG_NOTICE, "POST data too large (%ld > %ld)", - s->post_size, http_postbody_max); + if (s->post_size > http_body_max) { + kore_log(LOG_NOTICE, "body data too large (%ld > %ld)", + s->post_size, http_body_max); return (KORE_RESULT_ERROR); } - req->post_data = kore_buf_create(s->post_size); + req->http_body = kore_buf_create(s->post_size); } - if ((req->post_data->offset + data.length) > s->post_size) { + if ((req->http_body->offset + data.length) > s->post_size) { kore_debug("POST would grow too large"); return (KORE_RESULT_ERROR); } - kore_buf_append(req->post_data, (nb->buf + SPDY_FRAME_SIZE), + kore_buf_append(req->http_body, (nb->buf + SPDY_FRAME_SIZE), data.length); if (data.flags & FLAG_FIN) { - if (req->post_data->offset != s->post_size) { + if (req->http_body->offset != s->post_size) { kore_debug("FLAG_FIN before all POST data received"); return (KORE_RESULT_ERROR); } @@ -834,6 +835,7 @@ spdy_data_frame_recv(struct netbuf *nb) s->post_size = 0; s->flags |= FLAG_FIN; req->flags |= HTTP_REQUEST_COMPLETE; + req->flags &= ~HTTP_REQUEST_EXPECT_BODY; } /*