doc, examples: Update tutorial and examples

This commit is contained in:
Tatsuhiro Tsujikawa 2014-01-18 00:06:24 +09:00
parent e960c56aad
commit 0b14319675
4 changed files with 184 additions and 43 deletions

View File

@ -163,11 +163,13 @@ finished successfully. We first initialize nghttp2 session object in
callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
callbacks.on_stream_close_callback = on_stream_close_callback; callbacks.on_stream_close_callback = on_stream_close_callback;
callbacks.on_header_callback = on_header_callback;
callbacks.on_end_headers_callback = on_end_headers_callback;
nghttp2_session_client_new(&session_data->session, &callbacks, session_data); nghttp2_session_client_new(&session_data->session, &callbacks, session_data);
} }
Since we are creating client, we use `nghttp2_session_client_new()` to Since we are creating client, we use `nghttp2_session_client_new()` to
initialize nghttp2 session object. We setup 5 callbacks for the initialize nghttp2 session object. We setup 7 callbacks for the
nghttp2 session. We'll explain these callbacks later. nghttp2 session. We'll explain these callbacks later.
The `delete_http2_session_data()` destroys ``session_data`` and frees The `delete_http2_session_data()` destroys ``session_data`` and frees
@ -395,9 +397,8 @@ received from the remote peer::
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE && if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) { session_data->stream_data->stream_id == frame->hd.stream_id) {
/* Print response headers for the initiated request. */ fprintf(stderr, "Response headers for stream ID=%d:\n",
fprintf(stderr, "Response headers:\n"); frame->hd.stream_id);
print_headers(stderr, frame->headers.nva, frame->headers.nvlen);
} }
break; break;
} }
@ -409,6 +410,53 @@ HEADERS. We check te frame type and its category (it should be
:macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check :macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check
its stream ID. its stream ID.
Each request header name/value pair is emitted via
``on_header_callback`` function::
static int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) {
/* Print response headers for the initiated request. */
print_header(stderr, name, namelen, value, valuelen);
break;
}
}
return 0;
}
In this turotial, we just print the name/value pair.
After all name/value pairs are emitted for a frame,
``on_end_headers_callback`` function is called::
static int on_end_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
nghttp2_error_code error_code,
void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) {
fprintf(stderr, "All headers received with error_code=%d\n", error_code);
}
break;
}
return 0;
}
This callback may be called prematurely because of errors (e.g.,
header decompression failure) which is indicated by ``error_code``.
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::

View File

@ -243,11 +243,12 @@ We initialize nghttp2 session object which is done in
callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_request_recv_callback = on_request_recv_callback; callbacks.on_request_recv_callback = on_request_recv_callback;
callbacks.on_stream_close_callback = on_stream_close_callback; callbacks.on_stream_close_callback = on_stream_close_callback;
callbacks.on_header_callback = on_header_callback;
nghttp2_session_server_new(&session_data->session, &callbacks, session_data); nghttp2_session_server_new(&session_data->session, &callbacks, session_data);
} }
Since we are creating server, nghttp2 session object is created using Since we are creating server, nghttp2 session object is created using
`nghttp2_session_server_new()` function. We registers 4 callbacks to `nghttp2_session_server_new()` function. We registers 5 callbacks to
nghttp2 session object. We'll talk about these callbacks later. nghttp2 session object. We'll talk about these callbacks later.
After initialization of nghttp2 session object, we are going to send After initialization of nghttp2 session object, we are going to send
@ -421,8 +422,6 @@ received from the remote peer::
{ {
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;
size_t i;
const char PATH[] = ":path";
switch(frame->hd.type) { switch(frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) { if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
@ -431,16 +430,6 @@ received from the remote peer::
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
stream_data); stream_data);
for(i = 0; i < frame->headers.nvlen; ++i) {
nghttp2_nv *nv = &frame->headers.nva[i];
if(nv->namelen == sizeof(PATH) - 1 &&
memcmp(PATH, nv->name, nv->namelen) == 0) {
size_t j;
for(j = 0; j < nv->valuelen && nv->value[j] != '?'; ++j);
stream_data->request_path = percent_decode(nv->value, j);
break;
}
}
break; break;
default: default:
break; break;
@ -457,13 +446,47 @@ in nghttp2 session object using `nghttp2_set_stream_user_data()` in
order to get the object without searching through doubly linked list. order to get the object without searching through doubly linked list.
In this example server, we want to serve files relative to the current In this example server, we want to serve files relative to the current
working directory the program was invoked. We search ``:path`` header working directory the program was invoked. Each header name/value pair
field in request headers and keep the requested path in is emitted via ``on_header_callback`` function, which is called after
``http2_stream_data`` object. In this example program, we ignore ``on_frame_recv_callback()``::
``:method`` header field and always treat the request as GET request.
It is ok for the server to start sending response in this callback. In static int on_header_callback(nghttp2_session *session,
this example, we defer it to ``on_request_recv_callback()`` function. const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
void *user_data)
{
http2_stream_data *stream_data;
const char PATH[] = ":path";
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break;
}
stream_data = nghttp2_session_get_stream_user_data(session,
frame->hd.stream_id);
if(stream_data->request_path) {
break;
}
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
size_t j;
for(j = 0; j < valuelen && value[j] != '?'; ++j);
stream_data->request_path = percent_decode(value, j);
}
break;
}
return 0;
}
We search ``:path`` header field in request headers and keep the
requested path in ``http2_stream_data`` object. In this example
program, we ignore ``:method`` header field and always treat the
request as GET request.
It is ok for the server to start sending response in this callback (or
in `nghttp2_on_end_headers_callback()`, which is not used in this
tutorial). In this example, we defer it to
``on_request_recv_callback()`` function.
The ``on_request_recv_callback()`` function is invoked when all HTTP The ``on_request_recv_callback()`` function is invoked when all HTTP
request, including entity body, was received:: request, including entity body, was received::

