Added handling of received PING
This commit is contained in:
parent
9461147968
commit
d1c4c59aad
|
@ -51,6 +51,8 @@ AC_CHECK_LIB([cunit], [CU_initialize_registry],
|
|||
[have_cunit=yes], [have_cunit=no])
|
||||
AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ])
|
||||
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt])
|
||||
|
||||
# Checks for header files.
|
||||
AC_CHECK_HEADERS([ \
|
||||
arpa/inet.h \
|
||||
|
@ -58,6 +60,7 @@ AC_CHECK_HEADERS([ \
|
|||
stdint.h \
|
||||
stdlib.h \
|
||||
string.h \
|
||||
time.h \
|
||||
unistd.h \
|
||||
])
|
||||
|
||||
|
|
|
@ -179,11 +179,18 @@ typedef void (*spdylay_on_invalid_ctrl_recv_callback)
|
|||
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
|
||||
void *user_data);
|
||||
|
||||
/*
|
||||
* Callback function invoked when PING reply is received from peer.
|
||||
*/
|
||||
typedef void (*spdylay_on_ping_recv_callback)
|
||||
(spdylay_session *session, const struct timespec *rtt, 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_session_callbacks;
|
||||
|
||||
int spdylay_session_client_new(spdylay_session **session_ptr,
|
||||
|
@ -215,6 +222,8 @@ int spdylay_reply_submit(spdylay_session *session,
|
|||
int32_t stream_id, const char **nv,
|
||||
spdylay_data_provider *data_prd);
|
||||
|
||||
int spdylay_submit_ping(spdylay_session *session);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -71,8 +71,14 @@ int spdylay_session_client_new(spdylay_session **session_ptr,
|
|||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
memset(*session_ptr, 0, sizeof(spdylay_session));
|
||||
|
||||
/* IDs for use in client */
|
||||
(*session_ptr)->next_stream_id = 1;
|
||||
(*session_ptr)->last_recv_stream_id = 0;
|
||||
(*session_ptr)->next_unique_id = 1;
|
||||
|
||||
(*session_ptr)->last_ping_unique_id = 0;
|
||||
memset(&(*session_ptr)->last_ping_time, 0, sizeof(struct timespec));
|
||||
|
||||
r = spdylay_zlib_deflate_hd_init(&(*session_ptr)->hd_deflater);
|
||||
if(r != 0) {
|
||||
|
@ -131,10 +137,12 @@ static void spdylay_outbound_item_free(spdylay_outbound_item *item)
|
|||
case SPDYLAY_RST_STREAM:
|
||||
spdylay_frame_rst_stream_free(&item->frame->rst_stream);
|
||||
break;
|
||||
case SPDYLAY_PING:
|
||||
spdylay_frame_ping_free(&item->frame->ping);
|
||||
break;
|
||||
case SPDYLAY_HEADERS:
|
||||
/* Currently we don't have any API to send HEADERS frame, so this
|
||||
is unreachable. */
|
||||
abort();
|
||||
spdylay_frame_headers_free(&item->frame->headers);
|
||||
break;
|
||||
case SPDYLAY_DATA:
|
||||
spdylay_frame_data_free(&item->frame->data);
|
||||
break;
|
||||
|
@ -195,6 +203,10 @@ int spdylay_session_add_frame(spdylay_session *session,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case SPDYLAY_PING:
|
||||
/* Ping has "height" priority. Give it -1. */
|
||||
item->pri = -1;
|
||||
break;
|
||||
case SPDYLAY_HEADERS:
|
||||
/* Currently we don't have any API to send HEADERS frame, so this
|
||||
is unreachable. */
|
||||
|
@ -339,6 +351,12 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case SPDYLAY_PING:
|
||||
framebuflen = spdylay_frame_pack_ping(&framebuf, &item->frame->ping);
|
||||
if(framebuflen < 0) {
|
||||
return framebuflen;
|
||||
}
|
||||
break;
|
||||
case SPDYLAY_HEADERS:
|
||||
/* Currently we don't have any API to send HEADERS frame, so this
|
||||
is unreachable. */
|
||||
|
@ -370,7 +388,7 @@ static void spdylay_active_outbound_item_reset
|
|||
memset(aob, 0, sizeof(spdylay_active_outbound_item));
|
||||
}
|
||||
|
||||
static spdylay_outbound_item* spdylay_session_get_ob_pq_top
|
||||
spdylay_outbound_item* spdylay_session_get_ob_pq_top
|
||||
(spdylay_session *session)
|
||||
{
|
||||
return (spdylay_outbound_item*)spdylay_pq_top(&session->ob_pq);
|
||||
|
@ -401,6 +419,13 @@ static int spdylay_session_after_frame_sent(spdylay_session *session)
|
|||
case SPDYLAY_RST_STREAM:
|
||||
spdylay_session_close_stream(session, frame->rst_stream.stream_id);
|
||||
break;
|
||||
case SPDYLAY_PING:
|
||||
/* We record the time now and show application code RTT when
|
||||
reply PING is received. */
|
||||
session->last_ping_unique_id = frame->ping.unique_id;
|
||||
/* TODO If clock_gettime() fails, what should we do? */
|
||||
clock_gettime(CLOCK_MONOTONIC, &session->last_ping_time);
|
||||
break;
|
||||
case SPDYLAY_HEADERS:
|
||||
/* Currently we don't have any API to send HEADERS frame, so this
|
||||
is unreachable. */
|
||||
|
@ -688,6 +713,39 @@ int spdylay_session_on_syn_reply_received(spdylay_session *session,
|
|||
return r;
|
||||
}
|
||||
|
||||
int spdylay_session_on_ping_received(spdylay_session *session,
|
||||
spdylay_frame *frame)
|
||||
{
|
||||
int r = 0;
|
||||
if(frame->ping.unique_id != 0) {
|
||||
if(session->last_ping_unique_id == frame->ping.unique_id) {
|
||||
/* This is ping reply from peer */
|
||||
struct timespec rtt;
|
||||
clock_gettime(CLOCK_MONOTONIC, &rtt);
|
||||
rtt.tv_nsec -= session->last_ping_time.tv_nsec;
|
||||
if(rtt.tv_nsec < 0) {
|
||||
rtt.tv_nsec += 1000000000;
|
||||
--rtt.tv_sec;
|
||||
}
|
||||
rtt.tv_sec -= session->last_ping_time.tv_sec;
|
||||
/* Assign 0 to last_ping_unique_id so that we can ignore same
|
||||
ID. */
|
||||
session->last_ping_unique_id = 0;
|
||||
if(session->callbacks.on_ping_recv_callback) {
|
||||
session->callbacks.on_ping_recv_callback(session, &rtt,
|
||||
session->user_data);
|
||||
}
|
||||
spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_PING, frame);
|
||||
} else if((session->server && frame->ping.unique_id % 2 == 1) ||
|
||||
(!session->server && frame->ping.unique_id % 2 == 0)) {
|
||||
/* Peer sent ping, so ping it back */
|
||||
r = spdylay_session_add_ping(session, frame->ping.unique_id);
|
||||
spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_PING, frame);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int spdylay_session_on_headers_received(spdylay_session *session,
|
||||
spdylay_frame *frame)
|
||||
{
|
||||
|
@ -770,6 +828,17 @@ int spdylay_session_process_ctrl_frame(spdylay_session *session)
|
|||
spdylay_frame_syn_reply_free(&frame.syn_reply);
|
||||
}
|
||||
break;
|
||||
case SPDYLAY_PING:
|
||||
r = spdylay_frame_unpack_ping(&frame.ping,
|
||||
session->iframe.headbuf,
|
||||
sizeof(session->iframe.headbuf),
|
||||
session->iframe.buf,
|
||||
session->iframe.len);
|
||||
if(r == 0) {
|
||||
r = spdylay_session_on_ping_received(session, &frame);
|
||||
spdylay_frame_ping_free(&frame.ping);
|
||||
}
|
||||
break;
|
||||
case SPDYLAY_HEADERS:
|
||||
r = spdylay_frame_unpack_headers(&frame.headers,
|
||||
session->iframe.headbuf,
|
||||
|
@ -890,13 +959,35 @@ int spdylay_session_want_write(spdylay_session *session)
|
|||
return session->aob.item != NULL || !spdylay_pq_empty(&session->ob_pq);
|
||||
}
|
||||
|
||||
int spdylay_session_add_ping(spdylay_session *session, uint32_t unique_id)
|
||||
{
|
||||
int r;
|
||||
spdylay_frame *frame;
|
||||
frame = malloc(sizeof(spdylay_frame));
|
||||
if(frame == NULL) {
|
||||
return SPDYLAY_ERR_NOMEM;
|
||||
}
|
||||
spdylay_frame_ping_init(&frame->ping, unique_id);
|
||||
r = spdylay_session_add_frame(session, SPDYLAY_PING, frame);
|
||||
if(r != 0) {
|
||||
spdylay_frame_ping_free(&frame->ping);
|
||||
free(frame);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int spdylay_submit_ping(spdylay_session *session)
|
||||
{
|
||||
return spdylay_session_add_ping(session,
|
||||
spdylay_session_get_next_unique_id(session));
|
||||
}
|
||||
|
||||
int spdylay_reply_submit(spdylay_session *session,
|
||||
int32_t stream_id, const char **nv,
|
||||
spdylay_data_provider *data_prd)
|
||||
{
|
||||
int r;
|
||||
spdylay_frame *frame;
|
||||
spdylay_frame *data_frame = NULL;
|
||||
char **nv_copy;
|
||||
uint8_t flags = 0;
|
||||
frame = malloc(sizeof(spdylay_frame));
|
||||
|
@ -920,6 +1011,7 @@ int spdylay_reply_submit(spdylay_session *session,
|
|||
return r;
|
||||
}
|
||||
if(data_prd != NULL) {
|
||||
spdylay_frame *data_frame;
|
||||
/* TODO If error is not FATAL, we should add RST_STREAM frame to
|
||||
cancel this stream. */
|
||||
data_frame = malloc(sizeof(spdylay_frame));
|
||||
|
@ -1001,3 +1093,15 @@ ssize_t spdylay_session_pack_data_overwrite(spdylay_session *session,
|
|||
frame->flags = flags;
|
||||
return r+8;
|
||||
}
|
||||
|
||||
uint32_t spdylay_session_get_next_unique_id(spdylay_session *session)
|
||||
{
|
||||
if(session->next_unique_id > SPDYLAY_MAX_UNIQUE_ID) {
|
||||
if(session->server) {
|
||||
session->next_unique_id = 2;
|
||||
} else {
|
||||
session->next_unique_id = 1;
|
||||
}
|
||||
}
|
||||
return session->next_unique_id++;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <spdylay/spdylay.h>
|
||||
#include "spdylay_pq.h"
|
||||
#include "spdylay_map.h"
|
||||
|
@ -62,6 +64,10 @@ typedef enum {
|
|||
|
||||
#define SPDYLAY_HEAD_LEN 8
|
||||
|
||||
/* Maximum unique ID in use for PING. If unique ID exeeds this number,
|
||||
it wraps to 1 (client) or 2 (server) */
|
||||
#define SPDYLAY_MAX_UNIQUE_ID ((1u << 31)-1)
|
||||
|
||||
typedef struct {
|
||||
spdylay_inbound_state state;
|
||||
uint8_t headbuf[SPDYLAY_HEAD_LEN];
|
||||
|
@ -77,6 +83,9 @@ typedef struct spdylay_session {
|
|||
uint8_t server;
|
||||
int32_t next_stream_id;
|
||||
int32_t last_recv_stream_id;
|
||||
/* Counter of unique ID of PING. Wraps when it exceeds
|
||||
SPDYLAY_MAX_UNIQUE_ID */
|
||||
uint32_t next_unique_id;
|
||||
|
||||
spdylay_map /* <spdylay_stream*> */ streams;
|
||||
spdylay_pq /* <spdylay_outbound_item*> */ ob_pq;
|
||||
|
@ -89,6 +98,11 @@ typedef struct spdylay_session {
|
|||
spdylay_zlib hd_deflater;
|
||||
spdylay_zlib hd_inflater;
|
||||
|
||||
/* The last unique ID sent to the peer. */
|
||||
uint32_t last_ping_unique_id;
|
||||
/* Time stamp when last ping is sent. */
|
||||
struct timespec last_ping_time;
|
||||
|
||||
spdylay_session_callbacks callbacks;
|
||||
void *user_data;
|
||||
} spdylay_session;
|
||||
|
@ -102,6 +116,8 @@ int spdylay_session_add_frame(spdylay_session *session,
|
|||
int spdylay_session_add_rst_stream(spdylay_session *session,
|
||||
int32_t stream_id, uint32_t status_code);
|
||||
|
||||
int spdylay_session_add_ping(spdylay_session *session, uint32_t unique_id);
|
||||
|
||||
/*
|
||||
* Creates new stream in |session| with stream ID |stream_id|,
|
||||
* priority |pri| and flags |flags|. Currently, |flags| &
|
||||
|
@ -173,4 +189,15 @@ ssize_t spdylay_session_pack_data_overwrite(spdylay_session *session,
|
|||
uint8_t *buf, size_t len,
|
||||
spdylay_data *frame);
|
||||
|
||||
/*
|
||||
* Returns next unique ID which can be used with PING.
|
||||
*/
|
||||
uint32_t spdylay_session_get_next_unique_id(spdylay_session *session);
|
||||
|
||||
/*
|
||||
* Returns top of outbound frame queue. This function returns NULL if
|
||||
* queue is empty.
|
||||
*/
|
||||
spdylay_outbound_item* spdylay_session_get_ob_pq_top(spdylay_session *session);
|
||||
|
||||
#endif /* SPDYLAY_SESSION_H */
|
||||
|
|
|
@ -84,6 +84,8 @@ int main()
|
|||
test_spdylay_session_reply_fail) ||
|
||||
!CU_add_test(pSuite, "session_on_headers_received",
|
||||
test_spdylay_session_on_headers_received) ||
|
||||
!CU_add_test(pSuite, "session_on_ping_received",
|
||||
test_spdylay_session_on_ping_received) ||
|
||||
!CU_add_test(pSuite, "frame_unpack_nv", test_spdylay_frame_unpack_nv) ||
|
||||
!CU_add_test(pSuite, "frame_count_nv_space",
|
||||
test_spdylay_frame_count_nv_space) ||
|
||||
|
|
|
@ -49,7 +49,7 @@ typedef struct {
|
|||
typedef struct {
|
||||
accumulator *acc;
|
||||
scripted_data_feed *df;
|
||||
int valid, invalid;
|
||||
int valid, invalid, ping_recv;
|
||||
size_t data_source_length;
|
||||
} my_user_data;
|
||||
|
||||
|
@ -122,6 +122,14 @@ static void on_invalid_ctrl_recv_callback(spdylay_session *session,
|
|||
++ud->invalid;
|
||||
}
|
||||
|
||||
static void on_ping_recv_callback(spdylay_session *session,
|
||||
const struct timespec *rtt,
|
||||
void *user_data)
|
||||
{
|
||||
my_user_data *ud = (my_user_data*)user_data;
|
||||
++ud->ping_recv;
|
||||
}
|
||||
|
||||
static ssize_t fixed_length_data_source_read_callback
|
||||
(spdylay_session *session, uint8_t *buf, size_t len, int *eof,
|
||||
spdylay_data_source *source, void *user_data)
|
||||
|
@ -533,3 +541,44 @@ void test_spdylay_session_on_headers_received()
|
|||
spdylay_frame_headers_free(&frame.headers);
|
||||
spdylay_session_del(session);
|
||||
}
|
||||
|
||||
void test_spdylay_session_on_ping_received()
|
||||
{
|
||||
spdylay_session *session;
|
||||
spdylay_session_callbacks callbacks = {
|
||||
NULL,
|
||||
NULL,
|
||||
on_ctrl_recv_callback,
|
||||
on_invalid_ctrl_recv_callback,
|
||||
on_ping_recv_callback
|
||||
};
|
||||
my_user_data user_data;
|
||||
const char *nv[] = { NULL };
|
||||
spdylay_frame frame;
|
||||
spdylay_outbound_item *top;
|
||||
uint32_t unique_id;
|
||||
user_data.valid = 0;
|
||||
user_data.invalid = 0;
|
||||
user_data.ping_recv = 0;
|
||||
|
||||
spdylay_session_client_new(&session, &callbacks, &user_data);
|
||||
unique_id = 2;
|
||||
spdylay_frame_ping_init(&frame.ping, unique_id);
|
||||
|
||||
CU_ASSERT(0 == spdylay_session_on_ping_received(session, &frame));
|
||||
CU_ASSERT(1 == user_data.valid);
|
||||
CU_ASSERT(0 == user_data.ping_recv);
|
||||
top = spdylay_session_get_ob_pq_top(session);
|
||||
CU_ASSERT(SPDYLAY_PING == top->frame_type);
|
||||
CU_ASSERT(unique_id == top->frame->ping.unique_id);
|
||||
|
||||
session->last_ping_unique_id = 1;
|
||||
frame.ping.unique_id = 1;
|
||||
|
||||
CU_ASSERT(0 == spdylay_session_on_ping_received(session, &frame));
|
||||
CU_ASSERT(2 == user_data.valid);
|
||||
CU_ASSERT(1 == user_data.ping_recv);
|
||||
|
||||
spdylay_frame_ping_free(&frame.ping);
|
||||
spdylay_session_del(session);
|
||||
}
|
||||
|
|
|
@ -35,5 +35,6 @@ void test_spdylay_session_send_syn_reply();
|
|||
void test_spdylay_reply_submit();
|
||||
void test_spdylay_session_reply_fail();
|
||||
void test_spdylay_session_on_headers_received();
|
||||
void test_spdylay_session_on_ping_received();
|
||||
|
||||
#endif // SPDYLAY_SESSION_TEST_H
|
||||
|
|
Loading…
Reference in New Issue