Imported Upstream version 1.0.0
[platform/upstream/nghttp2.git] / doc / sources / tutorial-client.rst
1 Tutorial: HTTP/2 client
2 =========================
3
4 In this tutorial, we are going to write very primitive HTTP/2
5 client. The complete source code, `libevent-client.c`_, is attached at
6 the end of this page.  It also resides in examples directory in the
7 archive or repository.
8
9 This simple client takes 1 argument, HTTPS URI, and retrieves the
10 resource denoted by the URI. Its synopsis is like this::
11
12     $ libevent-client HTTPS_URI
13
14 We use libevent in this tutorial to handle networking I/O.  Please
15 note that nghttp2 itself does not depend on libevent.
16
17 First we do some setup routine for libevent and OpenSSL library in
18 function ``main()`` and ``run()``, which is not so relevant to nghttp2
19 library use. The one thing you should look at is setup NPN callback.
20 The NPN callback is used for the client to select the next application
21 protocol over the SSL/TLS transport. In this tutorial, we use
22 `nghttp2_select_next_protocol()` function to select the HTTP/2
23 protocol the library supports::
24
25     static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
26                                     unsigned char *outlen, const unsigned char *in,
27                                     unsigned int inlen, void *arg _U_) {
28       if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
29         errx(1, "Server did not advertise " NGHTTP2_PROTO_VERSION_ID);
30       }
31       return SSL_TLSEXT_ERR_OK;
32     }
33
34 The callback is set to the SSL_CTX object using
35 ``SSL_CTX_set_next_proto_select_cb()`` function::
36
37     static SSL_CTX *create_ssl_ctx(void) {
38       SSL_CTX *ssl_ctx;
39       ssl_ctx = SSL_CTX_new(SSLv23_client_method());
40       if (!ssl_ctx) {
41         errx(1, "Could not create SSL/TLS context: %s",
42              ERR_error_string(ERR_get_error(), NULL));
43       }
44       SSL_CTX_set_options(ssl_ctx,
45                           SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
46                               SSL_OP_NO_COMPRESSION |
47                               SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
48       SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
49       return ssl_ctx;
50     }
51
52 We use ``http2_session_data`` structure to store the data related to
53 the HTTP/2 session::
54
55     typedef struct {
56       nghttp2_session *session;
57       struct evdns_base *dnsbase;
58       struct bufferevent *bev;
59       http2_stream_data *stream_data;
60     } http2_session_data;
61
62 Since this program only handles 1 URI, it uses only 1 stream. We store
63 its stream specific data in ``http2_stream_data`` structure and the
64 ``stream_data`` points to it. The ``struct http2_stream_data`` is
65 defined as follows::
66
67     typedef struct {
68       /* The NULL-terminated URI string to retrieve. */
69       const char *uri;
70       /* Parsed result of the |uri| */
71       struct http_parser_url *u;
72       /* The authority portion of the |uri|, not NULL-terminated */
73       char *authority;
74       /* The path portion of the |uri|, including query, not
75          NULL-terminated */
76       char *path;
77       /* The length of the |authority| */
78       size_t authoritylen;
79       /* The length of the |path| */
80       size_t pathlen;
81       /* The stream ID of this stream */
82       int32_t stream_id;
83     } http2_stream_data;
84
85 We creates and initializes these structures in
86 ``create_http2_session_data()`` and ``create_http2_stream_data()``
87 respectively.
88
89 Then we call function ``initiate_connection()`` to start connecting to
90 the remote server::
91
92     static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
93                                     const char *host, uint16_t port,
94                                     http2_session_data *session_data) {
95       int rv;
96       struct bufferevent *bev;
97       SSL *ssl;
98
99       ssl = create_ssl(ssl_ctx);
100       bev = bufferevent_openssl_socket_new(
101           evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
102           BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
103       bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
104       rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
105                                                AF_UNSPEC, host, port);
106
107       if (rv != 0) {
108         errx(1, "Could not connect to the remote host %s", host);
109       }
110       session_data->bev = bev;
111     }
112
113 We set 3 callbacks for the bufferevent: ``reacb``, ``writecb`` and
114 ``eventcb``.
115
116 The ``eventcb()`` is invoked by libevent event loop when an event
117 (e.g., connection has been established, timeout, etc) happens on the
118 underlying network socket::
119
120     static void eventcb(struct bufferevent *bev, short events, void *ptr) {
121       http2_session_data *session_data = (http2_session_data *)ptr;
122       if (events & BEV_EVENT_CONNECTED) {
123         int fd = bufferevent_getfd(bev);
124         int val = 1;
125         fprintf(stderr, "Connected\n");
126         setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
127         initialize_nghttp2_session(session_data);
128         send_client_connection_header(session_data);
129         submit_request(session_data);
130         if (session_send(session_data) != 0) {
131           delete_http2_session_data(session_data);
132         }
133         return;
134       }
135       if (events & BEV_EVENT_EOF) {
136         warnx("Disconnected from the remote host");
137       } else if (events & BEV_EVENT_ERROR) {
138         warnx("Network error");
139       } else if (events & BEV_EVENT_TIMEOUT) {
140         warnx("Timeout");
141       }
142       delete_http2_session_data(session_data);
143     }
144
145 For ``BEV_EVENT_EOF``, ``BEV_EVENT_ERROR`` and ``BEV_EVENT_TIMEOUT``
146 event, we just simply tear down the connection. The
147 ``BEV_EVENT_CONNECTED`` event is invoked when SSL/TLS handshake is
148 finished successfully. We first initialize nghttp2 session object in
149 ``initialize_nghttp2_session()`` function::
150
151     static void initialize_nghttp2_session(http2_session_data *session_data) {
152       nghttp2_session_callbacks *callbacks;
153
154       nghttp2_session_callbacks_new(&callbacks);
155
156       nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
157
158       nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
159                                                            on_frame_recv_callback);
160
161       nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
162           callbacks, on_data_chunk_recv_callback);
163
164       nghttp2_session_callbacks_set_on_stream_close_callback(
165           callbacks, on_stream_close_callback);
166
167       nghttp2_session_callbacks_set_on_header_callback(callbacks,
168                                                        on_header_callback);
169
170       nghttp2_session_callbacks_set_on_begin_headers_callback(
171           callbacks, on_begin_headers_callback);
172
173       nghttp2_session_client_new(&session_data->session, callbacks, session_data);
174
175       nghttp2_session_callbacks_del(callbacks);
176     }
177
178 Since we are creating client, we use `nghttp2_session_client_new()` to
179 initialize nghttp2 session object.  We setup 7 callbacks for the
180 nghttp2 session. We'll explain these callbacks later.
181
182 The `delete_http2_session_data()` destroys ``session_data`` and frees
183 its bufferevent, so it closes underlying connection as well. It also
184 calls `nghttp2_session_del()` to delete nghttp2 session object.
185
186 We begin HTTP/2 communication by sending client connection preface,
187 which is 24 bytes magic byte string (:macro:`NGHTTP2_CLIENT_MAGIC`)
188 followed by SETTINGS frame.  First 24 bytes magic string is
189 automatically sent by nghttp2 library.  We send SETTINGS frame in
190 ``send_client_connection_header()``::
191
192     static void send_client_connection_header(http2_session_data *session_data) {
193       nghttp2_settings_entry iv[1] = {
194           {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
195       int rv;
196
197       /* client 24 bytes magic string will be sent by nghttp2 library */
198       rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
199                                    ARRLEN(iv));
200       if (rv != 0) {
201         errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
202       }
203     }
204
205 Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is
206 really not needed for this tiny example program, but we are
207 demonstrating the use of SETTINGS frame. To queue the SETTINGS frame
208 for the transmission, we use `nghttp2_submit_settings()`. Note that
209 `nghttp2_submit_settings()` function only queues the frame and not
210 actually send it. All ``nghttp2_submit_*()`` family functions have
211 this property. To actually send the frame, `nghttp2_session_send()` is
212 used, which is described about later.
213
214 After the transmission of client connection header, we enqueue HTTP
215 request in ``submit_request()`` function::
216
217     static void submit_request(http2_session_data *session_data) {
218       int32_t stream_id;
219       http2_stream_data *stream_data = session_data->stream_data;
220       const char *uri = stream_data->uri;
221       const struct http_parser_url *u = stream_data->u;
222       nghttp2_nv hdrs[] = {
223           MAKE_NV2(":method", "GET"),
224           MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
225                   u->field_data[UF_SCHEMA].len),
226           MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
227           MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
228       fprintf(stderr, "Request headers:\n");
229       print_headers(stderr, hdrs, ARRLEN(hdrs));
230       stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
231                                          ARRLEN(hdrs), NULL, stream_data);
232       if (stream_id < 0) {
233         errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
234       }
235
236       stream_data->stream_id = stream_id;
237     }
238
239 We build HTTP request header fields in ``hdrs`` which is an array of
240 :type:`nghttp2_nv`. There are 4 header fields to be sent: ``:method``,
241 ``:scheme``, ``:authority`` and ``:path``. To queue this HTTP request,
242 we use `nghttp2_submit_request()` function. The `stream_data` is
243 passed in *stream_user_data* parameter. It is used in nghttp2
244 callbacks which we'll describe about later.
245 `nghttp2_submit_request()` returns the newly assigned stream ID for
246 this request.
247
248 The next bufferevent callback is ``readcb()``, which is invoked when
249 data is available to read in the bufferevent input buffer::
250
251     static void readcb(struct bufferevent *bev, void *ptr) {
252       http2_session_data *session_data = (http2_session_data *)ptr;
253       ssize_t readlen;
254       struct evbuffer *input = bufferevent_get_input(bev);
255       size_t datalen = evbuffer_get_length(input);
256       unsigned char *data = evbuffer_pullup(input, -1);
257
258       readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
259       if (readlen < 0) {
260         warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
261         delete_http2_session_data(session_data);
262         return;
263       }
264       if (evbuffer_drain(input, readlen) != 0) {
265         warnx("Fatal error: evbuffer_drain failed");
266         delete_http2_session_data(session_data);
267         return;
268       }
269       if (session_send(session_data) != 0) {
270         delete_http2_session_data(session_data);
271         return;
272       }
273     }
274
275 In this function, we feed all unprocessed, received data to nghttp2
276 session object using `nghttp2_session_mem_recv()` function. The
277 `nghttp2_session_mem_recv()` processes the received data and may
278 invoke nghttp2 callbacks and also queue frames. Since there may be
279 pending frames, we call ``session_send()`` function to send those
280 frames. The ``session_send()`` function is defined as follows::
281
282     static int session_send(http2_session_data *session_data) {
283       int rv;
284
285       rv = nghttp2_session_send(session_data->session);
286       if (rv != 0) {
287         warnx("Fatal error: %s", nghttp2_strerror(rv));
288         return -1;
289       }
290       return 0;
291     }
292
293 The `nghttp2_session_send()` function serializes the frame into wire
294 format and call ``send_callback()`` function of type
295 :type:`nghttp2_send_callback`.  The ``send_callback()`` is defined as
296 follows::
297
298     static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
299                                  size_t length, int flags _U_, void *user_data) {
300       http2_session_data *session_data = (http2_session_data *)user_data;
301       struct bufferevent *bev = session_data->bev;
302       bufferevent_write(bev, data, length);
303       return length;
304     }
305
306 Since we use bufferevent to abstract network I/O, we just write the
307 data to the bufferevent object. Note that `nghttp2_session_send()`
308 continues to write all frames queued so far. If we were writing the
309 data to the non-blocking socket directly using ``write()`` system call
310 in the ``send_callback()``, we will surely get ``EAGAIN`` or
311 ``EWOULDBLOCK`` since the socket has limited send buffer. If that
312 happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the
313 nghttp2 library to stop sending further data. But writing to the
314 bufferevent, we have to regulate the amount data to be buffered by
315 ourselves to avoid possible huge memory consumption. In this example
316 client, we do not limit anything. To see how to regulate the amount of
317 buffered data, see the ``send_callback()`` in the server tutorial.
318
319 The third bufferevent callback is ``writecb()``, which is invoked when
320 all data written in the bufferevent output buffer have been sent::
321
322     static void writecb(struct bufferevent *bev _U_, void *ptr) {
323       http2_session_data *session_data = (http2_session_data *)ptr;
324       if (nghttp2_session_want_read(session_data->session) == 0 &&
325           nghttp2_session_want_write(session_data->session) == 0 &&
326           evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
327         delete_http2_session_data(session_data);
328       }
329     }
330
331 As described earlier, we just write off all data in `send_callback()`,
332 we have no data to write in this function. All we have to do is check
333 we have to drop connection or not. The nghttp2 session object keeps
334 track of reception and transmission of GOAWAY frame and other error
335 conditions as well. Using these information, nghttp2 session object
336 will tell whether the connection should be dropped or not. More
337 specifically, both `nghttp2_session_want_read()` and
338 `nghttp2_session_want_write()` return 0, we have no business in the
339 connection. But since we are using bufferevent and its deferred
340 callback option, the bufferevent output buffer may contain the pending
341 data when the ``writecb()`` is called. To handle this situation, we
342 also check whether the output buffer is empty or not. If these
343 conditions are met, we drop connection.
344
345 We have already described about nghttp2 callback ``send_callback()``.
346 Let's describe remaining nghttp2 callbacks we setup in
347 ``initialize_nghttp2_setup()`` function.
348
349 Each request header name/value pair is emitted via
350 ``on_header_callback`` function::
351
352     static int on_header_callback(nghttp2_session *session _U_,
353                                   const nghttp2_frame *frame, const uint8_t *name,
354                                   size_t namelen, const uint8_t *value,
355                                   size_t valuelen, uint8_t flags _U_,
356                                   void *user_data) {
357       http2_session_data *session_data = (http2_session_data *)user_data;
358       switch (frame->hd.type) {
359       case NGHTTP2_HEADERS:
360         if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
361             session_data->stream_data->stream_id == frame->hd.stream_id) {
362           /* Print response headers for the initiated request. */
363           print_header(stderr, name, namelen, value, valuelen);
364           break;
365         }
366       }
367       return 0;
368     }
369
370 In this tutorial, we just print the name/value pair.
371
372 After all name/value pairs are emitted for a frame,
373 ``on_frame_recv_callback`` function is called::
374
375     static int on_frame_recv_callback(nghttp2_session *session _U_,
376                                       const nghttp2_frame *frame, void *user_data) {
377       http2_session_data *session_data = (http2_session_data *)user_data;
378       switch (frame->hd.type) {
379       case NGHTTP2_HEADERS:
380         if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
381             session_data->stream_data->stream_id == frame->hd.stream_id) {
382           fprintf(stderr, "All headers received\n");
383         }
384         break;
385       }
386       return 0;
387     }
388
389 In this tutorial, we are just interested in the HTTP response
390 HEADERS. We check the frame type and its category (it should be
391 :macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check
392 its stream ID.
393
394 The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
395 of data is received from the remote peer::
396
397     static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
398                                            uint8_t flags _U_, int32_t stream_id,
399                                            const uint8_t *data, size_t len,
400                                            void *user_data) {
401       http2_session_data *session_data = (http2_session_data *)user_data;
402       if (session_data->stream_data->stream_id == stream_id) {
403         fwrite(data, len, 1, stdout);
404       }
405       return 0;
406     }
407
408 In our case, a chunk of data is response body. After checking stream
409 ID, we just write the received data to the stdout. Note that the
410 output in the terminal may be corrupted if the response body contains
411 some binary data.
412
413 The ``on_stream_close_callback()`` function is invoked when the stream
414 is about to close::
415
416     static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
417                                         nghttp2_error_code error_code,
418                                         void *user_data) {
419       http2_session_data *session_data = (http2_session_data *)user_data;
420       int rv;
421
422       if (session_data->stream_data->stream_id == stream_id) {
423         fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
424                 error_code);
425         rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
426         if (rv != 0) {
427           return NGHTTP2_ERR_CALLBACK_FAILURE;
428         }
429       }
430       return 0;
431     }
432
433 If the stream ID matches the one we initiated, it means that its
434 stream is going to be closed. Since we have finished to get the
435 resource we want (or the stream was reset by RST_STREAM from the
436 remote peer), we call `nghttp2_session_terminate_session()` to
437 commencing the closure of the HTTP/2 session gracefully. If you have
438 some data associated for the stream to be closed, you may delete it
439 here.