View File

@ -151,6 +151,16 @@ static void delete_http2_session_data(http2_session_data *session_data)
free(session_data); free(session_data);
} }
static void print_header(FILE *f,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen)
{
fwrite(name, namelen, 1, f);
fprintf(f, ": ");
fwrite(value, valuelen, 1, f);
fprintf(f, "\n");
}
/* Print HTTP headers to |f|. Please note that this function does not /* Print HTTP headers to |f|. Please note that this function does not
take into account that header name and value are sequence of take into account that header name and value are sequence of
octets, therefore they may contain non-printable characters. */ octets, therefore they may contain non-printable characters. */
@ -158,12 +168,11 @@ static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen)
{ {
size_t i; size_t i;
for(i = 0; i < nvlen; ++i) { for(i = 0; i < nvlen; ++i) {
fwrite(nva[i].name, nva[i].namelen, 1, stderr); print_header(f,
fprintf(stderr, ": "); nva[i].name, nva[i].namelen,
fwrite(nva[i].value, nva[i].valuelen, 1, stderr); nva[i].value, nva[i].valuelen);
fprintf(stderr, "\n");
} }
fprintf(stderr, "\n"); fprintf(f, "\n");
} }
/* nghttp2_send_callback. Here we transmit the |data|, |length| bytes, /* nghttp2_send_callback. Here we transmit the |data|, |length| bytes,
@ -201,6 +210,47 @@ static int before_frame_send_callback
return 0; return 0;
} }
/* nghttp2_on_header_callback: Called when nghttp2 library emits
single header name/value pair. */
static int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) {
/* Print response headers for the initiated request. */
print_header(stderr, name, namelen, value, valuelen);
break;
}
}
return 0;
}
/* nghttp2_on_end_headers_callback: Called when nghttp2 library emits
all header name/value pairs, or may be called prematurely because
of errors which is indicated by |error_code|. */
static int on_end_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
nghttp2_error_code error_code,
void *user_data)
{
http2_session_data *session_data = (http2_session_data*)user_data;
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) {
fprintf(stderr, "All headers received with error_code=%d\n", error_code);
}
break;
}
return 0;
}
/* nghttp2_on_frame_recv_callback: Called when nghttp2 library /* nghttp2_on_frame_recv_callback: Called when nghttp2 library
received a frame from the remote peer. */ received a frame from the remote peer. */
static int on_frame_recv_callback(nghttp2_session *session, static int on_frame_recv_callback(nghttp2_session *session,
@ -211,9 +261,8 @@ static int on_frame_recv_callback(nghttp2_session *session,
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE && if(frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
session_data->stream_data->stream_id == frame->hd.stream_id) { session_data->stream_data->stream_id == frame->hd.stream_id) {
/* Print response headers for the initiated request. */ fprintf(stderr, "Response headers for stream ID=%d:\n",
fprintf(stderr, "Response headers:\n"); frame->hd.stream_id);
print_headers(stderr, frame->headers.nva, frame->headers.nvlen);
} }
break; break;
} }
@ -311,6 +360,8 @@ static void initialize_nghttp2_session(http2_session_data *session_data)
callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
callbacks.on_stream_close_callback = on_stream_close_callback; callbacks.on_stream_close_callback = on_stream_close_callback;
callbacks.on_header_callback = on_header_callback;
callbacks.on_end_headers_callback = on_end_headers_callback;
nghttp2_session_client_new(&session_data->session, &callbacks, session_data); nghttp2_session_client_new(&session_data->session, &callbacks, session_data);
} }

View File

@ -325,13 +325,41 @@ static char* percent_decode(const uint8_t *value, size_t valuelen)
return res; return res;
} }
/* nghttp2_on_header_callback: Called when nghttp2 library emits
single header name/value pair. */
static int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
void *user_data)
{
http2_stream_data *stream_data;
const char PATH[] = ":path";
switch(frame->hd.type) {
case NGHTTP2_HEADERS:
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break;
}
stream_data = nghttp2_session_get_stream_user_data(session,
frame->hd.stream_id);
if(stream_data->request_path) {
break;
}
if(namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
size_t j;
for(j = 0; j < valuelen && value[j] != '?'; ++j);
stream_data->request_path = percent_decode(value, j);
}
break;
}
return 0;
}
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;
size_t i;
const char PATH[] = ":path";
switch(frame->hd.type) { switch(frame->hd.type) {
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) { if(frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
@ -340,16 +368,6 @@ static int on_frame_recv_callback(nghttp2_session *session,
stream_data = create_http2_stream_data(session_data, frame->hd.stream_id); stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
stream_data); stream_data);
for(i = 0; i < frame->headers.nvlen; ++i) {
nghttp2_nv *nv = &frame->headers.nva[i];
if(nv->namelen == sizeof(PATH) - 1 &&
memcmp(PATH, nv->name, nv->namelen) == 0) {
size_t j;
for(j = 0; j < nv->valuelen && nv->value[j] != '?'; ++j);
stream_data->request_path = percent_decode(nv->value, j);
break;
}
}
break; break;
default: default:
break; break;
@ -503,6 +521,7 @@ static void initialize_nghttp2_session(http2_session_data *session_data)
callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback;
callbacks.on_request_recv_callback = on_request_recv_callback; callbacks.on_request_recv_callback = on_request_recv_callback;
callbacks.on_stream_close_callback = on_stream_close_callback; callbacks.on_stream_close_callback = on_stream_close_callback;
callbacks.on_header_callback = on_header_callback;
nghttp2_session_server_new(&session_data->session, &callbacks, session_data); nghttp2_session_server_new(&session_data->session, &callbacks, session_data);
} }