From b35f0198113ef8ed88ec8f46cbf89a022d853911 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 28 Jan 2012 03:54:53 +0900 Subject: [PATCH] Added callback functions for DATA frames. Fixed unpacking length field. --- examples/spdycl.cc | 72 ++++++++++++++++++++++++++++++---- lib/includes/spdylay/spdylay.h | 23 +++++++++++ lib/spdylay_frame.c | 4 +- lib/spdylay_session.c | 40 +++++++++++++------ 4 files changed, 119 insertions(+), 20 deletions(-) diff --git a/examples/spdycl.cc b/examples/spdycl.cc index 784cb0f4..e6e9d534 100644 --- a/examples/spdycl.cc +++ b/examples/spdycl.cc @@ -147,9 +147,16 @@ public: { return fd_; } - int req_submit(const char *path) + int submit_request(const char *path) { - return spdylay_req_submit(session_, path); + const char *nv[] = { + "method", "GET", + "scheme", "https", + "url", path, + "version", "HTTP/1.1", + NULL + }; + return spdylay_submit_request(session_, 3, nv); } bool would_block(int r) { @@ -196,6 +203,54 @@ ssize_t recv_callback(spdylay_session *session, return r; } +void print_nv(char **nv) +{ + int i; + for(i = 0; nv[i]; i += 2) { + printf(" %s: %s\n", nv[i], nv[i+1]); + } +} + +void on_ctrl_recv_callback +(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, + void *user_data) +{ + static const char *ctrl_names[] = { + "SYN_STREAM", + "SYN_REPLY", + "RST_STREAM", + "SETTINGS", + "NOOP", + "PING", + "GOAWAY", + "HEADERS" + }; + printf("recv %s frame ", ctrl_names[type-1]); + switch(type) { + case SPDYLAY_SYN_REPLY: + printf("(stream_id=%d, flags=%d, length=%d)\n", + frame->syn_reply.stream_id, frame->syn_reply.hd.flags, + frame->syn_reply.hd.length); + print_nv(frame->syn_reply.nv); + break; + default: + break; + } +} + +void on_data_chunk_recv_callback +(spdylay_session *session, int32_t stream_id, uint8_t flags, + const uint8_t *data, size_t len, void *user_data) +{} + +void on_data_recv_callback +(spdylay_session *session, int32_t stream_id, uint8_t flags, int32_t length, + void *user_data) +{ + printf("recv DATA frame (stream_id=%d, flags=%d, length=%d)\n", + stream_id, flags, length); +} + void ctl_epollev(int epollfd, int op, SpdylayClient& sc) { epoll_event ev; @@ -293,7 +348,7 @@ int communicate(const char *host, const char *service, const char *path, return -1; } - sc.req_submit(path); + sc.submit_request(path); ctl_epollev(epollfd, EPOLL_CTL_ADD, sc); static const size_t MAX_EVENTS = 1; @@ -330,10 +385,13 @@ int communicate(const char *host, const char *service, const char *path, int run(const char *host, const char *service, const char *path) { - spdylay_session_callbacks callbacks = { - send_callback, - recv_callback, - }; + spdylay_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = send_callback; + callbacks.recv_callback = recv_callback; + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; + callbacks.on_data_recv_callback = on_data_recv_callback; return communicate(host, service, path, &callbacks); } diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index 728bff7c..3a6d6cf7 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -185,12 +185,35 @@ typedef void (*spdylay_on_invalid_ctrl_recv_callback) typedef void (*spdylay_on_ping_recv_callback) (spdylay_session *session, const struct timespec *rtt, void *user_data); +/* + * Callback function invoked when data chunk of DATA frame is + * received. |stream_id| is the stream ID of this DATA frame belongs + * to. |flags| is the flags of DATA frame which this data chunk is + * contained. flags & SPDYLAY_FLAG_FIN does not necessarily mean this + * chunk of data is the last one in the stream. You should use + * spdylay_on_data_recv_callback to know all data frame is received + * whose flags contains SPDYLAY_FLAG_FIN. + */ +typedef void (*spdylay_on_data_chunk_recv_callback) +(spdylay_session *session, int32_t stream_id, uint8_t flags, + const uint8_t *data, size_t len, void *user_data); + +/* + * Callback function invoked when DATA frame is received. The actual + * data it contains are received by spdylay_on_data_recv_callback. + */ +typedef void (*spdylay_on_data_recv_callback) +(spdylay_session *session, int32_t stream_id, uint8_t flags, int32_t length, + void *user_data); + typedef struct { spdylay_send_callback send_callback; spdylay_recv_callback recv_callback; spdylay_on_ctrl_recv_callback on_ctrl_recv_callback; spdylay_on_invalid_ctrl_recv_callback on_invalid_ctrl_recv_callback; spdylay_on_ping_recv_callback on_ping_recv_callback; + spdylay_on_data_chunk_recv_callback on_data_chunk_recv_callback; + spdylay_on_data_recv_callback on_data_recv_callback; } spdylay_session_callbacks; int spdylay_session_client_new(spdylay_session **session_ptr, diff --git a/lib/spdylay_frame.c b/lib/spdylay_frame.c index da83b602..ea58590c 100644 --- a/lib/spdylay_frame.c +++ b/lib/spdylay_frame.c @@ -57,9 +57,9 @@ static void spdylay_frame_unpack_ctrl_hd(spdylay_ctrl_hd *hd, const uint8_t* buf) { hd->version = spdylay_get_uint16(buf) & SPDYLAY_VERSION_MASK; - hd->type = spdylay_get_uint16(buf+2); + hd->type = spdylay_get_uint16(&buf[2]); hd->flags = buf[4]; - hd->length = spdylay_get_uint32(buf+5) & SPDYLAY_LENGTH_MASK; + hd->length = spdylay_get_uint32(&buf[4]) & SPDYLAY_LENGTH_MASK; } static ssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr, diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index 917f64f1..82b48aef 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -717,6 +717,7 @@ int spdylay_session_on_rst_stream_received(spdylay_session *session, spdylay_frame *frame) { spdylay_session_close_stream(session, frame->rst_stream.stream_id); + return 0; } int spdylay_session_on_ping_received(spdylay_session *session, @@ -881,10 +882,18 @@ int spdylay_session_process_ctrl_frame(spdylay_session *session) int spdylay_session_process_data_frame(spdylay_session *session) { - /* printf("Received data frame, stream_id %d, %zu bytes, fin=%d\n", */ - /* spdylay_get_uint32(session->iframe.headbuf), */ - /* session->iframe.len, */ - /* session->iframe.headbuf[4] & SPDYLAY_FLAG_FIN); */ + if(session->callbacks.on_data_recv_callback) { + int32_t stream_id; + uint8_t flags; + int32_t length; + stream_id = spdylay_get_uint32(session->iframe.headbuf) & + SPDYLAY_STREAM_ID_MASK; + flags = session->iframe.headbuf[4]; + length = spdylay_get_uint32(&session->iframe.headbuf[4]) & + SPDYLAY_LENGTH_MASK; + session->callbacks.on_data_recv_callback + (session, stream_id, flags, length, session->user_data); + } return 0; } @@ -909,7 +918,8 @@ int spdylay_session_recv(spdylay_session *session) } } session->iframe.state = SPDYLAY_RECV_PAYLOAD; - payloadlen = spdylay_get_uint32(&session->ibuf.mark[4]) & 0xffffff; + payloadlen = spdylay_get_uint32(&session->ibuf.mark[4]) & + SPDYLAY_LENGTH_MASK; memcpy(session->iframe.headbuf, session->ibuf.mark, SPDYLAY_HEAD_LEN); session->ibuf.mark += SPDYLAY_HEAD_LEN; if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { @@ -921,11 +931,6 @@ int spdylay_session_recv(spdylay_session *session) } session->iframe.off = 0; } else { - int32_t stream_id; - /* data frame */ - /* For data frame, We dont' buffer data. Instead, just pass - received data to callback function. */ - stream_id = spdylay_get_uint32(session->iframe.headbuf) & 0x7fffffff; /* TODO validate stream id here */ session->iframe.len = payloadlen; session->iframe.off = 0; @@ -947,8 +952,21 @@ int spdylay_session_recv(spdylay_session *session) } bufavail = spdylay_inbound_buffer_avail(&session->ibuf); readlen = bufavail < rempayloadlen ? bufavail : rempayloadlen; - if(session->iframe.buf != NULL) { + if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { memcpy(session->iframe.buf, session->ibuf.mark, readlen); + } else if(session->callbacks.on_data_chunk_recv_callback) { + int32_t stream_id; + uint8_t flags; + /* For data frame, We don't buffer data. Instead, just pass + received data to callback function. */ + stream_id = spdylay_get_uint32(session->iframe.headbuf) & + SPDYLAY_STREAM_ID_MASK; + flags = session->iframe.headbuf[4]; + session->callbacks.on_data_chunk_recv_callback(session, stream_id, + flags, + session->ibuf.mark, + readlen, + session->user_data); } session->iframe.off += readlen; session->ibuf.mark += readlen;