Update tutorial
This commit is contained in:
parent
2b465ee65f
commit
f2cd057e89
|
@ -22,11 +22,9 @@ protocol over the SSL/TLS transport. In this tutorial, we use
|
||||||
`nghttp2_select_next_protocol()` function to select the HTTP/2
|
`nghttp2_select_next_protocol()` function to select the HTTP/2
|
||||||
protocol the library supports::
|
protocol the library supports::
|
||||||
|
|
||||||
static int select_next_proto_cb(SSL* ssl,
|
static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
|
||||||
unsigned char **out, unsigned char *outlen,
|
unsigned char *outlen, const unsigned char *in,
|
||||||
const unsigned char *in, unsigned int inlen,
|
unsigned int inlen, void *arg _U_) {
|
||||||
void *arg)
|
|
||||||
{
|
|
||||||
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
||||||
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
|
||||||
}
|
}
|
||||||
|
@ -36,8 +34,7 @@ protocol the library supports::
|
||||||
The callback is set to the SSL_CTX object using
|
The callback is set to the SSL_CTX object using
|
||||||
``SSL_CTX_set_next_proto_select_cb()`` function::
|
``SSL_CTX_set_next_proto_select_cb()`` function::
|
||||||
|
|
||||||
static SSL_CTX* create_ssl_ctx(void)
|
static SSL_CTX *create_ssl_ctx(void) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||||
if (!ssl_ctx) {
|
if (!ssl_ctx) {
|
||||||
|
@ -45,7 +42,8 @@ The callback is set to the SSL_CTX object using
|
||||||
ERR_error_string(ERR_get_error(), NULL));
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
}
|
}
|
||||||
SSL_CTX_set_options(ssl_ctx,
|
SSL_CTX_set_options(ssl_ctx,
|
||||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION |
|
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||||
|
SSL_OP_NO_COMPRESSION |
|
||||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
|
||||||
return ssl_ctx;
|
return ssl_ctx;
|
||||||
|
@ -91,20 +89,17 @@ respectively.
|
||||||
Then we call function ``initiate_connection()`` to start connecting to
|
Then we call function ``initiate_connection()`` to start connecting to
|
||||||
the remote server::
|
the remote server::
|
||||||
|
|
||||||
static void initiate_connection(struct event_base *evbase,
|
static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
|
||||||
SSL_CTX *ssl_ctx,
|
|
||||||
const char *host, uint16_t port,
|
const char *host, uint16_t port,
|
||||||
http2_session_data *session_data)
|
http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
struct bufferevent *bev;
|
struct bufferevent *bev;
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
|
|
||||||
ssl = create_ssl(ssl_ctx);
|
ssl = create_ssl(ssl_ctx);
|
||||||
bev = bufferevent_openssl_socket_new(evbase, -1, ssl,
|
bev = bufferevent_openssl_socket_new(
|
||||||
BUFFEREVENT_SSL_CONNECTING,
|
evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
|
||||||
BEV_OPT_DEFER_CALLBACKS |
|
BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
|
||||||
BEV_OPT_CLOSE_ON_FREE);
|
|
||||||
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
|
||||||
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
|
||||||
AF_UNSPEC, host, port);
|
AF_UNSPEC, host, port);
|
||||||
|
@ -122,8 +117,7 @@ The ``eventcb()`` is invoked by libevent event loop when an event
|
||||||
(e.g., connection has been established, timeout, etc) happens on the
|
(e.g., connection has been established, timeout, etc) happens on the
|
||||||
underlying network socket::
|
underlying network socket::
|
||||||
|
|
||||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
static void eventcb(struct bufferevent *bev, short events, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (events & BEV_EVENT_CONNECTED) {
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
int fd = bufferevent_getfd(bev);
|
int fd = bufferevent_getfd(bev);
|
||||||
|
@ -154,28 +148,27 @@ event, we just simply tear down the connection. The
|
||||||
finished successfully. We first initialize nghttp2 session object in
|
finished successfully. We first initialize nghttp2 session object in
|
||||||
``initialize_nghttp2_session()`` function::
|
``initialize_nghttp2_session()`` function::
|
||||||
|
|
||||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
|
||||||
nghttp2_session_callbacks_new(&callbacks);
|
nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||||
(callbacks, on_data_chunk_recv_callback);
|
callbacks, on_data_chunk_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_header_callback
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||||
(callbacks, on_header_callback);
|
on_header_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
(callbacks, on_begin_headers_callback);
|
callbacks, on_begin_headers_callback);
|
||||||
|
|
||||||
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
nghttp2_session_client_new(&session_data->session, callbacks, session_data);
|
||||||
|
|
||||||
|
@ -196,18 +189,15 @@ which is 24 bytes magic byte sequence
|
||||||
transmission of client connection header is done in
|
transmission of client connection header is done in
|
||||||
``send_client_connection_header()``::
|
``send_client_connection_header()``::
|
||||||
|
|
||||||
static void send_client_connection_header(http2_session_data *session_data)
|
static void send_client_connection_header(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_settings_entry iv[1] = {
|
nghttp2_settings_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||||
};
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
bufferevent_write(session_data->bev,
|
bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||||
NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
|
||||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
|
||||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||||
iv, ARRLEN(iv));
|
ARRLEN(iv));
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
|
||||||
}
|
}
|
||||||
|
@ -225,23 +215,21 @@ used, which is described about later.
|
||||||
After the transmission of client connection header, we enqueue HTTP
|
After the transmission of client connection header, we enqueue HTTP
|
||||||
request in ``submit_request()`` function::
|
request in ``submit_request()`` function::
|
||||||
|
|
||||||
static void submit_request(http2_session_data *session_data)
|
static void submit_request(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
http2_stream_data *stream_data = session_data->stream_data;
|
http2_stream_data *stream_data = session_data->stream_data;
|
||||||
const char *uri = stream_data->uri;
|
const char *uri = stream_data->uri;
|
||||||
const struct http_parser_url *u = stream_data->u;
|
const struct http_parser_url *u = stream_data->u;
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
MAKE_NV2(":method", "GET"),
|
MAKE_NV2(":method", "GET"),
|
||||||
MAKE_NV(":scheme",
|
MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
|
||||||
&uri[u->field_data[UF_SCHEMA].off], u->field_data[UF_SCHEMA].len),
|
u->field_data[UF_SCHEMA].len),
|
||||||
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
|
||||||
MAKE_NV(":path", stream_data->path, stream_data->pathlen)
|
MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
|
||||||
};
|
|
||||||
fprintf(stderr, "Request headers:\n");
|
fprintf(stderr, "Request headers:\n");
|
||||||
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
print_headers(stderr, hdrs, ARRLEN(hdrs));
|
||||||
stream_id = nghttp2_submit_request(session_data->session, NULL,
|
stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
|
||||||
hdrs, ARRLEN(hdrs), NULL, stream_data);
|
ARRLEN(hdrs), NULL, stream_data);
|
||||||
if (stream_id < 0) {
|
if (stream_id < 0) {
|
||||||
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
|
||||||
}
|
}
|
||||||
|
@ -261,8 +249,7 @@ this request.
|
||||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||||
data is available to read in the bufferevent input buffer::
|
data is available to read in the bufferevent input buffer::
|
||||||
|
|
||||||
static void readcb(struct bufferevent *bev, void *ptr)
|
static void readcb(struct bufferevent *bev, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
ssize_t readlen;
|
ssize_t readlen;
|
||||||
struct evbuffer *input = bufferevent_get_input(bev);
|
struct evbuffer *input = bufferevent_get_input(bev);
|
||||||
|
@ -293,8 +280,7 @@ invoke nghttp2 callbacks and also queue frames. Since there may be
|
||||||
pending frames, we call ``session_send()`` function to send those
|
pending frames, we call ``session_send()`` function to send those
|
||||||
frames. The ``session_send()`` function is defined as follows::
|
frames. The ``session_send()`` function is defined as follows::
|
||||||
|
|
||||||
static int session_send(http2_session_data *session_data)
|
static int session_send(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = nghttp2_session_send(session_data->session);
|
rv = nghttp2_session_send(session_data->session);
|
||||||
|
@ -310,10 +296,8 @@ format and call ``send_callback()`` function of type
|
||||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||||
follows::
|
follows::
|
||||||
|
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
int flags, void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
struct bufferevent *bev = session_data->bev;
|
struct bufferevent *bev = session_data->bev;
|
||||||
bufferevent_write(bev, data, length);
|
bufferevent_write(bev, data, length);
|
||||||
|
@ -336,8 +320,7 @@ buffered data, see the ``send_callback()`` in the server tutorial.
|
||||||
The third bufferevent callback is ``writecb()``, which is invoked when
|
The third bufferevent callback is ``writecb()``, which is invoked when
|
||||||
all data written in the bufferevent output buffer have been sent::
|
all data written in the bufferevent output buffer have been sent::
|
||||||
|
|
||||||
static void writecb(struct bufferevent *bev, void *ptr)
|
static void writecb(struct bufferevent *bev _U_, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
if (nghttp2_session_want_read(session_data->session) == 0 &&
|
||||||
nghttp2_session_want_write(session_data->session) == 0 &&
|
nghttp2_session_want_write(session_data->session) == 0 &&
|
||||||
|
@ -367,13 +350,11 @@ Let's describe remaining nghttp2 callbacks we setup in
|
||||||
Each request header name/value pair is emitted via
|
Each request header name/value pair is emitted via
|
||||||
``on_header_callback`` function::
|
``on_header_callback`` function::
|
||||||
|
|
||||||
static int on_header_callback(nghttp2_session *session,
|
static int on_header_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame, const uint8_t *name,
|
||||||
const uint8_t *name, size_t namelen,
|
size_t namelen, const uint8_t *value,
|
||||||
const uint8_t *value, size_t valuelen,
|
size_t valuelen, uint8_t flags _U_,
|
||||||
uint8_t flags,
|
void *user_data) {
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
|
@ -392,9 +373,8 @@ In this tutorial, we just print the name/value pair.
|
||||||
After all name/value pairs are emitted for a frame,
|
After all name/value pairs are emitted for a frame,
|
||||||
``on_frame_recv_callback`` function is called::
|
``on_frame_recv_callback`` function is called::
|
||||||
|
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session _U_,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame, void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
|
@ -415,11 +395,10 @@ its stream ID.
|
||||||
The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
|
The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
|
||||||
of data is received from the remote peer::
|
of data is received from the remote peer::
|
||||||
|
|
||||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
|
||||||
int32_t stream_id,
|
uint8_t flags _U_, int32_t stream_id,
|
||||||
const uint8_t *data, size_t len,
|
const uint8_t *data, size_t len,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
if (session_data->stream_data->stream_id == stream_id) {
|
if (session_data->stream_data->stream_id == stream_id) {
|
||||||
fwrite(data, len, 1, stdout);
|
fwrite(data, len, 1, stdout);
|
||||||
|
@ -435,17 +414,15 @@ some binary data.
|
||||||
The ``on_stream_close_callback()`` function is invoked when the stream
|
The ``on_stream_close_callback()`` function is invoked when the stream
|
||||||
is about to close::
|
is about to close::
|
||||||
|
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
|
||||||
nghttp2_error_code error_code,
|
nghttp2_error_code error_code,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
if (session_data->stream_data->stream_id == stream_id) {
|
if (session_data->stream_data->stream_id == stream_id) {
|
||||||
fprintf(stderr, "Stream %d closed with error_code=%d\n",
|
fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
|
||||||
stream_id, error_code);
|
error_code);
|
||||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
|
|
@ -29,17 +29,17 @@ time::
|
||||||
static unsigned char next_proto_list[256];
|
static unsigned char next_proto_list[256];
|
||||||
static size_t next_proto_list_len;
|
static size_t next_proto_list_len;
|
||||||
|
|
||||||
static int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
|
static int next_proto_cb(SSL *s _U_, const unsigned char **data,
|
||||||
void *arg)
|
unsigned int *len, void *arg _U_) {
|
||||||
{
|
|
||||||
*data = next_proto_list;
|
*data = next_proto_list;
|
||||||
*len = (unsigned int)next_proto_list_len;
|
*len = (unsigned int)next_proto_list_len;
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SSL_CTX* create_ssl_ctx(const char *key_file, const char *cert_file)
|
static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
|
||||||
{
|
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
|
EC_KEY *ecdh;
|
||||||
|
|
||||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||||
|
|
||||||
...
|
...
|
||||||
|
@ -104,8 +104,7 @@ We first create a listener object to accept incoming connections. We use
|
||||||
libevent's ``struct evconnlistener`` for this purpose::
|
libevent's ``struct evconnlistener`` for this purpose::
|
||||||
|
|
||||||
static void start_listen(struct event_base *evbase, const char *service,
|
static void start_listen(struct event_base *evbase, const char *service,
|
||||||
app_context *app_ctx)
|
app_context *app_ctx) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
struct addrinfo *res, *rp;
|
struct addrinfo *res, *rp;
|
||||||
|
@ -116,7 +115,7 @@ libevent's ``struct evconnlistener`` for this purpose::
|
||||||
hints.ai_flags = AI_PASSIVE;
|
hints.ai_flags = AI_PASSIVE;
|
||||||
#ifdef AI_ADDRCONFIG
|
#ifdef AI_ADDRCONFIG
|
||||||
hints.ai_flags |= AI_ADDRCONFIG;
|
hints.ai_flags |= AI_ADDRCONFIG;
|
||||||
#endif // AI_ADDRCONFIG
|
#endif /* AI_ADDRCONFIG */
|
||||||
|
|
||||||
rv = getaddrinfo(NULL, service, &hints, &res);
|
rv = getaddrinfo(NULL, service, &hints, &res);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
|
@ -124,11 +123,12 @@ libevent's ``struct evconnlistener`` for this purpose::
|
||||||
}
|
}
|
||||||
for (rp = res; rp; rp = rp->ai_next) {
|
for (rp = res; rp; rp = rp->ai_next) {
|
||||||
struct evconnlistener *listener;
|
struct evconnlistener *listener;
|
||||||
listener = evconnlistener_new_bind(evbase, acceptcb, app_ctx,
|
listener = evconnlistener_new_bind(
|
||||||
LEV_OPT_CLOSE_ON_FREE |
|
evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
|
||||||
LEV_OPT_REUSEABLE, -1,
|
16, rp->ai_addr, rp->ai_addrlen);
|
||||||
rp->ai_addr, rp->ai_addrlen);
|
|
||||||
if (listener) {
|
if (listener) {
|
||||||
|
freeaddrinfo(res);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,9 +138,8 @@ libevent's ``struct evconnlistener`` for this purpose::
|
||||||
We specify the ``acceptcb`` callback which is called when a new connection is
|
We specify the ``acceptcb`` callback which is called when a new connection is
|
||||||
accepted::
|
accepted::
|
||||||
|
|
||||||
static void acceptcb(struct evconnlistener *listener, int fd,
|
static void acceptcb(struct evconnlistener *listener _U_, int fd,
|
||||||
struct sockaddr *addr, int addrlen, void *arg)
|
struct sockaddr *addr, int addrlen, void *arg) {
|
||||||
{
|
|
||||||
app_context *app_ctx = (app_context *)arg;
|
app_context *app_ctx = (app_context *)arg;
|
||||||
http2_session_data *session_data;
|
http2_session_data *session_data;
|
||||||
|
|
||||||
|
@ -158,8 +157,7 @@ The ``eventcb()`` callback is invoked by the libevent event loop when an event
|
||||||
(e.g., connection has been established, timeout, etc) happens on the
|
(e.g., connection has been established, timeout, etc) happens on the
|
||||||
underlying network socket::
|
underlying network socket::
|
||||||
|
|
||||||
static void eventcb(struct bufferevent *bev, short events, void *ptr)
|
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (events & BEV_EVENT_CONNECTED) {
|
if (events & BEV_EVENT_CONNECTED) {
|
||||||
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
fprintf(stderr, "%s connected\n", session_data->client_addr);
|
||||||
|
@ -195,8 +193,7 @@ communication.
|
||||||
We initialize a nghttp2 session object which is done in
|
We initialize a nghttp2 session object which is done in
|
||||||
``initialize_nghttp2_session()``::
|
``initialize_nghttp2_session()``::
|
||||||
|
|
||||||
static void initialize_nghttp2_session(http2_session_data *session_data)
|
static void initialize_nghttp2_session(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_option *option;
|
nghttp2_option *option;
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks *callbacks;
|
||||||
|
|
||||||
|
@ -210,19 +207,17 @@ We initialize a nghttp2 session object which is done in
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_frame_recv_callback
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||||
(callbacks, on_frame_recv_callback);
|
on_frame_recv_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_stream_close_callback
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||||
(callbacks, on_stream_close_callback);
|
callbacks, on_stream_close_callback);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_header_callback
|
|
||||||
(callbacks, on_header_callback);
|
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_on_begin_headers_callback
|
|
||||||
(callbacks, on_begin_headers_callback);
|
|
||||||
|
|
||||||
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||||
|
on_header_callback);
|
||||||
|
|
||||||
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||||
|
callbacks, on_begin_headers_callback);
|
||||||
|
|
||||||
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
|
||||||
option);
|
option);
|
||||||
|
@ -242,15 +237,13 @@ saves some lines of application code.
|
||||||
After initialization of the nghttp2 session object, we are going to send
|
After initialization of the nghttp2 session object, we are going to send
|
||||||
a server connection header in ``send_server_connection_header()``::
|
a server connection header in ``send_server_connection_header()``::
|
||||||
|
|
||||||
static int send_server_connection_header(http2_session_data *session_data)
|
static int send_server_connection_header(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
nghttp2_settings_entry iv[1] = {
|
nghttp2_settings_entry iv[1] = {
|
||||||
{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||||
};
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
|
||||||
iv, ARRLEN(iv));
|
ARRLEN(iv));
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
warnx("Fatal error: %s", nghttp2_strerror(rv));
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -272,8 +265,7 @@ have to process them here since libevent won't invoke callback functions for
|
||||||
this pending data. To process the received data, we call the
|
this pending data. To process the received data, we call the
|
||||||
``session_recv()`` function::
|
``session_recv()`` function::
|
||||||
|
|
||||||
static int session_recv(http2_session_data *session_data)
|
static int session_recv(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
ssize_t readlen;
|
ssize_t readlen;
|
||||||
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
struct evbuffer *input = bufferevent_get_input(session_data->bev);
|
||||||
size_t datalen = evbuffer_get_length(input);
|
size_t datalen = evbuffer_get_length(input);
|
||||||
|
@ -301,8 +293,7 @@ nghttp2 callbacks and also queue outgoing frames. Since there may be pending
|
||||||
outgoing frames, we call ``session_send()`` function to send off those
|
outgoing frames, we call ``session_send()`` function to send off those
|
||||||
frames. The ``session_send()`` function is defined as follows::
|
frames. The ``session_send()`` function is defined as follows::
|
||||||
|
|
||||||
static int session_send(http2_session_data *session_data)
|
static int session_send(http2_session_data *session_data) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
rv = nghttp2_session_send(session_data->session);
|
rv = nghttp2_session_send(session_data->session);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
|
@ -317,10 +308,8 @@ format and calls ``send_callback()`` of type
|
||||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||||
follows::
|
follows::
|
||||||
|
|
||||||
static ssize_t send_callback(nghttp2_session *session,
|
static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
|
||||||
const uint8_t *data, size_t length,
|
size_t length, int flags _U_, void *user_data) {
|
||||||
int flags, void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
struct bufferevent *bev = session_data->bev;
|
struct bufferevent *bev = session_data->bev;
|
||||||
/* Avoid excessive buffering in server side. */
|
/* Avoid excessive buffering in server side. */
|
||||||
|
@ -350,8 +339,7 @@ calling send_callback.
|
||||||
The next bufferevent callback is ``readcb()``, which is invoked when
|
The next bufferevent callback is ``readcb()``, which is invoked when
|
||||||
data is available to read in the bufferevent input buffer::
|
data is available to read in the bufferevent input buffer::
|
||||||
|
|
||||||
static void readcb(struct bufferevent *bev, void *ptr)
|
static void readcb(struct bufferevent *bev _U_, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (session_recv(session_data) != 0) {
|
if (session_recv(session_data) != 0) {
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
|
@ -365,8 +353,7 @@ data.
|
||||||
The third bufferevent callback is ``writecb()``, which is invoked when all
|
The third bufferevent callback is ``writecb()``, which is invoked when all
|
||||||
data in the bufferevent output buffer has been sent::
|
data in the bufferevent output buffer has been sent::
|
||||||
|
|
||||||
static void writecb(struct bufferevent *bev, void *ptr)
|
static void writecb(struct bufferevent *bev, void *ptr) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)ptr;
|
http2_session_data *session_data = (http2_session_data *)ptr;
|
||||||
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -407,8 +394,7 @@ a header block in HEADERS or PUSH_PROMISE frame is started::
|
||||||
|
|
||||||
static int on_begin_headers_callback(nghttp2_session *session,
|
static int on_begin_headers_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame,
|
||||||
void *user_data)
|
void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
|
|
||||||
|
@ -436,11 +422,10 @@ emitted via ``on_header_callback`` function, which is called after
|
||||||
``on_begin_headers_callback()``::
|
``on_begin_headers_callback()``::
|
||||||
|
|
||||||
static int on_header_callback(nghttp2_session *session,
|
static int on_header_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame, const uint8_t *name,
|
||||||
const uint8_t *name, size_t namelen,
|
size_t namelen, const uint8_t *value,
|
||||||
const uint8_t *value, size_t valuelen,
|
size_t valuelen, uint8_t flags _U_,
|
||||||
void *user_data)
|
void *user_data _U_) {
|
||||||
{
|
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
const char PATH[] = ":path";
|
const char PATH[] = ":path";
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
|
@ -448,14 +433,15 @@ emitted via ``on_header_callback`` function, which is called after
|
||||||
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
stream_data =
|
||||||
frame->hd.stream_id);
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
if (!stream_data || stream_data->request_path) {
|
if (!stream_data || stream_data->request_path) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
|
||||||
size_t j;
|
size_t j;
|
||||||
for(j = 0; j < valuelen && value[j] != '?'; ++j);
|
for (j = 0; j < valuelen && value[j] != '?'; ++j)
|
||||||
|
;
|
||||||
stream_data->request_path = percent_decode(value, j);
|
stream_data->request_path = percent_decode(value, j);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -472,8 +458,7 @@ The ``on_frame_recv_callback()`` function is invoked when a frame is
|
||||||
fully received::
|
fully received::
|
||||||
|
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame, void *user_data)
|
const nghttp2_frame *frame, void *user_data) {
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
|
@ -481,8 +466,8 @@ fully received::
|
||||||
case NGHTTP2_HEADERS:
|
case NGHTTP2_HEADERS:
|
||||||
/* Check that the client request has finished */
|
/* Check that the client request has finished */
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
stream_data = nghttp2_session_get_stream_user_data(session,
|
stream_data =
|
||||||
frame->hd.stream_id);
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
/* For DATA and HEADERS frame, this callback may be called after
|
/* For DATA and HEADERS frame, this callback may be called after
|
||||||
on_stream_close_callback. Check that stream still alive. */
|
on_stream_close_callback. Check that stream still alive. */
|
||||||
if (!stream_data) {
|
if (!stream_data) {
|
||||||
|
@ -508,8 +493,7 @@ header.
|
||||||
Sending the content of the file is done in ``send_response()`` function::
|
Sending the content of the file is done in ``send_response()`` function::
|
||||||
|
|
||||||
static int send_response(nghttp2_session *session, int32_t stream_id,
|
static int send_response(nghttp2_session *session, int32_t stream_id,
|
||||||
nghttp2_nv *nva, size_t nvlen, int fd)
|
nghttp2_nv *nva, size_t nvlen, int fd) {
|
||||||
{
|
|
||||||
int rv;
|
int rv;
|
||||||
nghttp2_data_provider data_prd;
|
nghttp2_data_provider data_prd;
|
||||||
data_prd.source.fd = fd;
|
data_prd.source.fd = fd;
|
||||||
|
@ -530,14 +514,15 @@ intended to be used as file descriptor. In this example server, we use
|
||||||
the file descriptor. We also set the ``file_read_callback()`` callback
|
the file descriptor. We also set the ``file_read_callback()`` callback
|
||||||
function to read the contents of the file::
|
function to read the contents of the file::
|
||||||
|
|
||||||
static ssize_t file_read_callback
|
static ssize_t file_read_callback(nghttp2_session *session _U_,
|
||||||
(nghttp2_session *session, int32_t stream_id,
|
int32_t stream_id _U_, uint8_t *buf,
|
||||||
uint8_t *buf, size_t length, uint32_t *data_flags,
|
size_t length, uint32_t *data_flags,
|
||||||
nghttp2_data_source *source, void *user_data)
|
nghttp2_data_source *source,
|
||||||
{
|
void *user_data _U_) {
|
||||||
int fd = source->fd;
|
int fd = source->fd;
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
while((r = read(fd, buf, length)) == -1 && errno == EINTR);
|
while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
|
||||||
|
;
|
||||||
if (r == -1) {
|
if (r == -1) {
|
||||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -559,11 +544,8 @@ remote peer.
|
||||||
The ``on_stream_close_callback()`` function is invoked when the stream
|
The ``on_stream_close_callback()`` function is invoked when the stream
|
||||||
is about to close::
|
is about to close::
|
||||||
|
|
||||||
static int on_stream_close_callback(nghttp2_session *session,
|
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
int32_t stream_id,
|
uint32_t error_code _U_, void *user_data) {
|
||||||
nghttp2_error_code error_code,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
http2_session_data *session_data = (http2_session_data *)user_data;
|
http2_session_data *session_data = (http2_session_data *)user_data;
|
||||||
http2_stream_data *stream_data;
|
http2_stream_data *stream_data;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue