diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib-http/test-http-header-parser.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/src/lib-http/test-http-header-parser.c b/src/lib-http/test-http-header-parser.c new file mode 100644 index 0000000..0550039 --- /dev/null +++ b/src/lib-http/test-http-header-parser.c @@ -0,0 +1,381 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str-sanitize.h" +#include "istream.h" +#include "test-common.h" +#include "http-response.h" +#include "http-header-parser.h" + +#include <time.h> + +struct http_header_parse_result { + const char *name; + const char *value; +}; + +struct http_header_parse_test { + const char *header; + struct http_header_limits limits; + enum http_header_parse_flags flags; + const struct http_header_parse_result *fields; +}; + +/* Valid header tests */ + +static const struct http_header_parse_result valid_header_parse_result1[] = { + { "Date", "Sat, 06 Oct 2012 16:01:44 GMT" }, + { "Server", "Apache/2.2.16 (Debian)" }, + { "Last-Modified", "Mon, 30 Jul 2012 11:09:28 GMT" }, + { "Etag", "\"3d24677-3261-4c60a1863aa00\"" }, + { "Accept-Ranges", "bytes" }, + { "Vary", "Accept-Encoding" }, + { "Content-Encoding", "gzip" }, + { "Content-Length", "4092" }, + { "Keep-Alive", "timeout=15, max=100" }, + { "Connection", "Keep-Alive" }, + { "Content-Type", "text/html" }, + { NULL, NULL } +}; + +static const struct http_header_parse_result valid_header_parse_result2[] = { + { "Host", "p5-lrqzb4yavu4l7nagydw-428649-i2-v6exp3-ds.metric.example.com" }, + { "User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0)" }, + { "Accept", "image/png,image/*;q=0.8,*/*;q=0.5" }, + { "Accept-Language", "en-us,en;q=0.5" }, + { "Accept-Encoding", "gzip, deflate" }, + { "DNT", "1" }, + { "Connection", "keep-alive" }, + { "Referer", "http://www.example.nl/" }, + { NULL, NULL } +}; + +static const struct http_header_parse_result valid_header_parse_result3[] = { + { "Date", "Sat, 06 Oct 2012 17:12:37 GMT" }, + { "Server", "Apache/2.2.16 (Debian) PHP/5.3.3-7+squeeze14 with" + " Suhosin-Patch proxy_html/3.0.1 mod_python/3.3.1 Python/2.6.6" + " mod_ssl/2.2.16 OpenSSL/0.9.8o mod_perl/2.0.4 Perl/v5.10.1" }, + { "WWW-Authenticate", "Basic realm=\"Munin\"" }, + { "Vary", "Accept-Encoding" }, + { "Content-Encoding", "gzip" }, + { "Content-Length", "445" }, + { "Keep-Alive", "timeout=15, max=98" }, + { "Connection", "Keep-Alive" }, + { "Content-Type", "text/html; charset=iso-8859-1" }, + { NULL, NULL } +}; + +static const struct http_header_parse_result valid_header_parse_result4[] = { + { "Age", "58" }, + { "Date", "Sun, 04 Aug 2013 09:33:09 GMT" }, + { "Expires", "Sun, 04 Aug 2013 09:34:08 GMT" }, + { "Cache-Control", "max-age=60" }, + { "Content-Length", "17336" }, + { "Connection", "Keep-Alive" }, + { "Via", "NS-CACHE-9.3" }, + { "Server", "Apache" }, + { "Vary", "Host" }, + { "Last-Modified", "Sun, 04 Aug 2013 09:33:07 GMT" }, + { "Content-Type", "text/html; charset=utf-8" }, + { "Content-Encoding", "gzip" }, + { NULL, NULL } +}; + +static const struct http_header_parse_result valid_header_parse_result5[] = { + { NULL, NULL } +}; + +static const struct http_header_parse_result valid_header_parse_result6[] = { + { "X-Frop", "This text\x80 contains obs-text\x81 characters" }, + { NULL, NULL } +}; + +static const struct http_header_parse_result valid_header_parse_result7[] = { + { "X-Frop", "This text contains invalid characters" }, + { NULL, NULL } +}; + +static const struct http_header_parse_test valid_header_parse_tests[] = { + { .header = + "Date: Sat, 06 Oct 2012 16:01:44 GMT\r\n" + "Server: Apache/2.2.16 (Debian)\r\n" + "Last-Modified: Mon, 30 Jul 2012 11:09:28 GMT\r\n" + "Etag: \"3d24677-3261-4c60a1863aa00\"\r\n" + "Accept-Ranges: bytes\r\n" + "Vary: Accept-Encoding\r\n" + "Content-Encoding: gzip\r\n" + "Content-Length: 4092\r\n" + "Keep-Alive: timeout=15, max=100\r\n" + "Connection: Keep-Alive\r\n" + "Content-Type: text/html\r\n" + "\r\n", + .fields = valid_header_parse_result1 + },{ + .header = + "Host: p5-lrqzb4yavu4l7nagydw-428649-i2-v6exp3-ds.metric.example.com\n" + "User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0)\n" + "Accept:\t\timage/png,image/*;q=0.8,*/*;q=0.5\n" + "Accept-Language:\ten-us,en;q=0.5\n" + "Accept-Encoding: \t\tgzip, deflate\n" + "DNT: 1\n" + "Connection: \t\tkeep-alive\n" + "Referer: http://www.example.nl/\n" + "\n", + .fields = valid_header_parse_result2 + },{ + .header = + "Date: Sat, 06 Oct 2012 17:12:37 GMT\r\n" + "Server: Apache/2.2.16 (Debian) PHP/5.3.3-7+squeeze14 with\r\n" + " Suhosin-Patch proxy_html/3.0.1 mod_python/3.3.1 Python/2.6.6\r\n" + " mod_ssl/2.2.16 OpenSSL/0.9.8o mod_perl/2.0.4 Perl/v5.10.1\r\n" + "WWW-Authenticate: Basic realm=\"Munin\"\r\n" + "Vary: Accept-Encoding\r\n" + "Content-Encoding: gzip\r\n" + "Content-Length: 445\r\n" + "Keep-Alive: timeout=15, max=98\r\n" + "Connection: Keep-Alive\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + "\r\n", + .fields = valid_header_parse_result3 + },{ + .header = + "Age: 58 \r\n" + "Date: Sun, 04 Aug 2013 09:33:09 GMT\r\n" + "Expires: Sun, 04 Aug 2013 09:34:08 GMT\r\n" + "Cache-Control: max-age=60 \r\n" + "Content-Length: 17336 \r\n" + "Connection: Keep-Alive\r\n" + "Via: NS-CACHE-9.3\r\n" + "Server: Apache\r\n" + "Vary: Host\r\n" + "Last-Modified: Sun, 04 Aug 2013 09:33:07 GMT\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "Content-Encoding: gzip\r\n" + "\r\n", + .fields = valid_header_parse_result4, + .limits = { + .max_size = 340, + .max_field_size = 46, + .max_fields = 12 + } + },{ + .header = + "\r\n", + .fields = valid_header_parse_result5 + },{ + .header = + "X-Frop: This text\x80 contains obs-text\x81 characters\r\n" + "\r\n", + .fields = valid_header_parse_result6 + },{ + .header = + "X-Frop: This text\x01 contains invalid\x7f characters\r\n" + "\r\n", + .fields = valid_header_parse_result7 + } +}; + +static const unsigned int valid_header_parse_test_count = N_ELEMENTS(valid_header_parse_tests); + +static void test_http_header_parse_valid(void) +{ + unsigned int i; + + for (i = 0; i < valid_header_parse_test_count; i++) T_BEGIN { + struct istream *input; + struct http_header_parser *parser; + const struct http_header_limits *limits; + const char *header, *field_name, *error = NULL; + const unsigned char *field_data; + size_t field_size; + int ret; + unsigned int j, pos, header_len; + + header = valid_header_parse_tests[i].header; + header_len = strlen(header); + limits = &valid_header_parse_tests[i].limits; + input = test_istream_create_data(header, header_len); + parser = http_header_parser_init(input, limits, + valid_header_parse_tests[i].flags); + + test_begin(t_strdup_printf("http header valid [%d]", i)); + + j = 0; pos = 0; test_istream_set_size(input, 0); + while ((ret=http_header_parse_next_field + (parser, &field_name, &field_data, &field_size, &error)) >= 0) { + const struct http_header_parse_result *result; + const char *field_value; + + if (ret == 0) { + if (pos == header_len) + break; + test_istream_set_size(input, ++pos); + continue; + } + + if (field_name == NULL) break; + + result = &valid_header_parse_tests[i].fields[j]; + field_value = t_strndup(field_data, field_size); + + if (result->name == NULL) { + test_out_reason("valid", FALSE, t_strdup_printf + ("%s: %s", field_name, str_sanitize(field_value, 100))); + break; + } + + test_out_reason("valid", + strcmp(result->name, field_name) == 0 && + strcmp(result->value, field_value) == 0, + t_strdup_printf("%s: %s", field_name, + str_sanitize(field_value, 100))); + j++; + } + + test_out_reason("parse success", ret > 0, error); + test_end(); + i_stream_unref(&input); + http_header_parser_deinit(&parser); + } T_END; +} + +static const struct http_header_parse_test invalid_header_parse_tests[] = { + { + .header = + "Date: Sat, 06 Oct 2012 16:01:44 GMT\r\n" + "Server : Apache/2.2.16 (Debian)\r\n" + "Last-Modified: Mon, 30 Jul 2012 11:09:28 GMT\r\n" + "\r\n" + },{ + .header = + "Date: Sat, 06 Oct 2012 17:18:22 GMT\r\n" + "Server: Apache/2.2.3 (CentOS)\r\n" + "X Powered By: PHP/5.3.6\r\n" + "\r\n" + },{ + .header = + "Host: www.example.com\n\r" + "Accept: image/png,image/*;q=0.8,*/*;q=0.5\n\r" + "Accept-Language: en-us,en;q=0.5\n\r" + "Accept-Encoding: gzip, deflate\n\r" + "\n\r" + },{ + .header = + "Host: p5-lrqzb4yavu4l7nagydw-428649-i2-v6exp3-ds.metric.example.com\n" + "User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0)\n" + "Accept:\t\timage/png,image/*;q=0.8,*/\1;q=0.5\n" + "\n", + .flags = HTTP_HEADER_PARSE_FLAG_STRICT + },{ + .header = + "Date: Sat, 06 Oct 2012 17:18:22 GMT\r\n" + "Server: Apache/2.2.3\177 (CentOS)\r\n" + "\r\n", + .flags = HTTP_HEADER_PARSE_FLAG_STRICT + },{ + .header = + "Date: Sat, 06 Oct 2012 17:12:37 GMT\r\n" + "Server: Apache/2.2.16 (Debian) PHP/5.3.3-7+squeeze14 with\r\n" + "Suhosin-Patch proxy_html/3.0.1 mod_python/3.3.1 Python/2.6.6\r\n" + "mod_ssl/2.2.16 OpenSSL/0.9.8o mod_perl/2.0.4 Perl/v5.10.1\r\n" + "\r\n" + },{ + .header = + "Date: Sat, 06 Oct 2012 17:12:37 GMT\r\n" + },{ + .header = + "Age: 58 \r\n" + "Date: Sun, 04 Aug 2013 09:33:09 GMT\r\n" + "Expires: Sun, 04 Aug 2013 09:34:08 GMT\r\n" + "Cache-Control: max-age=60 \r\n" + "Content-Length: 17336 \r\n" + "Connection: Keep-Alive\r\n" + "Via: NS-CACHE-9.3\r\n" + "Server: Apache\r\n" + "Vary: Host\r\n" + "Last-Modified: Sun, 04 Aug 2013 09:33:07 GMT\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "Content-Encoding: gzip\r\n" + "\r\n", + .limits = { .max_size = 339 } + },{ + .header = + "Age: 58 \r\n" + "Date: Sun, 04 Aug 2013 09:33:09 GMT\r\n" + "Expires: Sun, 04 Aug 2013 09:34:08 GMT\r\n" + "Cache-Control: max-age=60 \r\n" + "Content-Length: 17336 \r\n" + "Connection: Keep-Alive\r\n" + "Via: NS-CACHE-9.3\r\n" + "Server: Apache\r\n" + "Vary: Host\r\n" + "Last-Modified: Sun, 04 Aug 2013 09:33:07 GMT\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "Content-Encoding: gzip\r\n" + "\r\n", + .fields = valid_header_parse_result4, + .limits = { .max_field_size = 45 } + },{ + .header = + "Age: 58 \r\n" + "Date: Sun, 04 Aug 2013 09:33:09 GMT\r\n" + "Expires: Sun, 04 Aug 2013 09:34:08 GMT\r\n" + "Cache-Control: max-age=60 \r\n" + "Content-Length: 17336 \r\n" + "Connection: Keep-Alive\r\n" + "Via: NS-CACHE-9.3\r\n" + "Server: Apache\r\n" + "Vary: Host\r\n" + "Last-Modified: Sun, 04 Aug 2013 09:33:07 GMT\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "Content-Encoding: gzip\r\n" + "\r\n", + .fields = valid_header_parse_result4, + .limits = { .max_fields = 11 } + } +}; + +static const unsigned int invalid_header_parse_test_count = N_ELEMENTS(invalid_header_parse_tests); + +static void test_http_header_parse_invalid(void) +{ + unsigned int i; + + for (i = 0; i < invalid_header_parse_test_count; i++) T_BEGIN { + struct istream *input; + struct http_header_parser *parser; + const struct http_header_limits *limits; + const char *header, *field_name, *error = NULL; + const unsigned char *field_data; + size_t field_size; + int ret; + + header = invalid_header_parse_tests[i].header; + limits = &invalid_header_parse_tests[i].limits; + input = i_stream_create_from_data(header, strlen(header)); + parser = http_header_parser_init(input, limits, + invalid_header_parse_tests[i].flags); + + test_begin(t_strdup_printf("http header invalid [%d]", i)); + + while ((ret=http_header_parse_next_field + (parser, &field_name, &field_data, &field_size, &error)) > 0) { + if (field_name == NULL) break; + } + + test_out_reason("parse failure", ret < 0, error); + test_end(); + i_stream_unref(&input); + http_header_parser_deinit(&parser); + } T_END; +} + +int main(void) +{ + static void (*const test_functions[])(void) = { + test_http_header_parse_valid, + test_http_header_parse_invalid, + NULL + }; + return test_run(test_functions); +} |