Merge branch 'master' of https://github.com/skip2/nghttp2 into skip2-master
This commit is contained in:
commit
091055edec
|
@ -1,30 +1,31 @@
|
||||||
Tutorial: HTTP/2 server
|
Tutorial: HTTP/2 server
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
In this tutorial, we are going to write single-threaded, event-based
|
In this tutorial, we are going to write a single-threaded, event-based
|
||||||
HTTP/2 web server, which supports HTTPS only. It can handle
|
HTTP/2 web server, which supports HTTPS only. It can handle concurrent
|
||||||
concurrent multiple requests, but only the GET method is supported. The
|
multiple requests, but only the GET method is supported. The complete
|
||||||
complete source code, `libevent-server.c`_, is attached at the end of
|
source code, `libevent-server.c`_, is attached at the end of this
|
||||||
this page. It also resides in examples directory in the archive or
|
page. The source also resides in the examples directory in the
|
||||||
repository.
|
archive or repository.
|
||||||
|
|
||||||
This simple server takes 3 arguments, a port number to listen to, a path to
|
This simple server takes 3 arguments: The port number to listen on,
|
||||||
your SSL/TLS private key file and a path to your certificate file. Its
|
the path to your SSL/TLS private key file, and the path to your
|
||||||
synopsis is like this::
|
certificate file. The synopsis is::
|
||||||
|
|
||||||
$ libevent-server PORT /path/to/server.key /path/to/server.crt
|
$ libevent-server PORT /path/to/server.key /path/to/server.crt
|
||||||
|
|
||||||
We use libevent in this tutorial to handle networking I/O. Please
|
We use libevent in this tutorial to handle networking I/O. Please
|
||||||
note that nghttp2 itself does not depend on libevent.
|
note that nghttp2 itself does not depend on libevent.
|
||||||
|
|
||||||
First we create a setup routine for libevent and OpenSSL in the functions
|
The server starts with some libevent and OpenSSL setup in the
|
||||||
``main()`` and ``run()``. One thing in there you should look at, is the setup
|
``main()`` and ``run()`` functions. This setup isn't specific to
|
||||||
of the NPN callback. The NPN callback is used for the server to advertise
|
nghttp2, but one thing you should look at is setup of the NPN
|
||||||
which application protocols the server supports to a client. In this example
|
callback. The NPN callback is used by the server to advertise which
|
||||||
program, when creating ``SSL_CTX`` object, we store the application protocol
|
application protocols the server supports to a client. In this
|
||||||
name in the wire format of NPN in a statically allocated buffer. This is safe
|
example program, when creating the ``SSL_CTX`` object, we store the
|
||||||
because we only create one ``SSL_CTX`` object in the program's entire life
|
application protocol name in the wire format of NPN in a statically
|
||||||
time::
|
allocated buffer. This is safe because we only create one ``SSL_CTX``
|
||||||
|
object in the program's entire lifetime::
|
||||||
|
|
||||||
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;
|
||||||
|
@ -53,17 +54,20 @@ time::
|
||||||
return ssl_ctx;
|
return ssl_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
The wire format of NPN is a sequence of length prefixed string. Exactly one
|
The wire format of NPN is a sequence of length prefixed strings, with
|
||||||
byte is used to specify the length of each protocol identifier. In this
|
exactly one byte used to specify the length of each protocol
|
||||||
tutorial, we advertise the specific HTTP/2 protocol version the current
|
identifier. In this tutorial, we advertise the specific HTTP/2
|
||||||
nghttp2 library supports. The nghttp2 library exports its identifier in
|
protocol version the current nghttp2 library supports, which is
|
||||||
:macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function is the
|
exported in the identifier :macro:`NGHTTP2_PROTO_VERSION_ID`. The
|
||||||
server-side NPN callback. In the OpenSSL implementation, we just assign the
|
``next_proto_cb()`` function is the server-side NPN callback. In the
|
||||||
pointer to the NPN buffers we filled in earlier. The NPN callback function is
|
OpenSSL implementation, we just assign the pointer to the NPN buffers
|
||||||
set to the ``SSL_CTX`` object using
|
we filled in earlier. The NPN callback function is set to the
|
||||||
``SSL_CTX_set_next_protos_advertised_cb()``.
|
``SSL_CTX`` object using ``SSL_CTX_set_next_protos_advertised_cb()``.
|
||||||
|
|
||||||
We use the ``app_content`` structure to store application-wide data::
|
Next, let's take a look at the main structures used by the example
|
||||||
|
application:
|
||||||
|
|
||||||
|
We use the ``app_context`` structure to store application-wide data::
|
||||||
|
|
||||||
struct app_context {
|
struct app_context {
|
||||||
SSL_CTX *ssl_ctx;
|
SSL_CTX *ssl_ctx;
|
||||||
|
@ -90,18 +94,21 @@ We use the ``http2_stream_data`` structure to store stream-level data::
|
||||||
int fd;
|
int fd;
|
||||||
} http2_stream_data;
|
} http2_stream_data;
|
||||||
|
|
||||||
A single HTTP/2 session can have multiple streams. We manage these
|
A single HTTP/2 session can have multiple streams. To manage them, we
|
||||||
multiple streams with a doubly linked list. The first element of this
|
use a doubly linked list: The first element of this list is pointed
|
||||||
list is pointed to by the ``root->next`` in ``http2_session_data``.
|
to by the ``root->next`` in ``http2_session_data``. Initially,
|
||||||
Initially, ``root->next`` is ``NULL``. We use libevent's bufferevent
|
``root->next`` is ``NULL``.
|
||||||
structure to perform network I/O. Note that the bufferevent object is
|
|
||||||
kept in ``http2_session_data`` and not in ``http2_stream_data``. This
|
libevent's bufferevent structure is used to perform network I/O, with
|
||||||
is because ``http2_stream_data`` is just a logical stream multiplexed
|
the pointer to the bufferevent stored in the ``http2_session_data``
|
||||||
over the single connection managed by bufferevent in
|
structure. Note that the bufferevent object is kept in
|
||||||
|
``http2_session_data`` and not in ``http2_stream_data``. This is
|
||||||
|
because ``http2_stream_data`` is just a logical stream multiplexed
|
||||||
|
over the single connection managed by the bufferevent in
|
||||||
``http2_session_data``.
|
``http2_session_data``.
|
||||||
|
|
||||||
We first create a listener object to accept incoming connections. We use
|
We first create a listener object to accept incoming connections.
|
||||||
libevent's ``struct evconnlistener`` for this purpose::
|
libevent's ``struct evconnlistener`` is used 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) {
|
||||||
|
@ -135,7 +142,7 @@ libevent's ``struct evconnlistener`` for this purpose::
|
||||||
errx(1, "Could not start listener");
|
errx(1, "Could not start listener");
|
||||||
}
|
}
|
||||||
|
|
||||||
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 _U_, int fd,
|
static void acceptcb(struct evconnlistener *listener _U_, int fd,
|
||||||
|
@ -148,13 +155,13 @@ accepted::
|
||||||
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
|
bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Here we create the ``http2_session_data`` object. The bufferevent for
|
Here we create the ``http2_session_data`` object. The connection's
|
||||||
this connection is also initialized at this time. We specify three
|
bufferevent is initialized at the same time. We specify three
|
||||||
callbacks for the bufferevent: ``readcb``, ``writecb`` and
|
callbacks for the bufferevent: ``readcb``, ``writecb``, and
|
||||||
``eventcb``.
|
``eventcb``.
|
||||||
|
|
||||||
The ``eventcb()`` callback is invoked by the libevent event loop when an event
|
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.) occurs on the
|
||||||
underlying network socket::
|
underlying network socket::
|
||||||
|
|
||||||
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
|
||||||
|
@ -181,17 +188,19 @@ underlying network socket::
|
||||||
delete_http2_session_data(session_data);
|
delete_http2_session_data(session_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and
|
For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and
|
||||||
``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
|
``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
|
||||||
The ``delete_http2_session_data()`` function destroys the
|
The ``delete_http2_session_data()`` function destroys the
|
||||||
``http2_session_data`` object and thus also its bufferevent member.
|
``http2_session_data`` object and its associated bufferevent member.
|
||||||
As a result, the underlying connection is closed. The
|
As a result, the underlying connection is closed.
|
||||||
``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is
|
|
||||||
finished successfully. Now we are ready to start the HTTP/2
|
|
||||||
communication.
|
|
||||||
|
|
||||||
We initialize a nghttp2 session object which is done in
|
The
|
||||||
``initialize_nghttp2_session()``::
|
``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake has
|
||||||
|
completed successfully. After this we are ready to begin communicating
|
||||||
|
via HTTP/2.
|
||||||
|
|
||||||
|
The ``initialize_nghttp2_session()`` function initializes the nghttp2
|
||||||
|
session object and several callbacks::
|
||||||
|
|
||||||
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;
|
||||||
|
@ -217,13 +226,13 @@ We initialize a nghttp2 session object which is done in
|
||||||
nghttp2_session_callbacks_del(callbacks);
|
nghttp2_session_callbacks_del(callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
Since we are creating a server and uses options, the nghttp2 session
|
Since we are creating a server, we use `nghttp2_session_server_new()`
|
||||||
object is created using `nghttp2_session_server_new2()` function. We
|
to initialize the nghttp2 session object. We also setup 5 callbacks
|
||||||
registers five callbacks for nghttp2 session object. We'll talk about
|
for the nghttp2 session, these are explained later.
|
||||||
these callbacks later.
|
|
||||||
|
|
||||||
After initialization of the nghttp2 session object, we are going to send
|
The server now begins by sending the server connection preface, which
|
||||||
a server connection header in ``send_server_connection_header()``::
|
always consists of a SETTINGS frame.
|
||||||
|
``send_server_connection_header()`` configures and submits it::
|
||||||
|
|
||||||
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] = {
|
||||||
|
@ -239,11 +248,10 @@ a server connection header in ``send_server_connection_header()``::
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
The server connection header is a SETTINGS frame. We specify
|
In the example SETTINGS frame we've set
|
||||||
SETTINGS_MAX_CONCURRENT_STREAMS to 100 in the SETTINGS frame. To queue
|
SETTINGS_MAX_CONCURRENT_STREAMS to 100. `nghttp2_submit_settings()`
|
||||||
the SETTINGS frame for the transmission, we use
|
is used to queue the frame for transmission, but note it only queues
|
||||||
`nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()`
|
the frame for transmission, and doesn't actually send it. All
|
||||||
function only queues the frame and it does not actually send it. All
|
|
||||||
functions in the ``nghttp2_submit_*()`` family have this property. To
|
functions in the ``nghttp2_submit_*()`` family have this property. To
|
||||||
actually send the frame, `nghttp2_session_send()` should be used, as
|
actually send the frame, `nghttp2_session_send()` should be used, as
|
||||||
described later.
|
described later.
|
||||||
|
@ -274,12 +282,14 @@ this pending data. To process the received data, we call the
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
In this function, we feed all unprocessed but already received data to the
|
In this function, we feed all unprocessed but already received data to
|
||||||
nghttp2 session object using the `nghttp2_session_mem_recv()` function. The
|
the nghttp2 session object using the `nghttp2_session_mem_recv()`
|
||||||
`nghttp2_session_mem_recv()` function processes the data and may invoke the
|
function. The `nghttp2_session_mem_recv()` function processes the data
|
||||||
nghttp2 callbacks and also queue outgoing frames. Since there may be pending
|
and may both invoke the previously setup callbacks and also queue
|
||||||
outgoing frames, we call ``session_send()`` function to send off those
|
outgoing frames. To send any pending outgoing frames, we immediately
|
||||||
frames. The ``session_send()`` function is defined as follows::
|
call ``session_send()``.
|
||||||
|
|
||||||
|
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;
|
||||||
|
@ -292,7 +302,7 @@ frames. The ``session_send()`` function is defined as follows::
|
||||||
}
|
}
|
||||||
|
|
||||||
The `nghttp2_session_send()` function serializes the frame into wire
|
The `nghttp2_session_send()` function serializes the frame into wire
|
||||||
format and calls ``send_callback()`` of type
|
format and calls the ``send_callback()``, which is of type
|
||||||
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
:type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
|
||||||
follows::
|
follows::
|
||||||
|
|
||||||
|
@ -312,17 +322,16 @@ follows::
|
||||||
Since we use bufferevent to abstract network I/O, we just write the
|
Since we use bufferevent to abstract network I/O, we just write the
|
||||||
data to the bufferevent object. Note that `nghttp2_session_send()`
|
data to the bufferevent object. Note that `nghttp2_session_send()`
|
||||||
continues to write all frames queued so far. If we were writing the
|
continues to write all frames queued so far. If we were writing the
|
||||||
data to a non-blocking socket directly using ``write()`` system call
|
data to a non-blocking socket directly using the ``write()`` system
|
||||||
in the ``send_callback()``, we would surely get ``EAGAIN`` or
|
call in the ``send_callback()``, we'd soon receive an ``EAGAIN`` or
|
||||||
``EWOULDBLOCK`` back since the socket has limited send buffer. If that
|
``EWOULDBLOCK`` error since sockets have a limited send buffer. If
|
||||||
happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the
|
that happens, it's possible to return :macro:`NGHTTP2_ERR_WOULDBLOCK`
|
||||||
nghttp2 library to stop sending further data. But when writing to the
|
to signal the nghttp2 library to stop sending further data. But here,
|
||||||
bufferevent, we have to regulate the amount data to get buffered
|
when writing to the bufferevent, we have to regulate the amount data
|
||||||
ourselves to avoid using huge amounts of memory. To achieve this, we
|
to buffered ourselves to avoid using huge amounts of memory. To
|
||||||
check the size of the output buffer and if it reaches more than or
|
achieve this, we check the size of the output buffer and if it reaches
|
||||||
equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data
|
more than or equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop
|
||||||
and return :macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop
|
writing data and return :macro:`NGHTTP2_ERR_WOULDBLOCK`.
|
||||||
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::
|
||||||
|
@ -357,16 +366,18 @@ data in the bufferevent output buffer has been sent::
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
First we check whether we should drop the connection or not. The nghttp2
|
First we check whether we should drop the connection or not. The
|
||||||
session object keeps track of reception and transmission of GOAWAY frames and
|
nghttp2 session object keeps track of reception and transmission of
|
||||||
other error conditions as well. Using this information, the nghttp2 session
|
GOAWAY frames and other error conditions as well. Using this
|
||||||
object will tell whether the connection should be dropped or not. More
|
information, the nghttp2 session object can state whether the
|
||||||
specifically, if both `nghttp2_session_want_read()` and
|
connection should be dropped or not. More specifically, if both
|
||||||
`nghttp2_session_want_write()` return 0, we have no business left in the
|
`nghttp2_session_want_read()` and `nghttp2_session_want_write()`
|
||||||
connection. But since we are using bufferevent and its deferred callback
|
return 0, the connection is no-longer required and can be closed.
|
||||||
option, the bufferevent output buffer may contain pending data when the
|
Since we are using bufferevent and its deferred callback option, the
|
||||||
``writecb()`` is called. To handle this, we check whether the output buffer is
|
bufferevent output buffer may still contain pending data when the
|
||||||
empty or not. If all these conditions are met, we drop connection.
|
``writecb()`` is called. To handle this, we check whether the output
|
||||||
|
buffer is empty or not. If all of these conditions are met, we drop
|
||||||
|
connection.
|
||||||
|
|
||||||
Otherwise, we call ``session_send()`` to process the pending output
|
Otherwise, we call ``session_send()`` to process the pending output
|
||||||
data. Remember that in ``send_callback()``, we must not write all data to
|
data. Remember that in ``send_callback()``, we must not write all data to
|
||||||
|
@ -374,7 +385,7 @@ bufferevent to avoid excessive buffering. We continue processing pending data
|
||||||
when the output buffer becomes empty.
|
when the output buffer becomes empty.
|
||||||
|
|
||||||
We have already described the nghttp2 callback ``send_callback()``. Let's
|
We have already described the nghttp2 callback ``send_callback()``. Let's
|
||||||
learn about the remaining nghttp2 callbacks we setup in
|
learn about the remaining nghttp2 callbacks setup in
|
||||||
``initialize_nghttp2_setup()`` function.
|
``initialize_nghttp2_setup()`` function.
|
||||||
|
|
||||||
The ``on_begin_headers_callback()`` function is invoked when the reception of
|
The ``on_begin_headers_callback()`` function is invoked when the reception of
|
||||||
|
@ -396,13 +407,15 @@ a header block in HEADERS or PUSH_PROMISE frame is started::
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
We are only interested in the HEADERS frame in this function. Since the
|
We are only interested in the HEADERS frame in this function. Since
|
||||||
HEADERS frame has several roles in the HTTP/2 protocol, we check that it is a
|
the HEADERS frame has several roles in the HTTP/2 protocol, we check
|
||||||
request HEADERS, which opens new stream. If the frame is a request HEADERS, we
|
that it is a request HEADERS, which opens new stream. If the frame is
|
||||||
create a ``http2_stream_data`` object to store the stream related data. We
|
a request HEADERS, we create a ``http2_stream_data`` object to store
|
||||||
associate the created ``http2_stream_data`` object with the stream in the
|
the stream related data. We associate the created
|
||||||
nghttp2 session object using `nghttp2_set_stream_user_data()` to get the
|
``http2_stream_data`` object with the stream in the nghttp2 session
|
||||||
object without searching through the doubly linked list.
|
object using `nghttp2_set_stream_user_data()`. The
|
||||||
|
``http2_stream_data`` object can later be easily retrieved from the
|
||||||
|
stream, without searching through the doubly linked list.
|
||||||
|
|
||||||
In this example server, we want to serve files relative to the current working
|
In this example server, we want to serve files relative to the current working
|
||||||
directory in which the program was invoked. Each header name/value pair is
|
directory in which the program was invoked. Each header name/value pair is
|
||||||
|
@ -437,10 +450,10 @@ emitted via ``on_header_callback`` function, which is called after
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
We search for the ``:path`` header field among the request headers and store
|
We search for the ``:path`` header field among the request headers and
|
||||||
the requested path in the ``http2_stream_data`` object. In this example
|
store the requested path in the ``http2_stream_data`` object. In this
|
||||||
program, we ignore ``:method`` header field and always treat the request as a
|
example program, we ignore the ``:method`` header field and always
|
||||||
GET request.
|
treat the request as a GET request.
|
||||||
|
|
||||||
The ``on_frame_recv_callback()`` function is invoked when a frame is
|
The ``on_frame_recv_callback()`` function is invoked when a frame is
|
||||||
fully received::
|
fully received::
|
||||||
|
@ -470,15 +483,15 @@ fully received::
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
First we retrieve the ``http2_stream_data`` object associated with the stream
|
First we retrieve the ``http2_stream_data`` object associated with the
|
||||||
in ``on_begin_headers_callback()``. It is done using
|
stream in ``on_begin_headers_callback()`` using
|
||||||
`nghttp2_session_get_stream_user_data()`. If the requested path cannot be
|
`nghttp2_session_get_stream_user_data()`. If the requested path
|
||||||
served for some reason (e.g., file is not found), we send a 404 response,
|
cannot be served for some reason (e.g. file is not found), we send a
|
||||||
which is done in ``error_reply()``. Otherwise, we open the requested file and
|
404 response using ``error_reply()``. Otherwise, we open
|
||||||
send its content. We send the header field ``:status`` as a single response
|
the requested file and send its content. We send the header field
|
||||||
header.
|
``:status`` as a single response header.
|
||||||
|
|
||||||
Sending the content of the file is done in ``send_response()`` function::
|
Sending the file content is performed by the ``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) {
|
||||||
|
@ -495,12 +508,13 @@ Sending the content of the file is done in ``send_response()`` function::
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
The nghttp2 library uses the :type:`nghttp2_data_provider` structure to
|
nghttp2 uses the :type:`nghttp2_data_provider` structure to send the
|
||||||
send entity body to the remote peer. The ``source`` member of this
|
entity body to the remote peer. The ``source`` member of this
|
||||||
structure is a union and it can be either void pointer or int which is
|
structure is a union, which can be either a void pointer or an int
|
||||||
intended to be used as file descriptor. In this example server, we use
|
(which is intended to be used as file descriptor). In this example
|
||||||
the file descriptor. We also set the ``file_read_callback()`` callback
|
server, we use it as a file descriptor. We also set the
|
||||||
function to read the contents of the file::
|
``file_read_callback()`` callback function to read the contents of the
|
||||||
|
file::
|
||||||
|
|
||||||
static ssize_t file_read_callback(nghttp2_session *session _U_,
|
static ssize_t file_read_callback(nghttp2_session *session _U_,
|
||||||
int32_t stream_id _U_, uint8_t *buf,
|
int32_t stream_id _U_, uint8_t *buf,
|
||||||
|
@ -520,11 +534,11 @@ function to read the contents of the file::
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
If an error happens while reading the file, we return
|
If an error occurs while reading the file, we return
|
||||||
:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the
|
:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the
|
||||||
library to send RST_STREAM to the stream. When all data has been read, set
|
library to send RST_STREAM to the stream. When all data has been
|
||||||
the :macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the
|
read, the :macro:`NGHTTP2_DATA_FLAG_EOF` flag is set to signal nghttp2
|
||||||
nghttp2 library that we have finished reading the file.
|
that we have finished reading the file.
|
||||||
|
|
||||||
The `nghttp2_submit_response()` function is used to send the response to the
|
The `nghttp2_submit_response()` function is used to send the response to the
|
||||||
remote peer.
|
remote peer.
|
||||||
|
@ -546,5 +560,5 @@ is about to close::
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
We destroy the ``http2_stream_data`` object in this function since the stream
|
Lastly, we destroy the ``http2_stream_data`` object in this function,
|
||||||
is about to close and we no longer use that object.
|
since the stream is about to close and we no longer need the object.
|
||||||
|
|
Loading…
Reference in New Issue