1 Tutorial: HTTP/2 server
2 =========================
4 In this tutorial, we are going to write single-threaded, event-based
5 HTTP/2 web server, which supports HTTPS only. It can handle
6 concurrent multiple requests, but only the GET method is supported. The
7 complete source code, `libevent-server.c`_, is attached at the end of
8 this page. It also resides in examples directory in the archive or
11 This simple server takes 3 arguments, a port number to listen to, a path to
12 your SSL/TLS private key file and a path to your certificate file. Its
13 synopsis is like this::
15 $ libevent-server PORT /path/to/server.key /path/to/server.crt
17 We use libevent in this tutorial to handle networking I/O. Please
18 note that nghttp2 itself does not depend on libevent.
20 First we create a setup routine for libevent and OpenSSL in the functions
21 ``main()`` and ``run()``. One thing in there you should look at, is the setup
22 of the NPN callback. The NPN callback is used for the server to advertise
23 which application protocols the server supports to a client. In this example
24 program, when creating ``SSL_CTX`` object, we store the application protocol
25 name in the wire format of NPN in a statically allocated buffer. This is safe
26 because we only create one ``SSL_CTX`` object in the program's entire life
29 static unsigned char next_proto_list[256];
30 static size_t next_proto_list_len;
32 static int next_proto_cb(SSL *s _U_, const unsigned char **data,
33 unsigned int *len, void *arg _U_) {
34 *data = next_proto_list;
35 *len = (unsigned int)next_proto_list_len;
36 return SSL_TLSEXT_ERR_OK;
39 static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
43 ssl_ctx = SSL_CTX_new(SSLv23_server_method());
47 next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
48 memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID,
49 NGHTTP2_PROTO_VERSION_ID_LEN);
50 next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
52 SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
56 The wire format of NPN is a sequence of length prefixed string. Exactly one
57 byte is used to specify the length of each protocol identifier. In this
58 tutorial, we advertise the specific HTTP/2 protocol version the current
59 nghttp2 library supports. The nghttp2 library exports its identifier in
60 :macro:`NGHTTP2_PROTO_VERSION_ID`. The ``next_proto_cb()`` function is the
61 server-side NPN callback. In the OpenSSL implementation, we just assign the
62 pointer to the NPN buffers we filled in earlier. The NPN callback function is
63 set to the ``SSL_CTX`` object using
64 ``SSL_CTX_set_next_protos_advertised_cb()``.
66 We use the ``app_content`` structure to store application-wide data::
70 struct event_base *evbase;
73 We use the ``http2_session_data`` structure to store session-level
74 (which corresponds to one HTTP/2 connection) data::
76 typedef struct http2_session_data {
77 struct http2_stream_data root;
78 struct bufferevent *bev;
80 nghttp2_session *session;
84 We use the ``http2_stream_data`` structure to store stream-level data::
86 typedef struct http2_stream_data {
87 struct http2_stream_data *prev, *next;
93 A single HTTP/2 session can have multiple streams. We manage these
94 multiple streams with a doubly linked list. The first element of this
95 list is pointed to by the ``root->next`` in ``http2_session_data``.
96 Initially, ``root->next`` is ``NULL``. We use libevent's bufferevent
97 structure to perform network I/O. Note that the bufferevent object is
98 kept in ``http2_session_data`` and not in ``http2_stream_data``. This
99 is because ``http2_stream_data`` is just a logical stream multiplexed
100 over the single connection managed by bufferevent in
101 ``http2_session_data``.
103 We first create a listener object to accept incoming connections. We use
104 libevent's ``struct evconnlistener`` for this purpose::
106 static void start_listen(struct event_base *evbase, const char *service,
107 app_context *app_ctx) {
109 struct addrinfo hints;
110 struct addrinfo *res, *rp;
112 memset(&hints, 0, sizeof(hints));
113 hints.ai_family = AF_UNSPEC;
114 hints.ai_socktype = SOCK_STREAM;
115 hints.ai_flags = AI_PASSIVE;
117 hints.ai_flags |= AI_ADDRCONFIG;
118 #endif /* AI_ADDRCONFIG */
120 rv = getaddrinfo(NULL, service, &hints, &res);
124 for (rp = res; rp; rp = rp->ai_next) {
125 struct evconnlistener *listener;
126 listener = evconnlistener_new_bind(
127 evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
128 16, rp->ai_addr, rp->ai_addrlen);
135 errx(1, "Could not start listener");
138 We specify the ``acceptcb`` callback which is called when a new connection is
141 static void acceptcb(struct evconnlistener *listener _U_, int fd,
142 struct sockaddr *addr, int addrlen, void *arg) {
143 app_context *app_ctx = (app_context *)arg;
144 http2_session_data *session_data;
146 session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
148 bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
151 Here we create the ``http2_session_data`` object. The bufferevent for
152 this connection is also initialized at this time. We specify three
153 callbacks for the bufferevent: ``readcb``, ``writecb`` and
156 The ``eventcb()`` callback is invoked by the libevent event loop when an event
157 (e.g., connection has been established, timeout, etc) happens on the
158 underlying network socket::
160 static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
161 http2_session_data *session_data = (http2_session_data *)ptr;
162 if (events & BEV_EVENT_CONNECTED) {
163 fprintf(stderr, "%s connected\n", session_data->client_addr);
165 initialize_nghttp2_session(session_data);
167 if (send_server_connection_header(session_data) != 0) {
168 delete_http2_session_data(session_data);
174 if (events & BEV_EVENT_EOF) {
175 fprintf(stderr, "%s EOF\n", session_data->client_addr);
176 } else if (events & BEV_EVENT_ERROR) {
177 fprintf(stderr, "%s network error\n", session_data->client_addr);
178 } else if (events & BEV_EVENT_TIMEOUT) {
179 fprintf(stderr, "%s timeout\n", session_data->client_addr);
181 delete_http2_session_data(session_data);
184 For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and
185 ``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
186 The ``delete_http2_session_data()`` function destroys the
187 ``http2_session_data`` object and thus also its bufferevent member.
188 As a result, the underlying connection is closed. The
189 ``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is
190 finished successfully. Now we are ready to start the HTTP/2
193 We initialize a nghttp2 session object which is done in
194 ``initialize_nghttp2_session()``::
196 static void initialize_nghttp2_session(http2_session_data *session_data) {
197 nghttp2_session_callbacks *callbacks;
199 nghttp2_session_callbacks_new(&callbacks);
201 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
203 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
204 on_frame_recv_callback);
206 nghttp2_session_callbacks_set_on_stream_close_callback(
207 callbacks, on_stream_close_callback);
209 nghttp2_session_callbacks_set_on_header_callback(callbacks,
212 nghttp2_session_callbacks_set_on_begin_headers_callback(
213 callbacks, on_begin_headers_callback);
215 nghttp2_session_server_new(&session_data->session, callbacks, session_data);
217 nghttp2_session_callbacks_del(callbacks);
220 Since we are creating a server and uses options, the nghttp2 session
221 object is created using `nghttp2_session_server_new2()` function. We
222 registers five callbacks for nghttp2 session object. We'll talk about
223 these callbacks later.
225 After initialization of the nghttp2 session object, we are going to send
226 a server connection header in ``send_server_connection_header()``::
228 static int send_server_connection_header(http2_session_data *session_data) {
229 nghttp2_settings_entry iv[1] = {
230 {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
233 rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
236 warnx("Fatal error: %s", nghttp2_strerror(rv));
242 The server connection header is a SETTINGS frame. We specify
243 SETTINGS_MAX_CONCURRENT_STREAMS to 100 in the SETTINGS frame. To queue
244 the SETTINGS frame for the transmission, we use
245 `nghttp2_submit_settings()`. Note that `nghttp2_submit_settings()`
246 function only queues the frame and it does not actually send it. All
247 functions in the ``nghttp2_submit_*()`` family have this property. To
248 actually send the frame, `nghttp2_session_send()` should be used, as
251 Since bufferevent may buffer more than the first 24 bytes from the client, we
252 have to process them here since libevent won't invoke callback functions for
253 this pending data. To process the received data, we call the
254 ``session_recv()`` function::
256 static int session_recv(http2_session_data *session_data) {
258 struct evbuffer *input = bufferevent_get_input(session_data->bev);
259 size_t datalen = evbuffer_get_length(input);
260 unsigned char *data = evbuffer_pullup(input, -1);
262 readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
264 warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
267 if (evbuffer_drain(input, readlen) != 0) {
268 warnx("Fatal error: evbuffer_drain failed");
271 if (session_send(session_data) != 0) {
277 In this function, we feed all unprocessed but already received data to the
278 nghttp2 session object using the `nghttp2_session_mem_recv()` function. The
279 `nghttp2_session_mem_recv()` function processes the data and may invoke the
280 nghttp2 callbacks and also queue outgoing frames. Since there may be pending
281 outgoing frames, we call ``session_send()`` function to send off those
282 frames. The ``session_send()`` function is defined as follows::
284 static int session_send(http2_session_data *session_data) {
286 rv = nghttp2_session_send(session_data->session);
288 warnx("Fatal error: %s", nghttp2_strerror(rv));
294 The `nghttp2_session_send()` function serializes the frame into wire
295 format and calls ``send_callback()`` of type
296 :type:`nghttp2_send_callback`. The ``send_callback()`` is defined as
299 static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
300 size_t length, int flags _U_, void *user_data) {
301 http2_session_data *session_data = (http2_session_data *)user_data;
302 struct bufferevent *bev = session_data->bev;
303 /* Avoid excessive buffering in server side. */
304 if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
305 OUTPUT_WOULDBLOCK_THRESHOLD) {
306 return NGHTTP2_ERR_WOULDBLOCK;
308 bufferevent_write(bev, data, length);
312 Since we use bufferevent to abstract network I/O, we just write the
313 data to the bufferevent object. Note that `nghttp2_session_send()`
314 continues to write all frames queued so far. If we were writing the
315 data to a non-blocking socket directly using ``write()`` system call
316 in the ``send_callback()``, we would surely get ``EAGAIN`` or
317 ``EWOULDBLOCK`` back since the socket has limited send buffer. If that
318 happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the
319 nghttp2 library to stop sending further data. But when writing to the
320 bufferevent, we have to regulate the amount data to get buffered
321 ourselves to avoid using huge amounts of memory. To achieve this, we
322 check the size of the output buffer and if it reaches more than or
323 equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop writing data
324 and return :macro:`NGHTTP2_ERR_WOULDBLOCK` to tell the library to stop
325 calling send_callback.
327 The next bufferevent callback is ``readcb()``, which is invoked when
328 data is available to read in the bufferevent input buffer::
330 static void readcb(struct bufferevent *bev _U_, void *ptr) {
331 http2_session_data *session_data = (http2_session_data *)ptr;
332 if (session_recv(session_data) != 0) {
333 delete_http2_session_data(session_data);
338 In this function, we just call ``session_recv()`` to process incoming
341 The third bufferevent callback is ``writecb()``, which is invoked when all
342 data in the bufferevent output buffer has been sent::
344 static void writecb(struct bufferevent *bev, void *ptr) {
345 http2_session_data *session_data = (http2_session_data *)ptr;
346 if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
349 if (nghttp2_session_want_read(session_data->session) == 0 &&
350 nghttp2_session_want_write(session_data->session) == 0) {
351 delete_http2_session_data(session_data);
354 if (session_send(session_data) != 0) {
355 delete_http2_session_data(session_data);
360 First we check whether we should drop the connection or not. The nghttp2
361 session object keeps track of reception and transmission of GOAWAY frames and
362 other error conditions as well. Using this information, the nghttp2 session
363 object will tell whether the connection should be dropped or not. More
364 specifically, if both `nghttp2_session_want_read()` and
365 `nghttp2_session_want_write()` return 0, we have no business left in the
366 connection. But since we are using bufferevent and its deferred callback
367 option, the bufferevent output buffer may contain pending data when the
368 ``writecb()`` is called. To handle this, we check whether the output buffer is
369 empty or not. If all these conditions are met, we drop connection.
371 Otherwise, we call ``session_send()`` to process the pending output
372 data. Remember that in ``send_callback()``, we must not write all data to
373 bufferevent to avoid excessive buffering. We continue processing pending data
374 when the output buffer becomes empty.
376 We have already described the nghttp2 callback ``send_callback()``. Let's
377 learn about the remaining nghttp2 callbacks we setup in
378 ``initialize_nghttp2_setup()`` function.
380 The ``on_begin_headers_callback()`` function is invoked when the reception of
381 a header block in HEADERS or PUSH_PROMISE frame is started::
383 static int on_begin_headers_callback(nghttp2_session *session,
384 const nghttp2_frame *frame,
386 http2_session_data *session_data = (http2_session_data *)user_data;
387 http2_stream_data *stream_data;
389 if (frame->hd.type != NGHTTP2_HEADERS ||
390 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
393 stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
394 nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
399 We are only interested in the HEADERS frame in this function. Since the
400 HEADERS frame has several roles in the HTTP/2 protocol, we check that it is a
401 request HEADERS, which opens new stream. If the frame is a request HEADERS, we
402 create a ``http2_stream_data`` object to store the stream related data. We
403 associate the created ``http2_stream_data`` object with the stream in the
404 nghttp2 session object using `nghttp2_set_stream_user_data()` to get the
405 object without searching through the doubly linked list.
407 In this example server, we want to serve files relative to the current working
408 directory in which the program was invoked. Each header name/value pair is
409 emitted via ``on_header_callback`` function, which is called after
410 ``on_begin_headers_callback()``::
412 static int on_header_callback(nghttp2_session *session,
413 const nghttp2_frame *frame, const uint8_t *name,
414 size_t namelen, const uint8_t *value,
415 size_t valuelen, uint8_t flags _U_,
416 void *user_data _U_) {
417 http2_stream_data *stream_data;
418 const char PATH[] = ":path";
419 switch (frame->hd.type) {
420 case NGHTTP2_HEADERS:
421 if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
425 nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
426 if (!stream_data || stream_data->request_path) {
429 if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
431 for (j = 0; j < valuelen && value[j] != '?'; ++j)
433 stream_data->request_path = percent_decode(value, j);
440 We search for the ``:path`` header field among the request headers and store
441 the requested path in the ``http2_stream_data`` object. In this example
442 program, we ignore ``:method`` header field and always treat the request as a
445 The ``on_frame_recv_callback()`` function is invoked when a frame is
448 static int on_frame_recv_callback(nghttp2_session *session,
449 const nghttp2_frame *frame, void *user_data) {
450 http2_session_data *session_data = (http2_session_data *)user_data;
451 http2_stream_data *stream_data;
452 switch (frame->hd.type) {
454 case NGHTTP2_HEADERS:
455 /* Check that the client request has finished */
456 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
458 nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
459 /* For DATA and HEADERS frame, this callback may be called after
460 on_stream_close_callback. Check that stream still alive. */
464 return on_request_recv(session, session_data, stream_data);
473 First we retrieve the ``http2_stream_data`` object associated with the stream
474 in ``on_begin_headers_callback()``. It is done using
475 `nghttp2_session_get_stream_user_data()`. If the requested path cannot be
476 served for some reason (e.g., file is not found), we send a 404 response,
477 which is done in ``error_reply()``. Otherwise, we open the requested file and
478 send its content. We send the header field ``:status`` as a single response
481 Sending the content of the file is done in ``send_response()`` function::
483 static int send_response(nghttp2_session *session, int32_t stream_id,
484 nghttp2_nv *nva, size_t nvlen, int fd) {
486 nghttp2_data_provider data_prd;
487 data_prd.source.fd = fd;
488 data_prd.read_callback = file_read_callback;
490 rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
492 warnx("Fatal error: %s", nghttp2_strerror(rv));
498 The nghttp2 library uses the :type:`nghttp2_data_provider` structure to
499 send entity body to the remote peer. The ``source`` member of this
500 structure is a union and it can be either void pointer or int which is
501 intended to be used as file descriptor. In this example server, we use
502 the file descriptor. We also set the ``file_read_callback()`` callback
503 function to read the contents of the file::
505 static ssize_t file_read_callback(nghttp2_session *session _U_,
506 int32_t stream_id _U_, uint8_t *buf,
507 size_t length, uint32_t *data_flags,
508 nghttp2_data_source *source,
509 void *user_data _U_) {
512 while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
515 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
518 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
523 If an error happens while reading the file, we return
524 :macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. This tells the
525 library to send RST_STREAM to the stream. When all data has been read, set
526 the :macro:`NGHTTP2_DATA_FLAG_EOF` flag to ``*data_flags`` to tell the
527 nghttp2 library that we have finished reading the file.
529 The `nghttp2_submit_response()` function is used to send the response to the
532 The ``on_stream_close_callback()`` function is invoked when the stream
535 static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
536 uint32_t error_code _U_, void *user_data) {
537 http2_session_data *session_data = (http2_session_data *)user_data;
538 http2_stream_data *stream_data;
540 stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
544 remove_stream(session_data, stream_data);
545 delete_http2_stream_data(stream_data);
549 We destroy the ``http2_stream_data`` object in this function since the stream
550 is about to close and we no longer use that object.