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 c9d4f70298eba78795adc299f880a9b15ac95ee6
parent ee3fd3c039399607d074fcd47c22d6000ab01d17
Author: Joris Vink <joris@coders.se>
Date:   Mon,  9 Sep 2013 10:59:56 +0200

- Add SPDY RST control frame handler.
- Keep HTTP requests in connection, so we can delete them if the connection
  ends before the requests do (this way we don't leak them).
- When spdy_stream_close() is called, delete the attached http request.
  (This shouldn't hurt to do, so hopefully won't cause major fallout).
- When parsing HTTP, find the first occurence of end-of-headers so uploads
  with multipart/form-data can succeed properly.
- Add a test upload page to the example module.

Diffstat:
includes/http.h | 1+
includes/kore.h | 4++++
includes/spdy.h | 1+
modules/example/media/upload.html | 20++++++++++++++++++++
modules/example/module.conf | 1+
modules/example/src/example.c | 30++++++++++++++++++++++++++++++
src/connection.c | 5+++++
src/http.c | 41+++++++++++++++++++++++++++++------------
src/spdy.c | 35+++++++++++++++++++++++++++++++++++
9 files changed, 126 insertions(+), 12 deletions(-)

diff --git a/includes/http.h b/includes/http.h @@ -61,6 +61,7 @@ struct http_request { TAILQ_HEAD(, http_header) resp_headers; TAILQ_HEAD(, http_arg) arguments; TAILQ_ENTRY(http_request) list; + TAILQ_ENTRY(http_request) olist; }; extern int http_request_count; diff --git a/includes/kore.h b/includes/kore.h @@ -112,6 +112,9 @@ LIST_HEAD(listener_head, listener); #define KORE_IDLE_TIMER_MAX 20000 +/* XXX hackish. */ +struct http_request; + struct connection { u_int8_t type; int fd; @@ -144,6 +147,7 @@ struct connection { u_int32_t client_stream_id; TAILQ_HEAD(, spdy_stream) spdy_streams; + TAILQ_HEAD(, http_request) http_requests; TAILQ_ENTRY(connection) list; }; diff --git a/includes/spdy.h b/includes/spdy.h @@ -74,6 +74,7 @@ extern const unsigned char SPDY_dictionary_txt[]; /* control frames */ #define SPDY_CTRL_FRAME_SYN_STREAM 1 #define SPDY_CTRL_FRAME_SYN_REPLY 2 +#define SPDY_CTRL_FRAME_RST_STREAM 3 #define SPDY_CTRL_FRAME_SETTINGS 4 #define SPDY_CTRL_FRAME_PING 6 #define SPDY_CTRL_FRAME_GOAWAY 7 diff --git a/modules/example/media/upload.html b/modules/example/media/upload.html @@ -0,0 +1,20 @@ +<!DOCTYPE> +<html> +<head> + <link rel="stylesheet" href="/css/style.css" type="text/css"> + <title>Kore upload test</title> +</head> + +<body style="overflow: auto"> + +<div class="content"> + <form method="POST" enctype="multipart/form-data"> + <input type="file" name="file"> + <input type="submit" value="upload"> + </form> + + <p style="font-size: 12px; font-weight: normal">$upload$</p> +</div> + +</body> +</html> diff --git a/modules/example/module.conf b/modules/example/module.conf @@ -77,6 +77,7 @@ domain localhost { static /intro.jpg serve_intro static /b64test serve_b64test static /spdy-reset serve_spdyreset + static /upload serve_file_upload } #domain domain.com { diff --git a/modules/example/src/example.c b/modules/example/src/example.c @@ -24,6 +24,7 @@ int serve_index(struct http_request *); int serve_intro(struct http_request *); int serve_b64test(struct http_request *); int serve_spdyreset(struct http_request *); +int serve_file_upload(struct http_request *); void my_callback(void); void test_base64(u_int8_t *, u_int32_t, struct kore_buf *); @@ -121,6 +122,35 @@ serve_spdyreset(struct http_request *req) return (KORE_RESULT_OK); } +int +serve_file_upload(struct http_request *req) +{ + int r; + char *p; + u_int8_t *d; + struct kore_buf *b; + u_int32_t len; + + b = kore_buf_create(static_len_html_upload); + kore_buf_append(b, static_html_upload, static_len_html_upload); + + if (req->method == HTTP_METHOD_POST) { + p = http_post_data_text(req); + kore_buf_replace_string(b, "$upload$", p, strlen(p)); + kore_mem_free(p); + } else { + kore_buf_replace_string(b, "$upload$", NULL, 0); + } + + d = kore_buf_release(b, &len); + + http_response_header_add(req, "content-type", "text/html"); + r = http_response(req, 200, d, len); + kore_mem_free(d); + + return (r); +} + void test_base64(u_int8_t *src, u_int32_t slen, struct kore_buf *res) { diff --git a/src/connection.c b/src/connection.c @@ -81,6 +81,7 @@ kore_connection_accept(struct listener *l, struct connection **out) TAILQ_INIT(&(c->send_queue)); TAILQ_INIT(&(c->recv_queue)); TAILQ_INIT(&(c->spdy_streams)); + TAILQ_INIT(&(c->http_requests)); kore_worker_connection_add(c); kore_connection_start_idletimer(c); @@ -192,6 +193,7 @@ kore_connection_handle(struct connection *c) void kore_connection_remove(struct connection *c) { + struct http_request *req; struct netbuf *nb, *next; struct spdy_stream *s, *snext; @@ -209,6 +211,9 @@ kore_connection_remove(struct connection *c) if (c->deflate_started) deflateEnd(&(c->z_deflate)); + TAILQ_FOREACH(req, &(c->http_requests), olist) + req->flags |= HTTP_REQUEST_DELETE; + for (nb = TAILQ_FIRST(&(c->send_queue)); nb != NULL; nb = next) { next = TAILQ_NEXT(nb, list); TAILQ_REMOVE(&(c->send_queue), nb, list); diff --git a/src/http.c b/src/http.c @@ -98,6 +98,8 @@ http_request_new(struct connection *c, struct spdy_stream *s, char *host, http_request_count++; TAILQ_INSERT_TAIL(&http_requests, req, list); + TAILQ_INSERT_TAIL(&(c->http_requests), req, olist); + return (KORE_RESULT_OK); } @@ -176,6 +178,8 @@ http_request_free(struct http_request *req) struct http_arg *q, *qnext; struct http_header *hdr, *next; + TAILQ_REMOVE(&(req->owner->http_requests), req, olist); + for (hdr = TAILQ_FIRST(&(req->resp_headers)); hdr != NULL; hdr = next) { next = TAILQ_NEXT(hdr, list); @@ -204,6 +208,9 @@ http_request_free(struct http_request *req) kore_mem_free(q); } + if (req->method == HTTP_METHOD_POST && req->post_data != NULL) + kore_buf_free(req->post_data); + if (req->agent != NULL) kore_mem_free(req->agent); if (req->hdlr_extra != NULL) @@ -316,7 +323,7 @@ http_header_recv(struct netbuf *nb) struct http_request *req; struct netbuf *nnb; size_t clen, len; - u_int8_t *end_headers, ch; + u_int8_t *end_headers, *end; int h, i, v, skip, bytes_left; char *request[4], *host[3], *hbuf; char *p, *headers[HTTP_REQ_HEADER_MAX]; @@ -324,24 +331,32 @@ http_header_recv(struct netbuf *nb) kore_debug("http_header_recv(%p)", nb); - ch = nb->buf[nb->offset]; - nb->buf[nb->offset] = '\0'; - if (nb->len < 4) return (KORE_RESULT_OK); - if ((end_headers = (u_int8_t *)strrchr((char *)nb->buf, '\r')) == NULL) - return (KORE_RESULT_OK); - if (strncmp(((char *)end_headers - 2), "\r\n\r\n", 4)) + + end = nb->buf + nb->offset; + for (end_headers = nb->buf; end_headers < end; end_headers++) { + if (*end_headers != '\r') + continue; + + if ((end - end_headers) < 4) + return (KORE_RESULT_OK); + + if (!memcmp(end_headers, "\r\n\r\n", 4)) + break; + } + + if (end_headers == end) return (KORE_RESULT_OK); - nb->buf[nb->offset] = ch; - nb->buf[nb->len - 1] = '\0'; + *end_headers = '\0'; + end_headers += 4; nb->flags |= NETBUF_FORCE_REMOVE; - end_headers += 2; - len = end_headers - nb->buf; hbuf = (char *)nb->buf; + kore_debug("HTTP request:\n'%s'\n", hbuf); + h = kore_split_string(hbuf, "\r\n", headers, HTTP_REQ_HEADER_MAX); if (h < 2) return (KORE_RESULT_ERROR); @@ -424,7 +439,8 @@ http_header_recv(struct netbuf *nb) bytes_left = clen - (nb->offset - len); if (bytes_left > 0) { - kore_debug("need %ld more bytes for POST", bytes_left); + kore_debug("%ld/%ld (%ld - %ld) more bytes for POST", + bytes_left, clen, nb->offset, len); net_recv_queue(c, bytes_left, 0, &nnb, http_post_data_recv); nnb->extra = req; @@ -578,6 +594,7 @@ http_post_data_text(struct http_request *req) char *text; data = kore_buf_release(req->post_data, &len); + req->post_data = NULL; len++; text = kore_malloc(len); diff --git a/src/spdy.c b/src/spdy.c @@ -19,6 +19,7 @@ #include "http.h" static int spdy_ctrl_frame_syn_stream(struct netbuf *); +static int spdy_ctrl_frame_rst_stream(struct netbuf *); static int spdy_ctrl_frame_settings(struct netbuf *); static int spdy_ctrl_frame_ping(struct netbuf *); static int spdy_ctrl_frame_window(struct netbuf *); @@ -73,6 +74,9 @@ spdy_frame_recv(struct netbuf *nb) case SPDY_CTRL_FRAME_SYN_STREAM: cb = spdy_ctrl_frame_syn_stream; break; + case SPDY_CTRL_FRAME_RST_STREAM: + cb = spdy_ctrl_frame_rst_stream; + break; case SPDY_CTRL_FRAME_SETTINGS: cb = spdy_ctrl_frame_settings; break; @@ -392,6 +396,7 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb) } s = kore_malloc(sizeof(*s)); + s->httpreq = NULL; s->prio = syn.prio; s->flags = ctrl.flags; s->wsize = c->wsize_initial; @@ -461,6 +466,29 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb) } static int +spdy_ctrl_frame_rst_stream(struct netbuf *nb) +{ + struct spdy_stream *s; + u_int32_t stream_id; + struct connection *c = (struct connection *)nb->owner; + + stream_id = net_read32(nb->buf + SPDY_FRAME_SIZE); + if ((stream_id % 2) == 0) { + kore_debug("received RST for non-client stream %d", stream_id); + return (KORE_RESULT_ERROR); + } + + if ((s = spdy_stream_lookup(c, stream_id)) == NULL) { + kore_debug("received RST for unknown stream %d", stream_id); + return (KORE_RESULT_ERROR); + } + + spdy_stream_close(c, s); + + return (KORE_RESULT_OK); +} + +static int spdy_ctrl_frame_settings(struct netbuf *nb) { u_int8_t *buf; @@ -594,6 +622,8 @@ spdy_data_frame_recv(struct netbuf *nb) static void spdy_stream_close(struct connection *c, struct spdy_stream *s) { + struct http_request *req; + kore_debug("spdy_stream_close(%p, %p) <%d>", c, s, s->stream_id); TAILQ_REMOVE(&(c->spdy_streams), s, list); @@ -603,6 +633,11 @@ spdy_stream_close(struct connection *c, struct spdy_stream *s) kore_mem_free(s->hblock); } + if (s->httpreq != NULL) { + req = s->httpreq; + req->flags |= HTTP_REQUEST_DELETE; + } + kore_mem_free(s); }