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:
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);
}