tizen 2.4 release
[external/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 retreive. */
69       const char *uri;
70       /* Parsed result of the |uri| */
71       struct http_parser_url *u;
72       /* The authroity 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 sequence
188 (:macro:`NGHTTP2_CLIENT_CONNECTION_PREFACE`) and SETTINGS frame.  The
189 transmission of client connection header is done 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       bufferevent_write(session_data->bev, NGHTTP2_CLIENT_CONNECTION_PREFACE,
198                         NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
199       rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
200                                    ARRLEN(iv));
201       if (rv != 0) {
202         errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
203       }
204     }
205
206 Here we specify SETTINGS_MAX_CONCURRENT_STREAMS to 100, which is
207 really not needed for this tiny example progoram, but we are
208 demonstrating the use of SETTINGS frame. To queue the SETTINGS frame
209 for the transmission, we use `nghttp2_submit_settings()`. Note that
210 `nghttp2_submit_settings()` function only queues the frame and not
211 actually send it. All ``nghttp2_submit_*()`` family functions have
212 this property. To actually send the frame, `nghttp2_session_send()` is
213 used, which is described about later.
214
215 After the transmission of client connection header, we enqueue HTTP
216 request in ``submit_request()`` function::
217
218     static void submit_request(http2_session_data *session_data) {
219       int32_t stream_id;
220       http2_stream_data *stream_data = session_data->stream_data;
221       const char *uri = stream_data->uri;
222       const struct http_parser_url *u = stream_data->u;
223       nghttp2_nv hdrs[] = {
224           MAKE_NV2(":method", "GET"),
225           MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
226                   u->field_data[UF_SCHEMA].len),
227           MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
228           MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
229       fprintf(stderr, "Request headers:\n");
230       print_headers(stderr, hdrs, ARRLEN(hdrs));
231       stream_id = nghttp2_submit_request(session_data->session, NULL, hdrs,
232                                          ARRLEN(hdrs), NULL, stream_data);
233       if (stream_id < 0) {
234         errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
235       }
236
237       stream_data->stream_id = stream_id;
238     }
239
240 We build HTTP request header fields in ``hdrs`` which is an array of
241 :type:`nghttp2_nv`. There are 4 header fields to be sent: ``:method``,
242 ``:scheme``, ``:authority`` and ``:path``. To queue this HTTP request,
243 we use `nghttp2_submit_request()` function. The `stream_data` is
244 passed in *stream_user_data* parameter. It is used in nghttp2
245 callbacks which we'll describe about later.
246 `nghttp2_submit_request()` returns the newly assigned stream ID for
247 this request.
248
249 The next bufferevent callback is ``readcb()``, which is invoked when
250 data is available to read in the bufferevent input buffer::
251
252     static void readcb(struct bufferevent *bev, void *ptr) {
253       http2_session_data *session_data = (http2_session_data *)ptr;
254       ssize_t readlen;
255       struct evbuffer *input = bufferevent_get_input(bev);
256       size_t datalen = evbuffer_get_length(input);
257       unsigned char *data = evbuffer_pullup(input, -1);
258
259       readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
260       if (readlen < 0) {
261         warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
262         delete_http2_session_data(session_data);
263         return;
264       }
265       if (evbuffer_drain(input, readlen) != 0) {
266         warnx("Fatal error: evbuffer_drain failed");
267         delete_http2_session_data(session_data);
268         return;
269       }
270       if (session_send(session_data) != 0) {
271         delete_http2_session_data(session_data);
272         return;
273       }
274     }
275
276 In this function, we feed all unprocessed, received data to nghttp2
277 session object using `nghttp2_session_mem_recv()` function. The
278 `nghttp2_session_mem_recv()` processes the received data and may
279 invoke nghttp2 callbacks and also queue frames. Since there may be
280 pending frames, we call ``session_send()`` function to send those
281 frames. The ``session_send()`` function is defined as follows::
282
283     static int session_send(http2_session_data *session_data) {
284       int rv;
285
286       rv = nghttp2_session_send(session_data->session);
287       if (rv != 0) {
288         warnx("Fatal error: %s", nghttp2_strerror(rv));
289         return -1;
290       }
291       return 0;
292     }
293
294 The `nghttp2_session_send()` function serializes the frame into wire
295 format and call ``send_callback()`` function of type
296 :type:`nghttp2_send_callback`.  The ``send_callback()`` is defined as
297 follows::
298
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       bufferevent_write(bev, data, length);
304       return length;
305     }
306
307 Since we use bufferevent to abstract network I/O, we just write the
308 data to the bufferevent object. Note that `nghttp2_session_send()`
309 continues to write all frames queued so far. If we were writing the
310 data to the non-blocking socket directly using ``write()`` system call
311 in the ``send_callback()``, we will surely get ``EAGAIN`` or
312 ``EWOULDBLOCK`` since the socket has limited send buffer. If that
313 happens, we can return :macro:`NGHTTP2_ERR_WOULDBLOCK` to signal the
314 nghttp2 library to stop sending further data. But writing to the
315 bufferevent, we have to regulate the amount data to be buffered by
316 ourselves to avoid possible huge memory consumption. In this example
317 client, we do not limit anything. To see how to regulate the amount of
318 buffered data, see the ``send_callback()`` in the server tutorial.
319
320 The third bufferevent callback is ``writecb()``, which is invoked when
321 all data written in the bufferevent output buffer have been sent::
322
323     static void writecb(struct bufferevent *bev _U_, void *ptr) {
324       http2_session_data *session_data = (http2_session_data *)ptr;
325       if (nghttp2_session_want_read(session_data->session) == 0 &&
326           nghttp2_session_want_write(session_data->session) == 0 &&
327           evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
328         delete_http2_session_data(session_data);
329       }
330     }
331
332 As described earlier, we just write off all data in `send_callback()`,
333 we have no data to write in this function. All we have to do is check
334 we have to drop connection or not. The nghttp2 session object keeps
335 track of reception and transmission of GOAWAY frame and other error
336 conditions as well. Using these information, nghttp2 session object
337 will tell whether the connection should be dropped or not. More
338 specifically, both `nghttp2_session_want_read()` and
339 `nghttp2_session_want_write()` return 0, we have no business in the
340 connection. But since we are using bufferevent and its deferred
341 callback option, the bufferevent output buffer may contain the pending
342 data when the ``writecb()`` is called. To handle this situation, we
343 also check whether the output buffer is empty or not. If these
344 conditions are met, we drop connection.
345
346 We have already described about nghttp2 callback ``send_callback()``.
347 Let's describe remaining nghttp2 callbacks we setup in
348 ``initialize_nghttp2_setup()`` function.
349
350 Each request header name/value pair is emitted via
351 ``on_header_callback`` function::
352
353     static int on_header_callback(nghttp2_session *session _U_,
354                                   const nghttp2_frame *frame, const uint8_t *name,
355                                   size_t namelen, const uint8_t *value,
356                                   size_t valuelen, uint8_t flags _U_,
357                                   void *user_data) {
358       http2_session_data *session_data = (http2_session_data *)user_data;
359       switch (frame->hd.type) {
360       case NGHTTP2_HEADERS:
361         if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
362             session_data->stream_data->stream_id == frame->hd.stream_id) {
363           /* Print response headers for the initiated request. */
364           print_header(stderr, name, namelen, value, valuelen);
365           break;
366         }
367       }
368       return 0;
369     }
370
371 In this tutorial, we just print the name/value pair.
372
373 After all name/value pairs are emitted for a frame,
374 ``on_frame_recv_callback`` function is called::
375
376     static int on_frame_recv_callback(nghttp2_session *session _U_,
377                                       const nghttp2_frame *frame, void *user_data) {
378       http2_session_data *session_data = (http2_session_data *)user_data;
379       switch (frame->hd.type) {
380       case NGHTTP2_HEADERS:
381         if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
382             session_data->stream_data->stream_id == frame->hd.stream_id) {
383           fprintf(stderr, "All headers received\n");
384         }
385         break;
386       }
387       return 0;
388     }
389
390 In this tutorial, we are just interested in the HTTP response
391 HEADERS. We check te frame type and its category (it should be
392 :macro:`NGHTTP2_HCAT_RESPONSE` for HTTP response HEADERS). Also check
393 its stream ID.
394
395 The ``on_data_chunk_recv_callback()`` function is invoked when a chunk
396 of data is received from the remote peer::
397
398     static int on_data_chunk_recv_callback(nghttp2_session *session _U_,
399                                            uint8_t flags _U_, int32_t stream_id,
400                                            const uint8_t *data, size_t len,
401                                            void *user_data) {
402       http2_session_data *session_data = (http2_session_data *)user_data;
403       if (session_data->stream_data->stream_id == stream_id) {
404         fwrite(data, len, 1, stdout);
405       }
406       return 0;
407     }
408
409 In our case, a chunk of data is response body. After checking stream
410 ID, we just write the recieved data to the stdout. Note that the
411 output in the terminal may be corrupted if the response body contains
412 some binary data.
413
414 The ``on_stream_close_callback()`` function is invoked when the stream
415 is about to close::
416
417     static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
418                                         nghttp2_error_code error_code,
419                                         void *user_data) {
420       http2_session_data *session_data = (http2_session_data *)user_data;
421       int rv;
422
423       if (session_data->stream_data->stream_id == stream_id) {
424         fprintf(stderr, "Stream %d closed with error_code=%d\n", stream_id,
425                 error_code);
426         rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
427         if (rv != 0) {
428           return NGHTTP2_ERR_CALLBACK_FAILURE;
429         }
430       }
431       return 0;
432     }
433
434 If the stream ID matches the one we initiated, it means that its
435 stream is going to be closed. Since we have finished to get the
436 resource we want (or the stream was reset by RST_STREAM from the
437 remote peer), we call `nghttp2_session_terminate_session()` to
438 commencing the closure of the HTTP/2 session gracefully. If you have
439 some data associated for the stream to be closed, you may delete it
440 here.