blob: 9bbb7986d7b5363fea62111a8a8b9010894cd572 [file] [log] [blame]
/* 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 <net/http_parser.h>
#include <string.h>
#include <errno.h>
#include <misc/printk.h>
#include <tc_util.h>
#include <ztest.h>
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<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) |
(1<<UF_QUERY),
.port = 0,
.field_data = {
{ 0, 4 }, /* UF_SCHEMA */
{ 7, 10 }, /* UF_HOST */
{ 0, 0 }, /* UF_PORT */
{ 17, 12 }, /* UF_PATH */
{ 30, 187}, /* UF_QUERY */
{ 0, 0 }, /* UF_FRAGMENT */
{ 0, 0 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "space URL encoded",
.url = "/toto.html?toto=a%20b",
.is_connect = 0,
.u = { .field_set = (1<<UF_PATH) | (1<<UF_QUERY),
.port = 0,
.field_data = {
{ 0, 0 }, /* UF_SCHEMA */
{ 0, 0 }, /* UF_HOST */
{ 0, 0 }, /* UF_PORT */
{ 0, 10 }, /* UF_PATH */
{ 11, 10 }, /* UF_QUERY */
{ 0, 0 }, /* UF_FRAGMENT */
{ 0, 0 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "URL fragment",
.url = "/toto.html#titi",
.is_connect = 0,
.u = { .field_set = (1<<UF_PATH) | (1<<UF_FRAGMENT),
.port = 0,
.field_data = {
{ 0, 0 }, /* UF_SCHEMA */
{ 0, 0 }, /* UF_HOST */
{ 0, 0 }, /* UF_PORT */
{ 0, 10 }, /* UF_PATH */
{ 0, 0 }, /* UF_QUERY */
{ 11, 4 }, /* UF_FRAGMENT */
{ 0, 0 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "complex URL fragment",
.url = "http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
"http://www.example.com/index.html?foo=bar&hello=world#midpage",
.is_connect = 0,
.u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) |
(1<<UF_QUERY) | (1<<UF_FRAGMENT),
.port = 0,
.field_data = {
{ 0, 4 }, /* UF_SCHEMA */
{ 7, 22 }, /* UF_HOST */
{ 0, 0 }, /* UF_PORT */
{ 29, 6 }, /* UF_PATH */
{ 36, 69 }, /* UF_QUERY */
{106, 7 }, /* UF_FRAGMENT */
{ 0, 0 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "complex URL from node js url parser doc",
.url = "http://host.com:8080/p/a/t/h?query=string#hash",
.is_connect = 0,
.u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) |
(1<<UF_PATH) | (1<<UF_QUERY) | (1<<UF_FRAGMENT),
.port = 8080,
.field_data = {
{ 0, 4 }, /* UF_SCHEMA */
{ 7, 8 }, /* UF_HOST */
{ 16, 4 }, /* UF_PORT */
{ 20, 8 }, /* UF_PATH */
{ 29, 12 }, /* UF_QUERY */
{ 42, 4 }, /* UF_FRAGMENT */
{ 0, 0 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "complex URL with basic auth from node js url parser doc",
.url = "http://a:b@host.com:8080/p/a/t/h?query=string#hash",
.is_connect = 0,
.u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) |
(1<<UF_PATH) | (1<<UF_QUERY) | (1<<UF_FRAGMENT) |
(1<<UF_USERINFO),
.port = 8080,
.field_data = {
{ 0, 4 }, /* UF_SCHEMA */
{ 11, 8 }, /* UF_HOST */
{ 20, 4 }, /* UF_PORT */
{ 24, 8 }, /* UF_PATH */
{ 33, 12 }, /* UF_QUERY */
{ 46, 4 }, /* UF_FRAGMENT */
{ 7, 3 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "double @",
.url = "http://a:b@@hostname:443/",
.is_connect = 0,
.rv = 1
},
{
.name = "proxy empty host",
.url = "http://:443/",
.is_connect = 0,
.rv = 1
},
{
.name = "proxy empty port",
.url = "http://hostname:/",
.is_connect = 0,
.rv = 1,
},
{
.name = "CONNECT with basic auth",
.url = "a:b@hostname:443",
.is_connect = 1,
.rv = 1,
},
{
.name = "CONNECT empty host",
.url = ":443",
.is_connect = 1,
.rv = 1,
},
{
.name = "CONNECT empty port",
.url = "hostname:",
.is_connect = 1,
.rv = 1,
},
{
.name = "CONNECT with extra bits",
.url = "hostname:443/",
.is_connect = 1,
.rv = 1,
},
{
.name = "space in URL",
.url = "/foo bar/",
.rv = 1, /* s_dead */
},
{
.name = "proxy basic auth with space url encoded",
.url = "http://a%20:b@host.com/",
.is_connect = 0,
.u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) |
(1<<UF_USERINFO),
.port = 0,
.field_data = {
{ 0, 4 }, /* UF_SCHEMA */
{ 14, 8 }, /* UF_HOST */
{ 0, 0 }, /* UF_PORT */
{ 22, 1 }, /* UF_PATH */
{ 0, 0 }, /* UF_QUERY */
{ 0, 0 }, /* UF_FRAGMENT */
{ 7, 6 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "carriage return in URL",
.url = "/foo\rbar/",
.rv = 1 /* s_dead */
},
{
.name = "proxy double : in URL",
.url = "http://hostname::443/",
.rv = 1 /* s_dead */
},
{
.name = "proxy basic auth with double :",
.url = "http://a::b@host.com/",
.is_connect = 0,
.u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) |
(1<<UF_USERINFO),
.port = 0,
.field_data = {
{ 0, 4 }, /* UF_SCHEMA */
{ 12, 8 }, /* UF_HOST */
{ 0, 0 }, /* UF_PORT */
{ 20, 1 }, /* UF_PATH */
{ 0, 0 }, /* UF_QUERY */
{ 0, 0 }, /* UF_FRAGMENT */
{ 7, 4 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "line feed in URL",
.url = "/foo\nbar/",
.rv = 1 /* s_dead */
},
{
.name = "proxy empty basic auth",
.url = "http://@hostname/fo",
.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 */
{ 16, 3 }, /* UF_PATH */
{ 0, 0 }, /* UF_QUERY */
{ 0, 0 }, /* UF_FRAGMENT */
{ 0, 0 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "proxy line feed in hostname",
.url = "http://host\name/fo",
.rv = 1 /* s_dead */
},
{
.name = "proxy % in hostname",
.url = "http://host%name/fo",
.rv = 1 /* s_dead */
},
{
.name = "proxy ; in hostname",
.url = "http://host;ame/fo",
.rv = 1 /* s_dead */
},
{
.name = "proxy basic auth with unreservedchars",
.url = "http://a!;-_!=+$@host.com/",
.is_connect = 0,
.u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) |
(1<<UF_USERINFO),
.port = 0,
.field_data = {
{ 0, 4 }, /* UF_SCHEMA */
{ 17, 8 }, /* UF_HOST */
{ 0, 0 }, /* UF_PORT */
{ 25, 1 }, /* UF_PATH */
{ 0, 0 }, /* UF_QUERY */
{ 0, 0 }, /* UF_FRAGMENT */
{ 7, 9 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "proxy only empty basic auth",
.url = "http://@/fo",
.rv = 1 /* s_dead */
},
{
.name = "proxy only basic auth",
.url = "http://toto@/fo",
.rv = 1 /* s_dead */
},
{
.name = "proxy emtpy hostname",
.url = "http:///fo",
.rv = 1 /* s_dead */
},
{
.name = "proxy = in URL",
.url = "http://host=ame/fo",
.rv = 1 /* s_dead */
},
{
.name = "ipv6 address with Zone ID",
.url = "http://[fe80::a%25eth0]/",
.is_connect = 0,
.u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH),
.port = 0,
.field_data = {
{ 0, 4 }, /* UF_SCHEMA */
{ 8, 14 }, /* UF_HOST */
{ 0, 0 }, /* UF_PORT */
{ 23, 1 }, /* UF_PATH */
{ 0, 0 }, /* UF_QUERY */
{ 0, 0 }, /* UF_FRAGMENT */
{ 0, 0 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "ipv6 address with Zone ID, but '%' is not percent-encoded",
.url = "http://[fe80::a%eth0]/",
.is_connect = 0,
.u = { .field_set = (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH),
.port = 0,
.field_data = {
{ 0, 4 }, /* UF_SCHEMA */
{ 8, 12 }, /* UF_HOST */
{ 0, 0 }, /* UF_PORT */
{ 21, 1 }, /* UF_PATH */
{ 0, 0 }, /* UF_QUERY */
{ 0, 0 }, /* UF_FRAGMENT */
{ 0, 0 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "ipv6 address ending with '%'",
.url = "http://[fe80::a%]/",
.rv = 1 /* s_dead */
},
{
.name = "ipv6 address with Zone ID including bad character",
.url = "http://[fe80::a%$HOME]/",
.rv = 1 /* s_dead */
},
{
.name = "just ipv6 Zone ID",
.url = "http://[%eth0]/",
.rv = 1 /* s_dead */
},
#ifdef CONFIG_HTTP_PARSER_STRICT
{
.name = "tab in URL",
.url = "/foo\tbar/",
.rv = 1 /* s_dead */
},
{
.name = "form feed in URL",
.url = "/foo\fbar/",
.rv = 1 /* s_dead */
},
#else /* !HTTP_PARSER_STRICT */
{
.name = "tab in URL",
.url = "/foo\tbar/",
.u = { .field_set = (1 << UF_PATH),
.field_data = {
{ 0, 0 }, /* UF_SCHEMA */
{ 0, 0 }, /* UF_HOST */
{ 0, 0 }, /* UF_PORT */
{ 0, 9 }, /* UF_PATH */
{ 0, 0 }, /* UF_QUERY */
{ 0, 0 }, /* UF_FRAGMENT */
{ 0, 0 } } }, /* UF_USERINFO */
.rv = 0
},
{
.name = "form feed in URL",
.url = "/foo\fbar/",
.u = { .field_set = (1 << UF_PATH),
.field_data = {
{ 0, 0 }, /* UF_SCHEMA */
{ 0, 0 }, /* UF_HOST */
{ 0, 0 }, /* UF_PORT */
{ 0, 9 }, /* UF_PATH */
{ 0, 0 }, /* UF_QUERY */
{ 0, 0 }, /* UF_FRAGMENT */
{ 0, 0 } } }, /* UF_USERINFO */
.rv = 0
}
#endif
};
void test_preserve_data(void)
{
struct http_parser parser = { 0 };
char my_data[] = "application-specific data";
parser.data = my_data;
http_parser_init(&parser, HTTP_REQUEST);
/**TESTPOINT: Check results*/
zassert_equal(parser.data, my_data, "test_preserve_data error");
}
void test_parse_url(void)
{
struct http_parser_url u;
const struct url_test *test;
unsigned int elements;
unsigned int i;
int rv;
elements = sizeof(url_tests) / sizeof(url_tests[0]);
for (i = 0; i < elements; i++) {
test = &url_tests[i];
memset(&u, 0, sizeof(u));
rv = http_parser_parse_url(test->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");
}
}
}
void test_method_str(void)
{
/**TESTPOINT: Check test_method_str function*/
zassert_false(strcmp("GET", http_method_str(HTTP_GET)),
"http_method_str error");
zassert_false(strcmp("<unknown>", http_method_str(127)),
"http_method_str error");
}
void test_header_nread_value(void)
{
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;
}
#define RC_STR(rc) (rc == TC_PASS ? PASS : FAIL)
void test_http_header_fields(void)
{
int rc;
TC_START("HTTP header fields test");
/* api */
test_preserve_data();
test_parse_url();
test_method_str();
/* nread */
test_header_nread_value();
/* 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");
}
void test_main(void)
{
ztest_test_suite(test_http_header_fields_fn,
ztest_unit_test(test_http_header_fields));
ztest_run_test_suite(test_http_header_fields_fn);
}