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 }