commit f59e94a7b666fb55fe164c74b14528a7e97a668d
parent 777cfda791b70707f2665cc6c7e9ef86d261ba8c
Author: Joris Vink <joris@coders.se>
Date: Sat, 13 Jul 2013 19:56:38 +0200
Add spdy_session_teardown() which can properly teardown a SPDY session.
Use this throughout the spdy code to propagate session errors (if any) occur.
At the same time fix BSD's missing CONN_WRITE_BLOCK
Diffstat:
9 files changed, 104 insertions(+), 25 deletions(-)
diff --git a/includes/kore.h b/includes/kore.h
@@ -92,6 +92,7 @@ struct listener {
#define CONN_WRITE_POSSIBLE 0x02
#define CONN_WRITE_BLOCK 0x04
#define CONN_IDLE_TIMER_ACT 0x10
+#define CONN_READ_BLOCK 0x20
#define KORE_IDLE_TIMER_MAX 20000
@@ -299,6 +300,7 @@ int spdy_stream_get_header(struct spdy_header_block *,
char *, char **);
int spdy_frame_recv(struct netbuf *);
+void spdy_session_teardown(struct connection *c, u_int8_t);
void spdy_frame_send(struct connection *, u_int16_t,
u_int8_t, u_int32_t, struct spdy_stream *, u_int32_t);
void spdy_header_block_add(struct spdy_header_block *,
diff --git a/includes/spdy.h b/includes/spdy.h
@@ -76,9 +76,15 @@ extern const unsigned char SPDY_dictionary_txt[];
#define SPDY_CTRL_FRAME_SYN_REPLY 2
#define SPDY_CTRL_FRAME_SETTINGS 4
#define SPDY_CTRL_FRAME_PING 6
+#define SPDY_CTRL_FRAME_GOAWAY 7
#define SPDY_CTRL_FRAME_WINDOW 9
#define SPDY_DATA_FRAME 99
+/* session error codes */
+#define SPDY_SESSION_ERROR_OK 0
+#define SPDY_SESSION_ERROR_PROTOCOL 1
+#define SPDY_SESSION_ERROR_INTERNAL 2
+
/* flags */
#define FLAG_FIN 0x01
#define FLAG_UNIDIRECTIONAL 0x02
diff --git a/modules/example/module.conf b/modules/example/module.conf
@@ -55,6 +55,7 @@ domain 10.211.55.3 {
static / serve_index
static /intro.jpg serve_intro
static /b64test serve_b64test
+ static /spdy-reset serve_spdyreset
}
#domain domain.com {
diff --git a/modules/example/src/example.c b/modules/example/src/example.c
@@ -23,6 +23,8 @@ int serve_style_css(struct http_request *);
int serve_index(struct http_request *);
int serve_intro(struct http_request *);
int serve_b64test(struct http_request *);
+int serve_spdyreset(struct http_request *);
+
void test_base64(u_int8_t *, u_int32_t, struct kore_buf *);
char *b64tests[] = {
@@ -111,6 +113,13 @@ serve_b64test(struct http_request *req)
return (ret);
}
+int
+serve_spdyreset(struct http_request *req)
+{
+ spdy_session_teardown(req->owner, SPDY_SESSION_ERROR_OK);
+ return (KORE_RESULT_OK);
+}
+
void
test_base64(u_int8_t *src, u_int32_t slen, struct kore_buf *res)
{
diff --git a/src/bsd.c b/src/bsd.c
@@ -98,9 +98,11 @@ kore_platform_event_wait(void)
}
} else {
c = (struct connection *)events[i].udata;
- if (events[i].filter == EVFILT_READ)
+ if (events[i].filter == EVFILT_READ &&
+ !(c->flags & CONN_READ_BLOCK))
c->flags |= CONN_READ_POSSIBLE;
- if (events[i].filter == EVFILT_WRITE)
+ if (events[i].filter == EVFILT_WRITE &&
+ !(c->flags & CONN_WRITE_BLOCK))
c->flags |= CONN_WRITE_POSSIBLE;
if (!kore_connection_handle(c)) {
diff --git a/src/http.c b/src/http.c
@@ -222,8 +222,11 @@ http_response(struct http_request *req, int status, u_int8_t *d, u_int32_t len)
spdy_header_block_add(hblock, hdr->header, hdr->value);
htext = spdy_header_block_release(req->owner, hblock, &hlen);
- if (htext == NULL)
- return (KORE_RESULT_ERROR);
+ if (htext == NULL) {
+ spdy_session_teardown(req->owner,
+ SPDY_SESSION_ERROR_INTERNAL);
+ return (KORE_RESULT_OK);
+ }
spdy_frame_send(req->owner, SPDY_CTRL_FRAME_SYN_REPLY,
0, hlen, req->stream, 0);
diff --git a/src/linux.c b/src/linux.c
@@ -104,7 +104,8 @@ kore_platform_event_wait(void)
}
} else {
c = (struct connection *)events[i].data.ptr;
- if (events[i].events & EPOLLIN)
+ if (events[i].events & EPOLLIN &&
+ !(c->flags & CONN_READ_BLOCK))
c->flags |= CONN_READ_POSSIBLE;
if (events[i].events & EPOLLOUT &&
!(c->flags & CONN_WRITE_BLOCK))
diff --git a/src/spdy.c b/src/spdy.c
@@ -24,11 +24,12 @@ static int spdy_ctrl_frame_ping(struct netbuf *);
static int spdy_ctrl_frame_window(struct netbuf *);
static int spdy_data_frame_recv(struct netbuf *);
static int spdy_frame_send_done(struct netbuf *);
+static int spdy_goaway_send_done(struct netbuf *);
+
static void spdy_update_wsize(struct connection *,
struct spdy_stream *, u_int32_t);
static void spdy_stream_close(struct connection *,
struct spdy_stream *);
-
static int spdy_zlib_inflate(struct connection *, u_int8_t *,
size_t, u_int8_t **, u_int32_t *);
static int spdy_zlib_deflate(struct connection *, u_int8_t *,
@@ -61,7 +62,9 @@ spdy_frame_recv(struct netbuf *nb)
if (ctrl.version != 3) {
kore_debug("protocol mismatch (recv version %d)",
ctrl.version);
- return (KORE_RESULT_ERROR);
+
+ spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL);
+ return (KORE_RESULT_OK);
}
switch (ctrl.type) {
@@ -108,6 +111,9 @@ spdy_frame_recv(struct netbuf *nb)
if (r == KORE_RESULT_OK) {
net_recv_queue(c, SPDY_FRAME_SIZE,
0, NULL, spdy_frame_recv);
+ } else {
+ r = KORE_RESULT_OK;
+ spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL);
}
return (r);
@@ -145,6 +151,14 @@ spdy_frame_send(struct connection *c, u_int16_t type, u_int8_t flags,
length = 12;
break;
+ case SPDY_CTRL_FRAME_GOAWAY:
+ net_write16(&nb[0], 3);
+ nb[0] |= (1 << 7);
+ net_write16(&nb[2], type);
+ net_write32(&nb[4], len);
+ nb[4] = flags;
+ length = 8;
+ break;
case SPDY_DATA_FRAME:
net_write32(&nb[0], s->stream_id);
nb[0] &= ~(1 << 7);
@@ -309,6 +323,26 @@ spdy_frame_data_done(struct netbuf *nb)
return (KORE_RESULT_OK);
}
+void
+spdy_session_teardown(struct connection *c, u_int8_t err)
+{
+ u_int8_t d[8];
+
+ kore_debug("spdy_session_teardown(%p, %d)", c, err);
+
+ net_write32((u_int8_t *)&d[0], c->client_stream_id);
+ net_write32((u_int8_t *)&d[4], err);
+
+ spdy_frame_send(c, SPDY_CTRL_FRAME_GOAWAY, 0, 8, NULL, 0);
+ net_send_queue(c, d, sizeof(d), 0, NULL, spdy_goaway_send_done);
+
+ c->flags &= ~CONN_READ_POSSIBLE;
+ c->flags |= CONN_READ_BLOCK;
+
+ c->idle_timer.length = 5;
+ kore_connection_start_idletimer(c);
+}
+
static int
spdy_ctrl_frame_syn_stream(struct netbuf *nb)
{
@@ -337,19 +371,22 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb)
if ((syn.stream_id % 2) == 0 || syn.stream_id == 0) {
kore_debug("client sent incorrect id for SPDY_SYN_STREAM (%d)",
syn.stream_id);
- return (KORE_RESULT_ERROR);
+ spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL);
+ return (KORE_RESULT_OK);
}
/* XXX need to send protocol error. */
if (syn.stream_id < c->client_stream_id) {
kore_debug("client sent incorrect id SPDY_SYN_STREAM (%d < %d)",
syn.stream_id, c->client_stream_id);
- return (KORE_RESULT_ERROR);
+ spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL);
+ return (KORE_RESULT_OK);
}
if ((s = spdy_stream_lookup(c, syn.stream_id)) != NULL) {
kore_debug("duplicate SPDY_SYN_STREAM (%d)", syn.stream_id);
- return (KORE_RESULT_ERROR);
+ spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL);
+ return (KORE_RESULT_OK);
}
s = (struct spdy_stream *)kore_malloc(sizeof(*s));
@@ -366,7 +403,8 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb)
kore_mem_free(s->hblock->header_block);
kore_mem_free(s->hblock);
kore_mem_free(s);
- return (KORE_RESULT_ERROR);
+ spdy_session_teardown(c, SPDY_SESSION_ERROR_INTERNAL);
+ return (KORE_RESULT_OK);
}
s->hblock->header_pairs = net_read32(s->hblock->header_block);
@@ -376,19 +414,20 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb)
host = NULL;
method = NULL;
-#define GET_HEADER(n, r) \
- if (!spdy_stream_get_header(s->hblock, n, r)) { \
+#define GET_HEADER(n, r) \
+ if (!spdy_stream_get_header(s->hblock, n, r)) { \
kore_mem_free(s->hblock->header_block); \
kore_mem_free(s->hblock); \
kore_mem_free(s); \
- kore_debug("no such header: %s", n); \
- if (path != NULL) \
+ kore_debug("no such header: %s", n); \
+ if (path != NULL) \
kore_mem_free(path); \
- if (host != NULL) \
+ if (host != NULL) \
kore_mem_free(host); \
- if (method != NULL) \
+ if (method != NULL) \
kore_mem_free(method); \
- return (KORE_RESULT_ERROR); \
+ spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); \
+ return (KORE_RESULT_OK); \
}
GET_HEADER(":path", &path);
@@ -403,7 +442,8 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb)
kore_mem_free(s->hblock->header_block);
kore_mem_free(s->hblock);
kore_mem_free(s);
- return (KORE_RESULT_ERROR);
+ spdy_session_teardown(c, SPDY_SESSION_ERROR_INTERNAL);
+ return (KORE_RESULT_OK);
}
kore_mem_free(path);
@@ -432,7 +472,8 @@ spdy_ctrl_frame_settings(struct netbuf *nb)
if (length != ((ecount * 8) + 4)) {
kore_debug("ecount is not correct (%d != %d)", length,
(ecount * 8) + 4);
- return (KORE_RESULT_ERROR);
+ spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL);
+ return (KORE_RESULT_OK);
}
buf = nb->buf + SPDY_FRAME_SIZE + 4;
@@ -469,7 +510,8 @@ spdy_ctrl_frame_ping(struct netbuf *nb)
/* XXX todo - check if we sent the ping. */
if ((id % 2) == 0) {
kore_debug("received malformed client PING (%d)", id);
- return (KORE_RESULT_ERROR);
+ spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL);
+ return (KORE_RESULT_OK);
}
spdy_frame_send(c, SPDY_CTRL_FRAME_PING, 0, 4, NULL, id);
@@ -489,7 +531,8 @@ spdy_ctrl_frame_window(struct netbuf *nb)
if ((s = spdy_stream_lookup(c, stream_id)) == NULL) {
kore_debug("received WINDOW_UPDATE for nonexistant stream");
kore_debug("stream_id: %d", stream_id);
- return (KORE_RESULT_ERROR);
+ spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL);
+ return (KORE_RESULT_OK);
}
kore_debug("SPDY_WINDOW_UPDATE: %d:%d", stream_id, window_size);
@@ -521,13 +564,15 @@ spdy_data_frame_recv(struct netbuf *nb)
data.flags, data.length);
if ((s = spdy_stream_lookup(c, data.stream_id)) == NULL) {
- kore_debug("session data for incorrect stream");
- return (KORE_RESULT_OK);
+ kore_debug("session data for non-existant stream");
+ /* stream error */
+ return (KORE_RESULT_ERROR);
}
req = (struct http_request *)s->httpreq;
if (req->method != HTTP_METHOD_POST) {
kore_debug("data frame for non post received");
+ /* stream error */
return (KORE_RESULT_ERROR);
}
@@ -573,6 +618,15 @@ spdy_frame_send_done(struct netbuf *nb)
return (KORE_RESULT_OK);
}
+static int
+spdy_goaway_send_done(struct netbuf *nb)
+{
+ struct connection *c = (struct connection *)nb->owner;
+
+ kore_connection_disconnect(c);
+ return (KORE_RESULT_OK);
+}
+
static void
spdy_update_wsize(struct connection *c, struct spdy_stream *s, u_int32_t len)
{
diff --git a/src/worker.c b/src/worker.c
@@ -231,7 +231,8 @@ kore_worker_entry(struct kore_worker *kw)
idle_check = now;
TAILQ_FOREACH(c, &worker_clients, list) {
if (c->proto == CONN_PROTO_SPDY &&
- !(c->flags & CONN_WRITE_BLOCK))
+ !(c->flags & CONN_WRITE_BLOCK) &&
+ !(c->flags & CONN_READ_BLOCK))
continue;
if (!(c->flags & CONN_IDLE_TIMER_ACT))
continue;