doc, examples: Update tutorial and examples
This commit is contained in:
parent
e960c56aad
commit
0b14319675
|
@ -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::
|
||||||
|
|
||||||
|
|
|
@ -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::
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue