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
This commit is contained in:
Joris Vink 2013-07-13 19:56:38 +02:00
parent 777cfda791
commit f59e94a7b6
9 changed files with 104 additions and 25 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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