1/*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2012, 2014 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25#include "failmalloc_test.h"
26
27#include <stdio.h>
28#include <assert.h>
29
30#include <CUnit/CUnit.h>
31
32#include "nghttp2_session.h"
33#include "nghttp2_stream.h"
34#include "nghttp2_frame.h"
35#include "nghttp2_helper.h"
36#include "malloc_wrapper.h"
37#include "nghttp2_test_helper.h"
38
39typedef struct {
40  uint8_t data[8192];
41  uint8_t *datamark, *datalimit;
42} data_feed;
43
44typedef struct {
45  data_feed *df;
46  size_t data_source_length;
47} my_user_data;
48
49static void data_feed_init(data_feed *df, nghttp2_bufs *bufs) {
50  nghttp2_buf *buf;
51  size_t data_length;
52
53  buf = &bufs->head->buf;
54  data_length = nghttp2_buf_len(buf);
55
56  assert(data_length <= sizeof(df->data));
57  memcpy(df->data, buf->pos, data_length);
58  df->datamark = df->data;
59  df->datalimit = df->data + data_length;
60}
61
62static ssize_t null_send_callback(nghttp2_session *session, const uint8_t *data,
63                                  size_t len, int flags, void *user_data) {
64  (void)session;
65  (void)data;
66  (void)flags;
67  (void)user_data;
68
69  return (ssize_t)len;
70}
71
72static ssize_t data_feed_recv_callback(nghttp2_session *session, uint8_t *data,
73                                       size_t len, int flags, void *user_data) {
74  data_feed *df = ((my_user_data *)user_data)->df;
75  size_t avail = (size_t)(df->datalimit - df->datamark);
76  size_t wlen = nghttp2_min(avail, len);
77  (void)session;
78  (void)flags;
79
80  memcpy(data, df->datamark, wlen);
81  df->datamark += wlen;
82  return (ssize_t)wlen;
83}
84
85static ssize_t fixed_length_data_source_read_callback(
86    nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t len,
87    uint32_t *data_flags, nghttp2_data_source *source, void *user_data) {
88  my_user_data *ud = (my_user_data *)user_data;
89  size_t wlen;
90  (void)session;
91  (void)stream_id;
92  (void)buf;
93  (void)source;
94
95  if (len < ud->data_source_length) {
96    wlen = len;
97  } else {
98    wlen = ud->data_source_length;
99  }
100  ud->data_source_length -= wlen;
101  if (ud->data_source_length == 0) {
102    *data_flags = NGHTTP2_DATA_FLAG_EOF;
103  }
104  return (ssize_t)wlen;
105}
106
107#define TEST_FAILMALLOC_RUN(FUN)                                               \
108  do {                                                                         \
109    int nmalloc, i;                                                            \
110                                                                               \
111    nghttp2_failmalloc = 0;                                                    \
112    nghttp2_nmalloc = 0;                                                       \
113    FUN();                                                                     \
114    nmalloc = nghttp2_nmalloc;                                                 \
115                                                                               \
116    nghttp2_failmalloc = 1;                                                    \
117    for (i = 0; i < nmalloc; ++i) {                                            \
118      nghttp2_nmalloc = 0;                                                     \
119      nghttp2_failstart = i;                                                   \
120      /* printf("i=%zu\n", i); */                                              \
121      FUN();                                                                   \
122      /* printf("nmalloc=%d\n", nghttp2_nmalloc); */                           \
123    }                                                                          \
124    nghttp2_failmalloc = 0;                                                    \
125  } while (0)
126
127static void run_nghttp2_session_send(void) {
128  nghttp2_session *session;
129  nghttp2_session_callbacks callbacks;
130  nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"),
131                     MAKE_NV(":scheme", "https")};
132  nghttp2_data_provider data_prd;
133  nghttp2_settings_entry iv[2];
134  my_user_data ud;
135  int rv;
136  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
137  callbacks.send_callback = null_send_callback;
138
139  data_prd.read_callback = fixed_length_data_source_read_callback;
140  ud.data_source_length = 64 * 1024;
141
142  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
143  iv[0].value = 4096;
144  iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
145  iv[1].value = 100;
146
147  rv = nghttp2_session_client_new3(&session, &callbacks, &ud, NULL,
148                                   nghttp2_mem_fm());
149  if (rv != 0) {
150    goto client_new_fail;
151  }
152  rv = nghttp2_submit_request(session, NULL, nv, ARRLEN(nv), &data_prd, NULL);
153  if (rv < 0) {
154    goto fail;
155  }
156  rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, nv,
157                              ARRLEN(nv), NULL);
158  if (rv < 0) {
159    goto fail;
160  }
161  rv = nghttp2_session_send(session);
162  if (rv != 0) {
163    goto fail;
164  }
165  /* The HEADERS submitted by the previous nghttp2_submit_headers will
166     have stream ID 3. Send HEADERS to that stream. */
167  rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv,
168                              ARRLEN(nv), NULL);
169  if (rv != 0) {
170    goto fail;
171  }
172  rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd);
173  if (rv != 0) {
174    goto fail;
175  }
176  rv = nghttp2_session_send(session);
177  if (rv != 0) {
178    goto fail;
179  }
180  rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 3, NGHTTP2_CANCEL);
181  if (rv != 0) {
182    goto fail;
183  }
184  rv = nghttp2_session_send(session);
185  if (rv != 0) {
186    goto fail;
187  }
188  rv = nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL);
189  if (rv != 0) {
190    goto fail;
191  }
192  rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2);
193  if (rv != 0) {
194    goto fail;
195  }
196  rv = nghttp2_session_send(session);
197  if (rv != 0) {
198    goto fail;
199  }
200  rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 100, NGHTTP2_NO_ERROR,
201                             NULL, 0);
202  if (rv != 0) {
203    goto fail;
204  }
205  rv = nghttp2_session_send(session);
206  if (rv != 0) {
207    goto fail;
208  }
209
210fail:
211  nghttp2_session_del(session);
212client_new_fail:;
213}
214
215void test_nghttp2_session_send(void) {
216  TEST_FAILMALLOC_RUN(run_nghttp2_session_send);
217}
218
219static void run_nghttp2_session_send_server(void) {
220  nghttp2_session *session;
221  nghttp2_session_callbacks *callbacks;
222  int rv;
223  const uint8_t *txdata;
224  ssize_t txdatalen;
225  const uint8_t origin[] = "nghttp2.org";
226  const uint8_t altsvc_field_value[] = "h2=\":443\"";
227  static const uint8_t nghttp2[] = "https://nghttp2.org";
228  static const nghttp2_origin_entry ov = {
229      (uint8_t *)nghttp2,
230      sizeof(nghttp2) - 1,
231  };
232
233  rv = nghttp2_session_callbacks_new(&callbacks);
234  if (rv != 0) {
235    return;
236  }
237
238  rv = nghttp2_session_server_new3(&session, callbacks, NULL, NULL,
239                                   nghttp2_mem_fm());
240
241  nghttp2_session_callbacks_del(callbacks);
242
243  if (rv != 0) {
244    return;
245  }
246
247  rv = nghttp2_submit_altsvc(session, NGHTTP2_FLAG_NONE, 0, origin,
248                             sizeof(origin) - 1, altsvc_field_value,
249                             sizeof(altsvc_field_value) - 1);
250  if (rv != 0) {
251    goto fail;
252  }
253
254  rv = nghttp2_submit_origin(session, NGHTTP2_FLAG_NONE, &ov, 1);
255  if (rv != 0) {
256    goto fail;
257  }
258
259  txdatalen = nghttp2_session_mem_send(session, &txdata);
260
261  if (txdatalen < 0) {
262    goto fail;
263  }
264
265fail:
266  nghttp2_session_del(session);
267}
268
269void test_nghttp2_session_send_server(void) {
270  TEST_FAILMALLOC_RUN(run_nghttp2_session_send_server);
271}
272
273static void run_nghttp2_session_recv(void) {
274  nghttp2_session *session;
275  nghttp2_session_callbacks callbacks;
276  nghttp2_hd_deflater deflater;
277  nghttp2_frame frame;
278  nghttp2_bufs bufs;
279  nghttp2_nv nv[] = {
280      MAKE_NV(":method", "GET"),
281      MAKE_NV(":scheme", "https"),
282      MAKE_NV(":authority", "example.org"),
283      MAKE_NV(":path", "/"),
284  };
285  nghttp2_settings_entry iv[2];
286  my_user_data ud;
287  data_feed df;
288  int rv;
289  nghttp2_nv *nva;
290  size_t nvlen;
291
292  rv = frame_pack_bufs_init(&bufs);
293
294  if (rv != 0) {
295    return;
296  }
297
298  memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
299  callbacks.recv_callback = data_feed_recv_callback;
300  ud.df = &df;
301
302  nghttp2_failmalloc_pause();
303  nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm());
304  nghttp2_session_server_new3(&session, &callbacks, &ud, NULL,
305                              nghttp2_mem_fm());
306
307  /* Client preface */
308  nghttp2_bufs_add(&bufs, NGHTTP2_CLIENT_MAGIC, NGHTTP2_CLIENT_MAGIC_LEN);
309  data_feed_init(&df, &bufs);
310  nghttp2_bufs_reset(&bufs);
311  nghttp2_failmalloc_unpause();
312
313  rv = nghttp2_session_recv(session);
314  if (rv != 0) {
315    goto fail;
316  }
317
318  nghttp2_failmalloc_pause();
319  /* SETTINGS */
320  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
321  iv[0].value = 4096;
322  iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
323  iv[1].value = 100;
324  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE,
325                              nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm()),
326                              2);
327  nghttp2_frame_pack_settings(&bufs, &frame.settings);
328  nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm());
329  data_feed_init(&df, &bufs);
330  nghttp2_bufs_reset(&bufs);
331  nghttp2_failmalloc_unpause();
332
333  rv = nghttp2_session_recv(session);
334  if (rv != 0) {
335    goto fail;
336  }
337
338  nghttp2_failmalloc_pause();
339  /* HEADERS */
340  nvlen = ARRLEN(nv);
341  nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm());
342  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1,
343                             NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen);
344  nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
345  nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm());
346  data_feed_init(&df, &bufs);
347  nghttp2_bufs_reset(&bufs);
348  nghttp2_failmalloc_unpause();
349
350  rv = nghttp2_session_recv(session);
351  if (rv != 0) {
352    goto fail;
353  }
354
355  /* PING */
356  nghttp2_failmalloc_pause();
357  nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL);
358  nghttp2_frame_pack_ping(&bufs, &frame.ping);
359  nghttp2_frame_ping_free(&frame.ping);
360  data_feed_init(&df, &bufs);
361  nghttp2_bufs_reset(&bufs);
362
363  nghttp2_failmalloc_unpause();
364
365  rv = nghttp2_session_recv(session);
366  if (rv != 0) {
367    goto fail;
368  }
369
370  /* RST_STREAM */
371  nghttp2_failmalloc_pause();
372  nghttp2_frame_rst_stream_init(&frame.rst_stream, 1, NGHTTP2_PROTOCOL_ERROR);
373  nghttp2_frame_pack_rst_stream(&bufs, &frame.rst_stream);
374  nghttp2_frame_rst_stream_free(&frame.rst_stream);
375  nghttp2_bufs_reset(&bufs);
376
377  nghttp2_failmalloc_unpause();
378
379  rv = nghttp2_session_recv(session);
380  if (rv != 0) {
381    goto fail;
382  }
383
384fail:
385  nghttp2_bufs_free(&bufs);
386  nghttp2_session_del(session);
387  nghttp2_hd_deflate_free(&deflater);
388}
389
390void test_nghttp2_session_recv(void) {
391  TEST_FAILMALLOC_RUN(run_nghttp2_session_recv);
392}
393
394static void run_nghttp2_frame_pack_headers(void) {
395  nghttp2_hd_deflater deflater;
396  nghttp2_hd_inflater inflater;
397  nghttp2_frame frame, oframe;
398  nghttp2_bufs bufs;
399  nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"),
400                     MAKE_NV(":scheme", "https")};
401  int rv;
402  nghttp2_nv *nva;
403  size_t nvlen;
404
405  rv = frame_pack_bufs_init(&bufs);
406
407  if (rv != 0) {
408    return;
409  }
410
411  rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm());
412  if (rv != 0) {
413    goto deflate_init_fail;
414  }
415  rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm());
416  if (rv != 0) {
417    goto inflate_init_fail;
418  }
419  nvlen = ARRLEN(nv);
420  rv = nghttp2_nv_array_copy(&nva, nv, nvlen, nghttp2_mem_fm());
421  if (rv < 0) {
422    goto nv_copy_fail;
423  }
424  nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_STREAM, 1,
425                             NGHTTP2_HCAT_REQUEST, NULL, nva, nvlen);
426  rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater);
427  if (rv != 0) {
428    goto fail;
429  }
430  rv = unpack_framebuf(&oframe, &bufs);
431  if (rv != 0) {
432    goto fail;
433  }
434  nghttp2_frame_headers_free(&oframe.headers, nghttp2_mem_fm());
435
436fail:
437  nghttp2_frame_headers_free(&frame.headers, nghttp2_mem_fm());
438nv_copy_fail:
439  nghttp2_hd_inflate_free(&inflater);
440inflate_init_fail:
441  nghttp2_hd_deflate_free(&deflater);
442deflate_init_fail:
443  nghttp2_bufs_free(&bufs);
444}
445
446static void run_nghttp2_frame_pack_settings(void) {
447  nghttp2_frame frame, oframe;
448  nghttp2_bufs bufs;
449  nghttp2_buf *buf;
450  nghttp2_settings_entry iv[2], *iv_copy;
451  int rv;
452
453  rv = frame_pack_bufs_init(&bufs);
454
455  if (rv != 0) {
456    return;
457  }
458
459  iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
460  iv[0].value = 4096;
461  iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
462  iv[1].value = 100;
463
464  iv_copy = nghttp2_frame_iv_copy(iv, 2, nghttp2_mem_fm());
465
466  if (iv_copy == NULL) {
467    goto iv_copy_fail;
468  }
469
470  nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, iv_copy, 2);
471
472  rv = nghttp2_frame_pack_settings(&bufs, &frame.settings);
473
474  if (rv != 0) {
475    goto fail;
476  }
477
478  buf = &bufs.head->buf;
479
480  rv = nghttp2_frame_unpack_settings_payload2(
481      &oframe.settings.iv, &oframe.settings.niv, buf->pos + NGHTTP2_FRAME_HDLEN,
482      nghttp2_buf_len(buf) - NGHTTP2_FRAME_HDLEN, nghttp2_mem_fm());
483
484  if (rv != 0) {
485    goto fail;
486  }
487  nghttp2_frame_settings_free(&oframe.settings, nghttp2_mem_fm());
488
489fail:
490  nghttp2_frame_settings_free(&frame.settings, nghttp2_mem_fm());
491iv_copy_fail:
492  nghttp2_bufs_free(&bufs);
493}
494
495void test_nghttp2_frame(void) {
496  TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_headers);
497  TEST_FAILMALLOC_RUN(run_nghttp2_frame_pack_settings);
498}
499
500static int deflate_inflate(nghttp2_hd_deflater *deflater,
501                           nghttp2_hd_inflater *inflater, nghttp2_bufs *bufs,
502                           nghttp2_nv *nva, size_t nvlen, nghttp2_mem *mem) {
503  int rv;
504
505  rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, nva, nvlen);
506
507  if (rv != 0) {
508    return rv;
509  }
510
511  rv = (int)inflate_hd(inflater, NULL, bufs, 0, mem);
512
513  if (rv < 0) {
514    return rv;
515  }
516
517  nghttp2_bufs_reset(bufs);
518
519  return 0;
520}
521
522static void run_nghttp2_hd(void) {
523  nghttp2_hd_deflater deflater;
524  nghttp2_hd_inflater inflater;
525  nghttp2_bufs bufs;
526  int rv;
527  nghttp2_nv nva1[] = {
528      MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
529      MAKE_NV(":path", "/slashdot"),
530      MAKE_NV("accept-encoding", "gzip, deflate"), MAKE_NV("foo", "bar")};
531  nghttp2_nv nva2[] = {
532      MAKE_NV(":scheme", "https"), MAKE_NV(":authority", "example.org"),
533      MAKE_NV(":path", "/style.css"), MAKE_NV("cookie", "nghttp2=FTW"),
534      MAKE_NV("foo", "bar2")};
535
536  rv = frame_pack_bufs_init(&bufs);
537
538  if (rv != 0) {
539    return;
540  }
541
542  rv = nghttp2_hd_deflate_init(&deflater, nghttp2_mem_fm());
543
544  if (rv != 0) {
545    goto deflate_init_fail;
546  }
547
548  rv = nghttp2_hd_inflate_init(&inflater, nghttp2_mem_fm());
549
550  if (rv != 0) {
551    goto inflate_init_fail;
552  }
553
554  rv = deflate_inflate(&deflater, &inflater, &bufs, nva1, ARRLEN(nva1),
555                       nghttp2_mem_fm());
556
557  if (rv != 0) {
558    goto deflate_hd_fail;
559  }
560
561  rv = deflate_inflate(&deflater, &inflater, &bufs, nva2, ARRLEN(nva2),
562                       nghttp2_mem_fm());
563
564  if (rv != 0) {
565    goto deflate_hd_fail;
566  }
567
568deflate_hd_fail:
569  nghttp2_hd_inflate_free(&inflater);
570inflate_init_fail:
571  nghttp2_hd_deflate_free(&deflater);
572deflate_init_fail:
573  nghttp2_bufs_free(&bufs);
574}
575
576void test_nghttp2_hd(void) { TEST_FAILMALLOC_RUN(run_nghttp2_hd); }
577