github.com/moontrade/nogc@v0.1.7/net/http/test.cd (about)

     1  /* use `make test` to run the test */
     2  /*
     3   * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
     4   *                         Shigeo Mitsunari
     5   *
     6   * The software is licensed under either the MIT License (below) or the Perl
     7   * license.
     8   *
     9   * Permission is hereby granted, free of charge, to any person obtaining a copy
    10   * of this software and associated documentation files (the "Software"), to
    11   * deal in the Software without restriction, including without limitation the
    12   * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    13   * sell copies of the Software, and to permit persons to whom the Software is
    14   * furnished to do so, subject to the following conditions:
    15   *
    16   * The above copyright notice and this permission notice shall be included in
    17   * all copies or substantial portions of the Software.
    18   *
    19   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    20   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    21   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    22   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    23   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    24   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    25   * IN THE SOFTWARE.
    26   */
    27  #include <assert.h>
    28  #include <stdio.h>
    29  #include <stdlib.h>
    30  #include <string.h>
    31  #include <sys/mman.h>
    32  #include <sys/types.h>
    33  #include <unistd.h>
    34  #include "picotest/picotest.h"
    35  #include "picohttpparser.h"
    36  
    37  static int bufis(const char *s, size_t l, const char *t)
    38  {
    39      return strlen(t) == l && memcmp(s, t, l) == 0;
    40  }
    41  
    42  static char *inputbuf; /* point to the end of the buffer */
    43  
    44  static void test_request(void)
    45  {
    46      const char *method;
    47      size_t method_len;
    48      const char *path;
    49      size_t path_len;
    50      int minor_version;
    51      struct phr_header headers[4];
    52      size_t num_headers;
    53  
    54  #define PARSE(s, last_len, exp, comment)                                                                                           \
    55      do {                                                                                                                           \
    56          size_t slen = sizeof(s) - 1;                                                                                               \
    57          note(comment);                                                                                                             \
    58          num_headers = sizeof(headers) / sizeof(headers[0]);                                                                        \
    59          memcpy(inputbuf - slen, s, slen);                                                                                          \
    60          ok(phr_parse_request(inputbuf - slen, slen, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, \
    61                               last_len) == (exp == 0 ? (int)slen : exp));                                                           \
    62      } while (0)
    63  
    64      PARSE("GET / HTTP/1.0\r\n\r\n", 0, 0, "simple");
    65      ok(num_headers == 0);
    66      ok(bufis(method, method_len, "GET"));
    67      ok(bufis(path, path_len, "/"));
    68      ok(minor_version == 0);
    69  
    70      PARSE("GET / HTTP/1.0\r\n\r", 0, -2, "partial");
    71  
    72      PARSE("GET /hoge HTTP/1.1\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0, "parse headers");
    73      ok(num_headers == 2);
    74      ok(bufis(method, method_len, "GET"));
    75      ok(bufis(path, path_len, "/hoge"));
    76      ok(minor_version == 1);
    77      ok(bufis(headers[0].name, headers[0].name_len, "Host"));
    78      ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
    79      ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
    80      ok(bufis(headers[1].value, headers[1].value_len, ""));
    81  
    82      PARSE("GET /hoge HTTP/1.1\r\nHost: example.com\r\nUser-Agent: \343\201\262\343/1.0\r\n\r\n", 0, 0, "multibyte included");
    83      ok(num_headers == 2);
    84      ok(bufis(method, method_len, "GET"));
    85      ok(bufis(path, path_len, "/hoge"));
    86      ok(minor_version == 1);
    87      ok(bufis(headers[0].name, headers[0].name_len, "Host"));
    88      ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
    89      ok(bufis(headers[1].name, headers[1].name_len, "User-Agent"));
    90      ok(bufis(headers[1].value, headers[1].value_len, "\343\201\262\343/1.0"));
    91  
    92      PARSE("GET / HTTP/1.0\r\nfoo: \r\nfoo: b\r\n  \tc\r\n\r\n", 0, 0, "parse multiline");
    93      ok(num_headers == 3);
    94      ok(bufis(method, method_len, "GET"));
    95      ok(bufis(path, path_len, "/"));
    96      ok(minor_version == 0);
    97      ok(bufis(headers[0].name, headers[0].name_len, "foo"));
    98      ok(bufis(headers[0].value, headers[0].value_len, ""));
    99      ok(bufis(headers[1].name, headers[1].name_len, "foo"));
   100      ok(bufis(headers[1].value, headers[1].value_len, "b"));
   101      ok(headers[2].name == NULL);
   102      ok(bufis(headers[2].value, headers[2].value_len, "  \tc"));
   103  
   104      PARSE("GET / HTTP/1.0\r\nfoo : ab\r\n\r\n", 0, -1, "parse header name with trailing space");
   105  
   106      PARSE("GET", 0, -2, "incomplete 1");
   107      ok(method == NULL);
   108      PARSE("GET ", 0, -2, "incomplete 2");
   109      ok(bufis(method, method_len, "GET"));
   110      PARSE("GET /", 0, -2, "incomplete 3");
   111      ok(path == NULL);
   112      PARSE("GET / ", 0, -2, "incomplete 4");
   113      ok(bufis(path, path_len, "/"));
   114      PARSE("GET / H", 0, -2, "incomplete 5");
   115      PARSE("GET / HTTP/1.", 0, -2, "incomplete 6");
   116      PARSE("GET / HTTP/1.0", 0, -2, "incomplete 7");
   117      ok(minor_version == -1);
   118      PARSE("GET / HTTP/1.0\r", 0, -2, "incomplete 8");
   119      ok(minor_version == 0);
   120  
   121      PARSE("GET /hoge HTTP/1.0\r\n\r", strlen("GET /hoge HTTP/1.0\r\n\r") - 1, -2, "slowloris (incomplete)");
   122      PARSE("GET /hoge HTTP/1.0\r\n\r\n", strlen("GET /hoge HTTP/1.0\r\n\r\n") - 1, 0, "slowloris (complete)");
   123  
   124      PARSE(" / HTTP/1.0\r\n\r\n", 0, -1, "empty method");
   125      PARSE("GET  HTTP/1.0\r\n\r\n", 0, -1, "empty request-target");
   126  
   127      PARSE("GET / HTTP/1.0\r\n:a\r\n\r\n", 0, -1, "empty header name");
   128      PARSE("GET / HTTP/1.0\r\n :a\r\n\r\n", 0, -1, "header name (space only)");
   129  
   130      PARSE("G\0T / HTTP/1.0\r\n\r\n", 0, -1, "NUL in method");
   131      PARSE("G\tT / HTTP/1.0\r\n\r\n", 0, -1, "tab in method");
   132      PARSE(":GET / HTTP/1.0\r\n\r\n", 0, -1, "invalid method");
   133      PARSE("GET /\x7fhello HTTP/1.0\r\n\r\n", 0, -1, "DEL in uri-path");
   134      PARSE("GET / HTTP/1.0\r\na\0b: c\r\n\r\n", 0, -1, "NUL in header name");
   135      PARSE("GET / HTTP/1.0\r\nab: c\0d\r\n\r\n", 0, -1, "NUL in header value");
   136      PARSE("GET / HTTP/1.0\r\na\033b: c\r\n\r\n", 0, -1, "CTL in header name");
   137      PARSE("GET / HTTP/1.0\r\nab: c\033\r\n\r\n", 0, -1, "CTL in header value");
   138      PARSE("GET / HTTP/1.0\r\n/: 1\r\n\r\n", 0, -1, "invalid char in header value");
   139      PARSE("GET /\xa0 HTTP/1.0\r\nh: c\xa2y\r\n\r\n", 0, 0, "accept MSB chars");
   140      ok(num_headers == 1);
   141      ok(bufis(method, method_len, "GET"));
   142      ok(bufis(path, path_len, "/\xa0"));
   143      ok(minor_version == 0);
   144      ok(bufis(headers[0].name, headers[0].name_len, "h"));
   145      ok(bufis(headers[0].value, headers[0].value_len, "c\xa2y"));
   146  
   147      PARSE("GET / HTTP/1.0\r\n\x7c\x7e: 1\r\n\r\n", 0, 0, "accept |~ (though forbidden by SSE)");
   148      ok(num_headers == 1);
   149      ok(bufis(headers[0].name, headers[0].name_len, "\x7c\x7e"));
   150      ok(bufis(headers[0].value, headers[0].value_len, "1"));
   151  
   152      PARSE("GET / HTTP/1.0\r\n\x7b: 1\r\n\r\n", 0, -1, "disallow {");
   153  
   154      PARSE("GET / HTTP/1.0\r\nfoo: a \t \r\n\r\n", 0, 0, "exclude leading and trailing spaces in header value");
   155      ok(bufis(headers[0].value, headers[0].value_len, "a"));
   156  
   157      PARSE("GET   /   HTTP/1.0\r\n\r\n", 0, 0, "accept multiple spaces between tokens");
   158  
   159  #undef PARSE
   160  }
   161  
   162  static void test_response(void)
   163  {
   164      int minor_version;
   165      int status;
   166      const char *msg;
   167      size_t msg_len;
   168      struct phr_header headers[4];
   169      size_t num_headers;
   170  
   171  #define PARSE(s, last_len, exp, comment)                                                                                           \
   172      do {                                                                                                                           \
   173          size_t slen = sizeof(s) - 1;                                                                                               \
   174          note(comment);                                                                                                             \
   175          num_headers = sizeof(headers) / sizeof(headers[0]);                                                                        \
   176          memcpy(inputbuf - slen, s, slen);                                                                                          \
   177          ok(phr_parse_response(inputbuf - slen, slen, &minor_version, &status, &msg, &msg_len, headers, &num_headers, last_len) ==  \
   178             (exp == 0 ? (int)slen : exp));                                                                                          \
   179      } while (0)
   180  
   181      PARSE("HTTP/1.0 200 OK\r\n\r\n", 0, 0, "simple");
   182      ok(num_headers == 0);
   183      ok(status == 200);
   184      ok(minor_version == 0);
   185      ok(bufis(msg, msg_len, "OK"));
   186  
   187      PARSE("HTTP/1.0 200 OK\r\n\r", 0, -2, "partial");
   188  
   189      PARSE("HTTP/1.1 200 OK\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0, "parse headers");
   190      ok(num_headers == 2);
   191      ok(minor_version == 1);
   192      ok(status == 200);
   193      ok(bufis(msg, msg_len, "OK"));
   194      ok(bufis(headers[0].name, headers[0].name_len, "Host"));
   195      ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
   196      ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
   197      ok(bufis(headers[1].value, headers[1].value_len, ""));
   198  
   199      PARSE("HTTP/1.0 200 OK\r\nfoo: \r\nfoo: b\r\n  \tc\r\n\r\n", 0, 0, "parse multiline");
   200      ok(num_headers == 3);
   201      ok(minor_version == 0);
   202      ok(status == 200);
   203      ok(bufis(msg, msg_len, "OK"));
   204      ok(bufis(headers[0].name, headers[0].name_len, "foo"));
   205      ok(bufis(headers[0].value, headers[0].value_len, ""));
   206      ok(bufis(headers[1].name, headers[1].name_len, "foo"));
   207      ok(bufis(headers[1].value, headers[1].value_len, "b"));
   208      ok(headers[2].name == NULL);
   209      ok(bufis(headers[2].value, headers[2].value_len, "  \tc"));
   210  
   211      PARSE("HTTP/1.0 500 Internal Server Error\r\n\r\n", 0, 0, "internal server error");
   212      ok(num_headers == 0);
   213      ok(minor_version == 0);
   214      ok(status == 500);
   215      ok(bufis(msg, msg_len, "Internal Server Error"));
   216      ok(msg_len == sizeof("Internal Server Error") - 1);
   217  
   218      PARSE("H", 0, -2, "incomplete 1");
   219      PARSE("HTTP/1.", 0, -2, "incomplete 2");
   220      PARSE("HTTP/1.1", 0, -2, "incomplete 3");
   221      ok(minor_version == -1);
   222      PARSE("HTTP/1.1 ", 0, -2, "incomplete 4");
   223      ok(minor_version == 1);
   224      PARSE("HTTP/1.1 2", 0, -2, "incomplete 5");
   225      PARSE("HTTP/1.1 200", 0, -2, "incomplete 6");
   226      ok(status == 0);
   227      PARSE("HTTP/1.1 200 ", 0, -2, "incomplete 7");
   228      ok(status == 200);
   229      PARSE("HTTP/1.1 200 O", 0, -2, "incomplete 8");
   230      PARSE("HTTP/1.1 200 OK\r", 0, -2, "incomplete 9");
   231      ok(msg == NULL);
   232      PARSE("HTTP/1.1 200 OK\r\n", 0, -2, "incomplete 10");
   233      ok(bufis(msg, msg_len, "OK"));
   234      PARSE("HTTP/1.1 200 OK\n", 0, -2, "incomplete 11");
   235      ok(bufis(msg, msg_len, "OK"));
   236  
   237      PARSE("HTTP/1.1 200 OK\r\nA: 1\r", 0, -2, "incomplete 11");
   238      ok(num_headers == 0);
   239      PARSE("HTTP/1.1 200 OK\r\nA: 1\r\n", 0, -2, "incomplete 12");
   240      ok(num_headers == 1);
   241      ok(bufis(headers[0].name, headers[0].name_len, "A"));
   242      ok(bufis(headers[0].value, headers[0].value_len, "1"));
   243  
   244      PARSE("HTTP/1.0 200 OK\r\n\r", strlen("HTTP/1.0 200 OK\r\n\r") - 1, -2, "slowloris (incomplete)");
   245      PARSE("HTTP/1.0 200 OK\r\n\r\n", strlen("HTTP/1.0 200 OK\r\n\r\n") - 1, 0, "slowloris (complete)");
   246  
   247      PARSE("HTTP/1. 200 OK\r\n\r\n", 0, -1, "invalid http version");
   248      PARSE("HTTP/1.2z 200 OK\r\n\r\n", 0, -1, "invalid http version 2");
   249      PARSE("HTTP/1.1  OK\r\n\r\n", 0, -1, "no status code");
   250  
   251      PARSE("HTTP/1.1 200\r\n\r\n", 0, 0, "accept missing trailing whitespace in status-line");
   252      ok(bufis(msg, msg_len, ""));
   253      PARSE("HTTP/1.1 200X\r\n\r\n", 0, -1, "garbage after status 1");
   254      PARSE("HTTP/1.1 200X \r\n\r\n", 0, -1, "garbage after status 2");
   255      PARSE("HTTP/1.1 200X OK\r\n\r\n", 0, -1, "garbage after status 3");
   256  
   257      PARSE("HTTP/1.1 200 OK\r\nbar: \t b\t \t\r\n\r\n", 0, 0, "exclude leading and trailing spaces in header value");
   258      ok(bufis(headers[0].value, headers[0].value_len, "b"));
   259  
   260      PARSE("HTTP/1.1   200   OK\r\n\r\n", 0, 0, "accept multiple spaces between tokens");
   261  
   262  #undef PARSE
   263  }
   264  
   265  static void test_headers(void)
   266  {
   267      /* only test the interface; the core parser is tested by the tests above */
   268  
   269      struct phr_header headers[4];
   270      size_t num_headers;
   271  
   272  #define PARSE(s, last_len, exp, comment)                                                                                           \
   273      do {                                                                                                                           \
   274          note(comment);                                                                                                             \
   275          num_headers = sizeof(headers) / sizeof(headers[0]);                                                                        \
   276          ok(phr_parse_headers(s, strlen(s), headers, &num_headers, last_len) == (exp == 0 ? (int)strlen(s) : exp));                 \
   277      } while (0)
   278  
   279      PARSE("Host: example.com\r\nCookie: \r\n\r\n", 0, 0, "simple");
   280      ok(num_headers == 2);
   281      ok(bufis(headers[0].name, headers[0].name_len, "Host"));
   282      ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
   283      ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
   284      ok(bufis(headers[1].value, headers[1].value_len, ""));
   285  
   286      PARSE("Host: example.com\r\nCookie: \r\n\r\n", 1, 0, "slowloris");
   287      ok(num_headers == 2);
   288      ok(bufis(headers[0].name, headers[0].name_len, "Host"));
   289      ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
   290      ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
   291      ok(bufis(headers[1].value, headers[1].value_len, ""));
   292  
   293      PARSE("Host: example.com\r\nCookie: \r\n\r", 0, -2, "partial");
   294  
   295      PARSE("Host: e\7fample.com\r\nCookie: \r\n\r", 0, -1, "error");
   296  
   297  #undef PARSE
   298  }
   299  
   300  static void test_chunked_at_once(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected)
   301  {
   302      struct phr_chunked_decoder dec = {0};
   303      char *buf;
   304      size_t bufsz;
   305      ssize_t ret;
   306  
   307      dec.consume_trailer = consume_trailer;
   308  
   309      note("testing at-once, source at line %d", line);
   310  
   311      buf = strdup(encoded);
   312      bufsz = strlen(buf);
   313  
   314      ret = phr_decode_chunked(&dec, buf, &bufsz);
   315  
   316      ok(ret == expected);
   317      ok(bufsz == strlen(decoded));
   318      ok(bufis(buf, bufsz, decoded));
   319      if (expected >= 0) {
   320          if (ret == expected)
   321              ok(bufis(buf + bufsz, ret, encoded + strlen(encoded) - ret));
   322          else
   323              ok(0);
   324      }
   325  
   326      free(buf);
   327  }
   328  
   329  static void test_chunked_per_byte(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected)
   330  {
   331      struct phr_chunked_decoder dec = {0};
   332      char *buf = malloc(strlen(encoded) + 1);
   333      size_t bytes_to_consume = strlen(encoded) - (expected >= 0 ? expected : 0), bytes_ready = 0, bufsz, i;
   334      ssize_t ret;
   335  
   336      dec.consume_trailer = consume_trailer;
   337  
   338      note("testing per-byte, source at line %d", line);
   339  
   340      for (i = 0; i < bytes_to_consume - 1; ++i) {
   341          buf[bytes_ready] = encoded[i];
   342          bufsz = 1;
   343          ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz);
   344          if (ret != -2) {
   345              ok(0);
   346              goto cleanup;
   347          }
   348          bytes_ready += bufsz;
   349      }
   350      strcpy(buf + bytes_ready, encoded + bytes_to_consume - 1);
   351      bufsz = strlen(buf + bytes_ready);
   352      ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz);
   353      ok(ret == expected);
   354      bytes_ready += bufsz;
   355      ok(bytes_ready == strlen(decoded));
   356      ok(bufis(buf, bytes_ready, decoded));
   357      if (expected >= 0) {
   358          if (ret == expected)
   359              ok(bufis(buf + bytes_ready, expected, encoded + bytes_to_consume));
   360          else
   361              ok(0);
   362      }
   363  
   364  cleanup:
   365      free(buf);
   366  }
   367  
   368  static void test_chunked_failure(int line, const char *encoded, ssize_t expected)
   369  {
   370      struct phr_chunked_decoder dec = {0};
   371      char *buf = strdup(encoded);
   372      size_t bufsz, i;
   373      ssize_t ret;
   374  
   375      note("testing failure at-once, source at line %d", line);
   376      bufsz = strlen(buf);
   377      ret = phr_decode_chunked(&dec, buf, &bufsz);
   378      ok(ret == expected);
   379  
   380      note("testing failure per-byte, source at line %d", line);
   381      memset(&dec, 0, sizeof(dec));
   382      for (i = 0; encoded[i] != '\0'; ++i) {
   383          buf[0] = encoded[i];
   384          bufsz = 1;
   385          ret = phr_decode_chunked(&dec, buf, &bufsz);
   386          if (ret == -1) {
   387              ok(ret == expected);
   388              goto cleanup;
   389          } else if (ret == -2) {
   390              /* continue */
   391          } else {
   392              ok(0);
   393              goto cleanup;
   394          }
   395      }
   396      ok(ret == expected);
   397  
   398  cleanup:
   399      free(buf);
   400  }
   401  
   402  static void (*chunked_test_runners[])(int, int, const char *, const char *, ssize_t) = {test_chunked_at_once, test_chunked_per_byte,
   403                                                                                          NULL};
   404  
   405  static void test_chunked(void)
   406  {
   407      size_t i;
   408  
   409      for (i = 0; chunked_test_runners[i] != NULL; ++i) {
   410          chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
   411          chunked_test_runners[i](__LINE__, 0, "6\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
   412          chunked_test_runners[i](__LINE__, 0, "6;comment=hi\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
   413          chunked_test_runners[i](__LINE__, 0, "6\r\nhello \r\n5\r\nworld\r\n0\r\na: b\r\nc: d\r\n\r\n", "hello world",
   414                                  sizeof("a: b\r\nc: d\r\n\r\n") - 1);
   415          chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
   416      }
   417  
   418      note("failures");
   419      test_chunked_failure(__LINE__, "z\r\nabcdefg", -1);
   420      if (sizeof(size_t) == 8) {
   421          test_chunked_failure(__LINE__, "6\r\nhello \r\nffffffffffffffff\r\nabcdefg", -2);
   422          test_chunked_failure(__LINE__, "6\r\nhello \r\nfffffffffffffffff\r\nabcdefg", -1);
   423      }
   424  }
   425  
   426  static void test_chunked_consume_trailer(void)
   427  {
   428      size_t i;
   429  
   430      for (i = 0; chunked_test_runners[i] != NULL; ++i) {
   431          chunked_test_runners[i](__LINE__, 1, "b\r\nhello world\r\n0\r\n", "hello world", -2);
   432          chunked_test_runners[i](__LINE__, 1, "6\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", -2);
   433          chunked_test_runners[i](__LINE__, 1, "6;comment=hi\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", -2);
   434          chunked_test_runners[i](__LINE__, 1, "b\r\nhello world\r\n0\r\n\r\n", "hello world", 0);
   435          chunked_test_runners[i](__LINE__, 1, "b\nhello world\n0\n\n", "hello world", 0);
   436          chunked_test_runners[i](__LINE__, 1, "6\r\nhello \r\n5\r\nworld\r\n0\r\na: b\r\nc: d\r\n\r\n", "hello world", 0);
   437      }
   438  }
   439  
   440  static void test_chunked_leftdata(void)
   441  {
   442  #define NEXT_REQ "GET / HTTP/1.1\r\n\r\n"
   443  
   444      struct phr_chunked_decoder dec = {0};
   445      dec.consume_trailer = 1;
   446      char buf[] = "5\r\nabcde\r\n0\r\n\r\n" NEXT_REQ;
   447      size_t bufsz = sizeof(buf) - 1;
   448  
   449      ssize_t ret = phr_decode_chunked(&dec, buf, &bufsz);
   450      ok(ret >= 0);
   451      ok(bufsz == 5);
   452      ok(memcmp(buf, "abcde", 5) == 0);
   453      ok(ret == sizeof(NEXT_REQ) - 1);
   454      ok(memcmp(buf + bufsz, NEXT_REQ, sizeof(NEXT_REQ) - 1) == 0);
   455  
   456  #undef NEXT_REQ
   457  }
   458  
   459  int main(void)
   460  {
   461      long pagesize = sysconf(_SC_PAGESIZE);
   462      assert(pagesize >= 1);
   463  
   464      inputbuf = mmap(NULL, pagesize * 3, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
   465      assert(inputbuf != MAP_FAILED);
   466      inputbuf += pagesize * 2;
   467      ok(mprotect(inputbuf - pagesize, pagesize, PROT_READ | PROT_WRITE) == 0);
   468  
   469      subtest("request", test_request);
   470      subtest("response", test_response);
   471      subtest("headers", test_headers);
   472      subtest("chunked", test_chunked);
   473      subtest("chunked-consume-trailer", test_chunked_consume_trailer);
   474      subtest("chunked-leftdata", test_chunked_leftdata);
   475  
   476      munmap(inputbuf - pagesize * 2, pagesize * 3);
   477  
   478      return done_testing();
   479  }