/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include static struct http_parser_settings settings_null = { .on_message_begin = 0, .on_header_field = 0, .on_header_value = 0, .on_url = 0, .on_status = 0, .on_body = 0, .on_headers_complete = 0, .on_message_complete = 0, .on_chunk_header = 0, .on_chunk_complete = 0}; struct url_test { const char *name; const char *url; int is_connect; struct http_parser_url u; int rv; }; const struct url_test url_tests[] = { { .name = "proxy request", .url = "http://hostname/", .is_connect = 0, .u = { .field_set = (1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH), .port = 0, .field_data = { { 0, 4 }, /* UF_SCHEMA */ { 7, 8 }, /* UF_HOST */ { 0, 0 }, /* UF_PORT */ { 15, 1 }, /* UF_PATH */ { 0, 0 }, /* UF_QUERY */ { 0, 0 }, /* UF_FRAGMENT */ { 0, 0 } } }, /* UF_USERINFO */ .rv = 0 }, { .name = "proxy request with port", .url = "http://hostname:444/", .is_connect = 0, .u = { .field_set = (1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH), .port = 444, .field_data = { { 0, 4 }, /* UF_SCHEMA */ { 7, 8 }, /* UF_HOST */ { 16, 3 }, /* UF_PORT */ { 19, 1 }, /* UF_PATH */ { 0, 0 }, /* UF_QUERY */ { 0, 0 }, /* UF_FRAGMENT */ { 0, 0 } } }, /* UF_USERINFO */ .rv = 0 }, { .name = "CONNECT request", .url = "hostname:443", .is_connect = 1, .u = { .field_set = (1 << UF_HOST) | (1 << UF_PORT), .port = 443, .field_data = { { 0, 0 }, /* UF_SCHEMA */ { 0, 8 }, /* UF_HOST */ { 9, 3 }, /* UF_PORT */ { 0, 0 }, /* UF_PATH */ { 0, 0 }, /* UF_QUERY */ { 0, 0 }, /* UF_FRAGMENT */ { 0, 0 } } }, /* UF_USERINFO */ .rv = 0 }, { .name = "CONNECT request but not connect", .url = "hostname:443", .is_connect = 0, .rv = 1 }, { .name = "proxy ipv6 request", .url = "http://[1:2::3:4]/", .is_connect = 0, .u = { .field_set = (1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH), .port = 0, .field_data = { { 0, 4 }, /* UF_SCHEMA */ { 8, 8 }, /* UF_HOST */ { 0, 0 }, /* UF_PORT */ { 17, 1 }, /* UF_PATH */ { 0, 0 }, /* UF_QUERY */ { 0, 0 }, /* UF_FRAGMENT */ { 0, 0 } } }, /* UF_USERINFO */ .rv = 0 }, { .name = "proxy ipv6 request with port", .url = "http://[1:2::3:4]:67/", .is_connect = 0, .u = { .field_set = (1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH), .port = 67, .field_data = { { 0, 4 }, /* UF_SCHEMA */ { 8, 8 }, /* UF_HOST */ { 18, 2 }, /* UF_PORT */ { 20, 1 }, /* UF_PATH */ { 0, 0 }, /* UF_QUERY */ { 0, 0 }, /* UF_FRAGMENT */ { 0, 0 } } }, /* UF_USERINFO */ .rv = 0 }, { .name = "CONNECT ipv6 address", .url = "[1:2::3:4]:443", .is_connect = 1, .u = { .field_set = (1 << UF_HOST) | (1 << UF_PORT), .port = 443, .field_data = { { 0, 0 }, /* UF_SCHEMA */ { 1, 8 }, /* UF_HOST */ { 11, 3 }, /* UF_PORT */ { 0, 0 }, /* UF_PATH */ { 0, 0 }, /* UF_QUERY */ { 0, 0 }, /* UF_FRAGMENT */ { 0, 0 } } }, /* UF_USERINFO */ .rv = 0 }, { .name = "ipv4 in ipv6 address", .url = "http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/", .is_connect = 0, .u = { .field_set = (1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH), .port = 0, .field_data = { { 0, 4 }, /* UF_SCHEMA */ { 8, 37 }, /* UF_HOST */ { 0, 0 }, /* UF_PORT */ { 46, 1 }, /* UF_PATH */ { 0, 0 }, /* UF_QUERY */ { 0, 0 }, /* UF_FRAGMENT */ { 0, 0 } } }, /* UF_USERINFO */ .rv = 0 }, { .name = "extra ? in query string", .url = "http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css," "fp-base-min.css,fp-channel-min.css,fp-product-min.css,fp-mall-" "min.css,fp-category-min.css,fp-sub-min.css,fp-gdp4p-min.css," "fp-css3-min.css,fp-misc-min.css?t=20101022.css", .is_connect = 0, .u = { .field_set = (1<url, strlen(test->url), test->is_connect, &u); if (test->rv == 0) { /**TESTPOINT: Check test_parse_url functions*/ zassert_false(rv, "http_parser_parse_url error"); zassert_false(memcmp(&u, &test->u, sizeof(u)), "test_parse_url failed"); } else { /* test->rv != 0 */ zassert_true(rv, "http_parser_parse_url error"); } } } ZTEST(http_header_fields_fn, test_method_str) { /**TESTPOINT: Check test_method_str function*/ zassert_false(strcmp("GET", http_method_str(HTTP_GET)), "http_method_str error"); zassert_false(strcmp("", http_method_str(127)), "http_method_str error"); } ZTEST(http_header_fields_fn, test_header_nread_value) { struct http_parser parser = { 0 }; const char *buf; size_t parsed; http_parser_init(&parser, HTTP_REQUEST); buf = "GET / HTTP/1.1\r\nheader: value\nhdr: value\r\n"; parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); zassert_equal(parsed, strlen(buf), "http_parser error"); zassert_equal(parser.nread, strlen(buf), "http_parser error"); } int test_invalid_header_content(int req, const char *str) { struct http_parser parser = { 0 }; const char *buf; size_t parsed; size_t buflen; http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.1 200 OK\r\n"; parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); /**TESTPOINTS: Check test_invalid_header_content functions*/ zassert_equal(parsed, strlen(buf), "http_parser_execute error"); buf = str; buflen = strlen(buf); parsed = http_parser_execute(&parser, &settings_null, buf, buflen); if (parsed != buflen) { zassert_equal(HTTP_PARSER_ERRNO(&parser), HPE_INVALID_HEADER_TOKEN, "http_parser_execute error"); return TC_PASS; } return TC_FAIL; } int test_invalid_header_field_content_error(int req) { int rc; rc = test_invalid_header_content(req, "Foo: F\01ailure"); /**TESTPOINTS: Check test_invalid_header_field_content_error*/ zassert_false(rc, "test_invalid_header_content error"); rc = test_invalid_header_content(req, "Foo: B\02ar"); zassert_false(rc, "test_invalid_header_content error"); return TC_PASS; } int test_invalid_header_field(int req, const char *str) { struct http_parser parser = { 0 }; const char *buf; size_t parsed; size_t buflen; http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.1 200 OK\r\n"; parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); /**TESTPOINTS: Check http_parser_execute*/ zassert_equal(parsed, strlen(buf), "http_parser_execute error"); buf = str; buflen = strlen(buf); parsed = http_parser_execute(&parser, &settings_null, buf, buflen); if (parsed != buflen) { zassert_equal(HTTP_PARSER_ERRNO(&parser), HPE_INVALID_HEADER_TOKEN, "http_parser_execute error"); return TC_PASS; } return TC_FAIL; } int test_invalid_header_field_token_error(int req) { int rc; rc = test_invalid_header_field(req, "Fo@: Failure"); /**TESTPOINTS: Check test_invalid_header_field_token_error*/ zassert_false(rc, "test_invalid_header_field error"); rc = test_invalid_header_field(req, "Foo\01\test: Bar"); zassert_false(rc, "test_invalid_header_field error"); return TC_PASS; } int test_double_content_length_error(int req) { struct http_parser parser = { 0 }; const char *buf; size_t parsed; size_t buflen; http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.1 200 OK\r\n"; parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); /**TESTPOINTS: Check http_parser_execute*/ zassert_equal(parsed, strlen(buf), "http_parser_execute error"); buf = "Content-Length: 0\r\nContent-Length: 1\r\n\r\n"; buflen = strlen(buf); parsed = http_parser_execute(&parser, &settings_null, buf, buflen); if (parsed != buflen) { int error = HTTP_PARSER_ERRNO(&parser); zassert_equal(error, HPE_UNEXPECTED_CONTENT_LENGTH, "http_parser_execute error"); return TC_PASS; } return TC_FAIL; } int test_chunked_content_length_error(int req) { struct http_parser parser = { 0 }; const char *buf; size_t parsed; size_t buflen; http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.1 200 OK\r\n"; parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); /**TESTPOINTS: Check http_parser_execute*/ zassert_equal(parsed, strlen(buf), "http_parser_execute error"); buf = "Transfer-Encoding: chunked\r\nContent-Length: 1\r\n\r\n"; buflen = strlen(buf); parsed = http_parser_execute(&parser, &settings_null, buf, buflen); if (parsed != buflen) { int error = HTTP_PARSER_ERRNO(&parser); zassert_equal(error, HPE_UNEXPECTED_CONTENT_LENGTH, "http_parser_execute error"); return TC_PASS; } return TC_FAIL; } int test_header_cr_no_lf_error(int req) { struct http_parser parser = { 0 }; const char *buf; size_t parsed; size_t buflen; http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.1 200 OK\r\n"; parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); /**TESTPOINTS: Check http_parser_execute*/ zassert_equal(parsed, strlen(buf), "http_parser_execute error"); buf = "Foo: 1\rBar: 1\r\n\r\n"; buflen = strlen(buf); parsed = http_parser_execute(&parser, &settings_null, buf, buflen); if (parsed != buflen) { int error = HTTP_PARSER_ERRNO(&parser); zassert_equal(error, HPE_LF_EXPECTED, "http_parser_execute error"); return TC_PASS; } return TC_FAIL; } ZTEST(http_header_fields_fn, test_http_header_fields) { int rc; /* header field tests */ rc = test_double_content_length_error(HTTP_REQUEST); /**TESTPOINT: Check test_double_content_length_error*/ zassert_false(rc, "test_double_content_length_error failed"); rc = test_chunked_content_length_error(HTTP_REQUEST); /**TESTPOINT: Check test_chunked_content_length_error*/ zassert_false(rc, "test_chunked_content_length_error failed"); rc = test_header_cr_no_lf_error(HTTP_REQUEST); /**TESTPOINT: Check test_header_cr_no_lf_error*/ zassert_false(rc, "test_header_cr_no_lf_error failed"); rc = test_invalid_header_field_token_error(HTTP_REQUEST); /**TESTPOINT: Check test_invalid_header_field_token_error*/ zassert_false(rc, "test_invalid_header_field_token_error failed"); rc = test_invalid_header_field_content_error(HTTP_REQUEST); /**TESTPOINT: Check test_invalid_header_field_content_error*/ zassert_false(rc, "test_invalid_header_field_content_error failed"); rc = test_double_content_length_error(HTTP_RESPONSE); /**TESTPOINT: Check test_double_content_length_error*/ zassert_false(rc, "test_double_content_length_error failed"); rc = test_chunked_content_length_error(HTTP_RESPONSE); /**TESTPOINT: Check test_chunked_content_length_error*/ zassert_false(rc, "test_chunked_content_length_error failed"); rc = test_header_cr_no_lf_error(HTTP_RESPONSE); /**TESTPOINT: Check test_header_cr_no_lf_error*/ zassert_false(rc, "test_header_cr_no_lf_error failed"); rc = test_invalid_header_field_token_error(HTTP_RESPONSE); /**TESTPOINT: Check test_invalid_header_field_token_error*/ zassert_false(rc, "test_invalid_header_field_token_error failed"); rc = test_invalid_header_field_content_error(HTTP_RESPONSE); /**TESTPOINT: Check test_invalid_header_field_content_error*/ zassert_false(rc, "test_invalid_header_field_content_error failed"); } ZTEST_SUITE(http_header_fields_fn, NULL, NULL, NULL, NULL, NULL);