• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Tutorial: HTTP/2 server
2=========================
3
4In this tutorial, we are going to write a single-threaded, event-based
5HTTP/2 web server, which supports HTTPS only. It can handle concurrent
6multiple requests, but only the GET method is supported. The complete
7source code, `libevent-server.c`_, is attached at the end of this
8page.  The source also resides in the examples directory in the
9archive or repository.
10
11This simple server takes 3 arguments: The port number to listen on,
12the path to your SSL/TLS private key file, and the path to your
13certificate file.  The synopsis is:
14
15.. code-block:: text
16
17    $ libevent-server PORT /path/to/server.key /path/to/server.crt
18
19We use libevent in this tutorial to handle networking I/O.  Please
20note that nghttp2 itself does not depend on libevent.
21
22The server starts with some libevent and OpenSSL setup in the
23``main()`` and ``run()`` functions. This setup isn't specific to
24nghttp2, but one thing you should look at is setup of the NPN
25callback. The NPN callback is used by the server to advertise which
26application protocols the server supports to a client.  In this
27example program, when creating the ``SSL_CTX`` object, we store the
28application protocol name in the wire format of NPN in a statically
29allocated buffer. This is safe because we only create one ``SSL_CTX``
30object in the program's entire lifetime.
31
32If you are following TLS related RFC, you know that NPN is not the
33standardized way to negotiate HTTP/2.  NPN itself is not even
34published as RFC.  The standard way to negotiate HTTP/2 is ALPN,
35Application-Layer Protocol Negotiation Extension, defined in `RFC 7301
36<https://tools.ietf.org/html/rfc7301>`_.  The one caveat of ALPN is
37that OpenSSL >= 1.0.2 is required.  We use macro to enable/disable
38ALPN support depending on OpenSSL version.  In ALPN, client sends the
39list of supported application protocols, and server selects one of
40them.  We provide the callback for it::
41
42    static unsigned char next_proto_list[256];
43    static size_t next_proto_list_len;
44
45    static int next_proto_cb(SSL *s _U_, const unsigned char **data,
46                             unsigned int *len, void *arg _U_) {
47      *data = next_proto_list;
48      *len = (unsigned int)next_proto_list_len;
49      return SSL_TLSEXT_ERR_OK;
50    }
51
52    #if OPENSSL_VERSION_NUMBER >= 0x10002000L
53    static int alpn_select_proto_cb(SSL *ssl _U_, const unsigned char **out,
54                                    unsigned char *outlen, const unsigned char *in,
55                                    unsigned int inlen, void *arg _U_) {
56      int rv;
57
58      rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen);
59
60      if (rv != 1) {
61        return SSL_TLSEXT_ERR_NOACK;
62      }
63
64      return SSL_TLSEXT_ERR_OK;
65    }
66    #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
67
68    static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
69      SSL_CTX *ssl_ctx;
70      EC_KEY *ecdh;
71
72      ssl_ctx = SSL_CTX_new(SSLv23_server_method());
73
74      ...
75
76      next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
77      memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID,
78             NGHTTP2_PROTO_VERSION_ID_LEN);
79      next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
80
81      SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
82
83    #if OPENSSL_VERSION_NUMBER >= 0x10002000L
84      SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL);
85    #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
86
87      return ssl_ctx;
88    }
89
90The wire format of NPN is a sequence of length prefixed strings, with
91exactly one byte used to specify the length of each protocol
92identifier.  In this tutorial, we advertise the specific HTTP/2
93protocol version the current nghttp2 library supports, which is
94exported in the identifier :macro:`NGHTTP2_PROTO_VERSION_ID`. The
95``next_proto_cb()`` function is the server-side NPN callback. In the
96OpenSSL implementation, we just assign the pointer to the NPN buffers
97we filled in earlier. The NPN callback function is set to the
98``SSL_CTX`` object using ``SSL_CTX_set_next_protos_advertised_cb()``.
99
100In ``alpn_select_proto_cb()``, we use `nghttp2_select_next_protocol()`
101to select application protocol.  The `nghttp2_select_next_protocol()`
102returns 1 only if it selected h2 (ALPN identifier for HTTP/2), and out
103parameters were assigned accordingly.
104
105Next, let's take a look at the main structures used by the example
106application:
107
108We use the ``app_context`` structure to store application-wide data::
109
110    struct app_context {
111      SSL_CTX *ssl_ctx;
112      struct event_base *evbase;
113    };
114
115We use the ``http2_session_data`` structure to store session-level
116(which corresponds to one HTTP/2 connection) data::
117
118    typedef struct http2_session_data {
119      struct http2_stream_data root;
120      struct bufferevent *bev;
121      app_context *app_ctx;
122      nghttp2_session *session;
123      char *client_addr;
124    } http2_session_data;
125
126We use the ``http2_stream_data`` structure to store stream-level data::
127
128    typedef struct http2_stream_data {
129      struct http2_stream_data *prev, *next;
130      char *request_path;
131      int32_t stream_id;
132      int fd;
133    } http2_stream_data;
134
135A single HTTP/2 session can have multiple streams.  To manage them, we
136use a doubly linked list:  The first element of this list is pointed
137to by the ``root->next`` in ``http2_session_data``.  Initially,
138``root->next`` is ``NULL``.
139
140libevent's bufferevent structure is used to perform network I/O, with
141the pointer to the bufferevent stored in the ``http2_session_data``
142structure.  Note that the bufferevent object is kept in
143``http2_session_data`` and not in ``http2_stream_data``. This is
144because ``http2_stream_data`` is just a logical stream multiplexed
145over the single connection managed by the bufferevent in
146``http2_session_data``.
147
148We first create a listener object to accept incoming connections.
149libevent's ``struct evconnlistener`` is used for this purpose::
150
151    static void start_listen(struct event_base *evbase, const char *service,
152                             app_context *app_ctx) {
153      int rv;
154      struct addrinfo hints;
155      struct addrinfo *res, *rp;
156
157      memset(&hints, 0, sizeof(hints));
158      hints.ai_family = AF_UNSPEC;
159      hints.ai_socktype = SOCK_STREAM;
160      hints.ai_flags = AI_PASSIVE;
161    #ifdef AI_ADDRCONFIG
162      hints.ai_flags |= AI_ADDRCONFIG;
163    #endif /* AI_ADDRCONFIG */
164
165      rv = getaddrinfo(NULL, service, &hints, &res);
166      if (rv != 0) {
167        errx(1, NULL);
168      }
169      for (rp = res; rp; rp = rp->ai_next) {
170        struct evconnlistener *listener;
171        listener = evconnlistener_new_bind(
172            evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
173            16, rp->ai_addr, (int)rp->ai_addrlen);
174        if (listener) {
175          freeaddrinfo(res);
176
177          return;
178        }
179      }
180      errx(1, "Could not start listener");
181    }
182
183We specify the ``acceptcb`` callback, which is called when a new connection is
184accepted::
185
186    static void acceptcb(struct evconnlistener *listener _U_, int fd,
187                         struct sockaddr *addr, int addrlen, void *arg) {
188      app_context *app_ctx = (app_context *)arg;
189      http2_session_data *session_data;
190
191      session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
192
193      bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
194    }
195
196Here we create the ``http2_session_data`` object. The connection's
197bufferevent is initialized at the same time. We specify three
198callbacks for the bufferevent: ``readcb``, ``writecb``, and
199``eventcb``.
200
201The ``eventcb()`` callback is invoked by the libevent event loop when an event
202(e.g. connection has been established, timeout, etc.) occurs on the
203underlying network socket::
204
205    static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
206      http2_session_data *session_data = (http2_session_data *)ptr;
207      if (events & BEV_EVENT_CONNECTED) {
208        const unsigned char *alpn = NULL;
209        unsigned int alpnlen = 0;
210        SSL *ssl;
211
212        fprintf(stderr, "%s connected\n", session_data->client_addr);
213
214        ssl = bufferevent_openssl_get_ssl(session_data->bev);
215
216        SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
217    #if OPENSSL_VERSION_NUMBER >= 0x10002000L
218        if (alpn == NULL) {
219          SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
220        }
221    #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
222
223        if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
224          fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr);
225          delete_http2_session_data(session_data);
226          return;
227        }
228
229        initialize_nghttp2_session(session_data);
230
231        if (send_server_connection_header(session_data) != 0 ||
232            session_send(session_data) != 0) {
233          delete_http2_session_data(session_data);
234          return;
235        }
236
237        return;
238      }
239      if (events & BEV_EVENT_EOF) {
240        fprintf(stderr, "%s EOF\n", session_data->client_addr);
241      } else if (events & BEV_EVENT_ERROR) {
242        fprintf(stderr, "%s network error\n", session_data->client_addr);
243      } else if (events & BEV_EVENT_TIMEOUT) {
244        fprintf(stderr, "%s timeout\n", session_data->client_addr);
245      }
246      delete_http2_session_data(session_data);
247    }
248
249Here we validate that HTTP/2 is negotiated, and if not, drop
250connection.
251
252For the ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR``, and
253``BEV_EVENT_TIMEOUT`` events, we just simply tear down the connection.
254The ``delete_http2_session_data()`` function destroys the
255``http2_session_data`` object and its associated bufferevent member.
256As a result, the underlying connection is closed.
257
258The
259``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake has
260completed successfully. After this we are ready to begin communicating
261via HTTP/2.
262
263The ``initialize_nghttp2_session()`` function initializes the nghttp2
264session object and several callbacks::
265
266    static void initialize_nghttp2_session(http2_session_data *session_data) {
267      nghttp2_session_callbacks *callbacks;
268
269      nghttp2_session_callbacks_new(&callbacks);
270
271      nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
272
273      nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
274                                                           on_frame_recv_callback);
275
276      nghttp2_session_callbacks_set_on_stream_close_callback(
277          callbacks, on_stream_close_callback);
278
279      nghttp2_session_callbacks_set_on_header_callback(callbacks,
280                                                       on_header_callback);
281
282      nghttp2_session_callbacks_set_on_begin_headers_callback(
283          callbacks, on_begin_headers_callback);
284
285      nghttp2_session_server_new(&session_data->session, callbacks, session_data);
286
287      nghttp2_session_callbacks_del(callbacks);
288    }
289
290Since we are creating a server, we use `nghttp2_session_server_new()`
291to initialize the nghttp2 session object.  We also setup 5 callbacks
292for the nghttp2 session, these are explained later.
293
294The server now begins by sending the server connection preface, which
295always consists of a SETTINGS frame.
296``send_server_connection_header()`` configures and submits it::
297
298    static int send_server_connection_header(http2_session_data *session_data) {
299      nghttp2_settings_entry iv[1] = {
300          {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
301      int rv;
302
303      rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
304                                   ARRLEN(iv));
305      if (rv != 0) {
306        warnx("Fatal error: %s", nghttp2_strerror(rv));
307        return -1;
308      }
309      return 0;
310    }
311
312In the example SETTINGS frame we've set
313SETTINGS_MAX_CONCURRENT_STREAMS to 100. `nghttp2_submit_settings()`
314is used to queue the frame for transmission, but note it only queues
315the frame for transmission, and doesn't actually send it. All
316functions in the ``nghttp2_submit_*()`` family have this property. To
317actually send the frame, `nghttp2_session_send()` should be used, as
318described later.
319
320Since bufferevent may buffer more than the first 24 bytes from the client, we
321have to process them here since libevent won't invoke callback functions for
322this pending data. To process the received data, we call the
323``session_recv()`` function::
324
325    static int session_recv(http2_session_data *session_data) {
326      ssize_t readlen;
327      struct evbuffer *input = bufferevent_get_input(session_data->bev);
328      size_t datalen = evbuffer_get_length(input);
329      unsigned char *data = evbuffer_pullup(input, -1);
330
331      readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
332      if (readlen < 0) {
333        warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
334        return -1;
335      }
336      if (evbuffer_drain(input, (size_t)readlen) != 0) {
337        warnx("Fatal error: evbuffer_drain failed");
338        return -1;
339      }
340      if (session_send(session_data) != 0) {
341        return -1;
342      }
343      return 0;
344    }
345
346In this function, we feed all unprocessed but already received data to
347the nghttp2 session object using the `nghttp2_session_mem_recv()`
348function. The `nghttp2_session_mem_recv()` function processes the data
349and may both invoke the previously setup callbacks and also queue
350outgoing frames. To send any pending outgoing frames, we immediately
351call ``session_send()``.
352
353The ``session_send()`` function is defined as follows::
354
355    static int session_send(http2_session_data *session_data) {
356      int rv;
357      rv = nghttp2_session_send(session_data->session);
358      if (rv != 0) {
359        warnx("Fatal error: %s", nghttp2_strerror(rv));
360        return -1;
361      }
362      return 0;
363    }
364
365The `nghttp2_session_send()` function serializes the frame into wire
366format and calls the ``send_callback()``, which is of type
367:type:`nghttp2_send_callback`.  The ``send_callback()`` is defined as
368follows::
369
370    static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
371                                 size_t length, int flags _U_, void *user_data) {
372      http2_session_data *session_data = (http2_session_data *)user_data;
373      struct bufferevent *bev = session_data->bev;
374      /* Avoid excessive buffering in server side. */
375      if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
376          OUTPUT_WOULDBLOCK_THRESHOLD) {
377        return NGHTTP2_ERR_WOULDBLOCK;
378      }
379      bufferevent_write(bev, data, length);
380      return (ssize_t)length;
381    }
382
383Since we use bufferevent to abstract network I/O, we just write the
384data to the bufferevent object. Note that `nghttp2_session_send()`
385continues to write all frames queued so far. If we were writing the
386data to a non-blocking socket directly using the ``write()`` system
387call in the ``send_callback()``, we'd soon receive an  ``EAGAIN`` or
388``EWOULDBLOCK`` error since sockets have a limited send buffer. If
389that happens, it's possible to return :macro:`NGHTTP2_ERR_WOULDBLOCK`
390to signal the nghttp2 library to stop sending further data. But here,
391when writing to the bufferevent, we have to regulate the amount data
392to buffered ourselves to avoid using huge amounts of memory. To
393achieve this, we check the size of the output buffer and if it reaches
394more than or equal to ``OUTPUT_WOULDBLOCK_THRESHOLD`` bytes, we stop
395writing data and return :macro:`NGHTTP2_ERR_WOULDBLOCK`.
396
397The next bufferevent callback is ``readcb()``, which is invoked when
398data is available to read in the bufferevent input buffer::
399
400    static void readcb(struct bufferevent *bev _U_, void *ptr) {
401      http2_session_data *session_data = (http2_session_data *)ptr;
402      if (session_recv(session_data) != 0) {
403        delete_http2_session_data(session_data);
404        return;
405      }
406    }
407
408In this function, we just call ``session_recv()`` to process incoming
409data.
410
411The third bufferevent callback is ``writecb()``, which is invoked when all
412data in the bufferevent output buffer has been sent::
413
414    static void writecb(struct bufferevent *bev, void *ptr) {
415      http2_session_data *session_data = (http2_session_data *)ptr;
416      if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
417        return;
418      }
419      if (nghttp2_session_want_read(session_data->session) == 0 &&
420          nghttp2_session_want_write(session_data->session) == 0) {
421        delete_http2_session_data(session_data);
422        return;
423      }
424      if (session_send(session_data) != 0) {
425        delete_http2_session_data(session_data);
426        return;
427      }
428    }
429
430First we check whether we should drop the connection or not. The
431nghttp2 session object keeps track of reception and transmission of
432GOAWAY frames and other error conditions as well. Using this
433information, the nghttp2 session object can state whether the
434connection should be dropped or not. More specifically, if both
435`nghttp2_session_want_read()` and `nghttp2_session_want_write()`
436return 0, the connection is no-longer required and can be closed.
437Since we are using bufferevent and its deferred callback option, the
438bufferevent output buffer may still contain pending data when the
439``writecb()`` is called. To handle this, we check whether the output
440buffer is empty or not. If all of these conditions are met, we drop
441connection.
442
443Otherwise, we call ``session_send()`` to process the pending output
444data. Remember that in ``send_callback()``, we must not write all data to
445bufferevent to avoid excessive buffering. We continue processing pending data
446when the output buffer becomes empty.
447
448We have already described the nghttp2 callback ``send_callback()``.  Let's
449learn about the remaining nghttp2 callbacks setup in
450``initialize_nghttp2_setup()`` function.
451
452The ``on_begin_headers_callback()`` function is invoked when the reception of
453a header block in HEADERS or PUSH_PROMISE frame is started::
454
455    static int on_begin_headers_callback(nghttp2_session *session,
456                                         const nghttp2_frame *frame,
457                                         void *user_data) {
458      http2_session_data *session_data = (http2_session_data *)user_data;
459      http2_stream_data *stream_data;
460
461      if (frame->hd.type != NGHTTP2_HEADERS ||
462          frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
463        return 0;
464      }
465      stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
466      nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
467                                           stream_data);
468      return 0;
469    }
470
471We are only interested in the HEADERS frame in this function. Since
472the HEADERS frame has several roles in the HTTP/2 protocol, we check
473that it is a request HEADERS, which opens new stream. If the frame is
474a request HEADERS, we create a ``http2_stream_data`` object to store
475the stream related data. We associate the created
476``http2_stream_data`` object with the stream in the nghttp2 session
477object using `nghttp2_set_stream_user_data()`. The
478``http2_stream_data`` object can later be easily retrieved from the
479stream, without searching through the doubly linked list.
480
481In this example server, we want to serve files relative to the current working
482directory in which the program was invoked. Each header name/value pair is
483emitted via ``on_header_callback`` function, which is called after
484``on_begin_headers_callback()``::
485
486    static int on_header_callback(nghttp2_session *session,
487                                  const nghttp2_frame *frame, const uint8_t *name,
488                                  size_t namelen, const uint8_t *value,
489                                  size_t valuelen, uint8_t flags _U_,
490                                  void *user_data _U_) {
491      http2_stream_data *stream_data;
492      const char PATH[] = ":path";
493      switch (frame->hd.type) {
494      case NGHTTP2_HEADERS:
495        if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
496          break;
497        }
498        stream_data =
499            nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
500        if (!stream_data || stream_data->request_path) {
501          break;
502        }
503        if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
504          size_t j;
505          for (j = 0; j < valuelen && value[j] != '?'; ++j)
506            ;
507          stream_data->request_path = percent_decode(value, j);
508        }
509        break;
510      }
511      return 0;
512    }
513
514We search for the ``:path`` header field among the request headers and
515store the requested path in the ``http2_stream_data`` object. In this
516example program, we ignore the ``:method`` header field and always
517treat the request as a GET request.
518
519The ``on_frame_recv_callback()`` function is invoked when a frame is
520fully received::
521
522    static int on_frame_recv_callback(nghttp2_session *session,
523                                      const nghttp2_frame *frame, void *user_data) {
524      http2_session_data *session_data = (http2_session_data *)user_data;
525      http2_stream_data *stream_data;
526      switch (frame->hd.type) {
527      case NGHTTP2_DATA:
528      case NGHTTP2_HEADERS:
529        /* Check that the client request has finished */
530        if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
531          stream_data =
532              nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
533          /* For DATA and HEADERS frame, this callback may be called after
534             on_stream_close_callback. Check that stream still alive. */
535          if (!stream_data) {
536            return 0;
537          }
538          return on_request_recv(session, session_data, stream_data);
539        }
540        break;
541      default:
542        break;
543      }
544      return 0;
545    }
546
547First we retrieve the ``http2_stream_data`` object associated with the
548stream in ``on_begin_headers_callback()`` using
549`nghttp2_session_get_stream_user_data()`. If the requested path
550cannot be served for some reason (e.g. file is not found), we send a
551404 response using ``error_reply()``.  Otherwise, we open
552the requested file and send its content. We send the header field
553``:status`` as a single response header.
554
555Sending the file content is performed by the ``send_response()`` function::
556
557    static int send_response(nghttp2_session *session, int32_t stream_id,
558                             nghttp2_nv *nva, size_t nvlen, int fd) {
559      int rv;
560      nghttp2_data_provider data_prd;
561      data_prd.source.fd = fd;
562      data_prd.read_callback = file_read_callback;
563
564      rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
565      if (rv != 0) {
566        warnx("Fatal error: %s", nghttp2_strerror(rv));
567        return -1;
568      }
569      return 0;
570    }
571
572nghttp2 uses the :type:`nghttp2_data_provider` structure to send the
573entity body to the remote peer. The ``source`` member of this
574structure is a union, which can be either a void pointer or an int
575(which is intended to be used as file descriptor). In this example
576server, we use it as a file descriptor. We also set the
577``file_read_callback()`` callback function to read the contents of the
578file::
579
580    static ssize_t file_read_callback(nghttp2_session *session _U_,
581                                      int32_t stream_id _U_, uint8_t *buf,
582                                      size_t length, uint32_t *data_flags,
583                                      nghttp2_data_source *source,
584                                      void *user_data _U_) {
585      int fd = source->fd;
586      ssize_t r;
587      while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
588        ;
589      if (r == -1) {
590        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
591      }
592      if (r == 0) {
593        *data_flags |= NGHTTP2_DATA_FLAG_EOF;
594      }
595      return r;
596    }
597
598If an error occurs while reading the file, we return
599:macro:`NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`.  This tells the
600library to send RST_STREAM to the stream.  When all data has been
601read, the :macro:`NGHTTP2_DATA_FLAG_EOF` flag is set to signal nghttp2
602that we have finished reading the file.
603
604The `nghttp2_submit_response()` function is used to send the response to the
605remote peer.
606
607The ``on_stream_close_callback()`` function is invoked when the stream
608is about to close::
609
610    static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
611                                        uint32_t error_code _U_, void *user_data) {
612      http2_session_data *session_data = (http2_session_data *)user_data;
613      http2_stream_data *stream_data;
614
615      stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
616      if (!stream_data) {
617        return 0;
618      }
619      remove_stream(session_data, stream_data);
620      delete_http2_stream_data(stream_data);
621      return 0;
622    }
623
624Lastly, we destroy the ``http2_stream_data`` object in this function,
625since the stream is about to close and we no longer need the object.
626