- 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.
This commit is contained in:
Joris Vink 2013-09-09 10:59:56 +02:00
parent ee3fd3c039
commit c9d4f70298
9 changed files with 126 additions and 12 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
@ -460,6 +465,29 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb)
return (KORE_RESULT_OK);
}
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)
{
@ -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);
}