From 1faa520084b901b15d83d3d68baaee2535051def Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Sat, 12 Oct 2013 10:52:35 +0300 Subject: [PATCH] lib-http: Added more tests for the http_request_parser. --- src/lib-http/test-http-request-parser.c | 249 ++++++++++++++++++++---- 1 file changed, 209 insertions(+), 40 deletions(-) diff --git a/src/lib-http/test-http-request-parser.c b/src/lib-http/test-http-request-parser.c index c213428211..804e3a872d 100644 --- a/src/lib-http/test-http-request-parser.c +++ b/src/lib-http/test-http-request-parser.c @@ -12,20 +12,27 @@ #include -struct http_request_parse_test { +/* + * Test: valid requests + */ + +struct http_request_valid_parse_test { const char *request; const char *method; const char *target_raw; - struct http_request_target target; + struct { + enum http_request_target_format format; + struct http_url url; + } target; unsigned char version_major; unsigned char version_minor; uoff_t content_length; const char *payload; + bool connection_close; + bool expect_100_continue; }; -/* Valid header tests */ - -static const struct http_request_parse_test +static const struct http_request_valid_parse_test valid_request_parse_tests[] = { { .request = "GET / HTTP/1.1\r\n" @@ -34,17 +41,20 @@ valid_request_parse_tests[] = { .method = "GET", .target_raw = "/", .target = { - .format = HTTP_REQUEST_TARGET_FORMAT_ORIGIN + .format = HTTP_REQUEST_TARGET_FORMAT_ORIGIN, + .url = { .host_name = "example.com" } }, .version_major = 1, .version_minor = 1, },{ .request = "OPTIONS * HTTP/1.0\r\n" "Host: example.com\r\n" + "Connection: Keep-Alive\r\n" "\r\n", .method = "OPTIONS", .target_raw = "*", .target = { - .format = HTTP_REQUEST_TARGET_FORMAT_ASTERISK + .format = HTTP_REQUEST_TARGET_FORMAT_ASTERISK, + .url = { .host_name = "example.com" } }, .version_major = 1, .version_minor = 0, },{ .request = @@ -54,7 +64,8 @@ valid_request_parse_tests[] = { .method = "CONNECT", .target_raw = "example.com:443", .target = { - .format = HTTP_REQUEST_TARGET_FORMAT_AUTHORITY + .format = HTTP_REQUEST_TARGET_FORMAT_AUTHORITY, + .url = { .host_name = "example.com", .have_port = TRUE, .port = 443 } }, .version_major = 1, .version_minor = 2, },{ .request = @@ -64,7 +75,12 @@ valid_request_parse_tests[] = { .method = "GET", .target_raw = "https://www.example.com:443", .target = { - .format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE + .format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, + .url = { + .host_name = "www.example.com", + .have_port = TRUE, .port = 443, + .have_ssl = TRUE + } }, .version_major = 1, .version_minor = 1, },{ .request = @@ -77,9 +93,48 @@ valid_request_parse_tests[] = { .target_raw = "http://api.example.com:8080/commit?user=dirk", .target = { .format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, + .url = { .host_name = "api.example.com", .have_port = TRUE, .port = 8080 } }, .version_major = 1, .version_minor = 1, .payload = "Content!\r\n" + },{ .request = + "GET http://www.example.com/index.php?seq=1 HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Connection: close\r\n" + "\r\n", + .method = "GET", + .target_raw = "http://www.example.com/index.php?seq=1", + .target = { + .format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, + .url = { .host_name = "www.example.com" } + }, + .version_major = 1, .version_minor = 1, + .connection_close = TRUE + },{ .request = + "GET http://www.example.com/index.html HTTP/1.0\r\n" + "Host: www.example.com\r\n" + "\r\n", + .method = "GET", + .target_raw = "http://www.example.com/index.html", + .target = { + .format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, + .url = { .host_name = "www.example.com" } + }, + .version_major = 1, .version_minor = 0, + .connection_close = TRUE + },{ .request = + "GET http://www.example.com/index.html HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Expect: 100-continue\r\n" + "\r\n", + .method = "GET", + .target_raw = "http://www.example.com/index.html", + .target = { + .format = HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE, + .url = { .host_name = "www.example.com" } + }, + .version_major = 1, .version_minor = 1, + .expect_100_continue = TRUE } }; @@ -110,7 +165,7 @@ static void test_http_request_parse_valid(void) for (i = 0; i < valid_request_parse_test_count; i++) T_BEGIN { struct istream *input; struct ostream *output; - const struct http_request_parse_test *test; + const struct http_request_valid_parse_test *test; struct http_request_parser *parser; struct http_request request; enum http_request_parse_error error_code; @@ -153,26 +208,65 @@ static void test_http_request_parse_valid(void) if (ret == 0) { /* verify last request only */ if (request.method == NULL || test->method == NULL) { - test_out(t_strdup_printf("request->method = %s", test->method), + test_out(t_strdup_printf("request->method = %s", request.method), request.method == test->method); } else { - test_out(t_strdup_printf("request->method = %s", test->method), + test_out(t_strdup_printf("request->method = %s", request.method), strcmp(request.method, test->method) == 0); } + if (request.target_raw == NULL || test->target_raw == NULL) { - test_out(t_strdup_printf("request->target = %s", test->target_raw), + test_out(t_strdup_printf + ("request->target_raw = %s", request.target_raw), request.target_raw == test->target_raw); } else { - test_out(t_strdup_printf("request->target = %s", test->target_raw), + test_out(t_strdup_printf + ("request->target_raw = %s", request.target_raw), strcmp(request.target_raw, test->target_raw) == 0); } + if (request.target.url == NULL) { + test_out("request->target.url = (null)", + test->target.url.host_name == NULL && !test->target.url.have_port); + } else { + if (request.target.url->host_name == NULL || + test->target.url.host_name == NULL) { + test_out(t_strdup_printf("request->target.url->host_name = %s", + request.target.url->host_name), + request.target.url->host_name == test->target.url.host_name); + } else { + test_out(t_strdup_printf("request->target.url->host_name = %s", + request.target.url->host_name), + strcmp(request.target.url->host_name, + test->target.url.host_name) == 0); + } + if (!request.target.url->have_port) { + test_out("request->target.url->port = (unspecified)", + request.target.url->have_port == test->target.url.have_port); + } else { + test_out(t_strdup_printf + ("request->target.url->port = %u", request.target.url->port), + request.target.url->have_port == test->target.url.have_port && + request.target.url->port == test->target.url.port); + } + test_out(t_strdup_printf("request->target.url->have_ssl = %s", + (request.target.url->have_ssl ? "yes" : "no")), + request.target.url->have_ssl == test->target.url.have_ssl); + } test_out(t_strdup_printf("request->target_format = %s", - _request_target_format(test->target.format)), + _request_target_format(request.target.format)), request.target.format == test->target.format); - test_out(t_strdup_printf("request->version = %d.%d", - test->version_major, test->version_minor), + + test_out(t_strdup_printf("request->version = %u.%u", + request.version_major, request.version_minor), request.version_major == test->version_major && request.version_minor == test->version_minor); + test_out(t_strdup_printf("request->connection_close = %s", + (request.connection_close ? "yes" : "no")), + request.connection_close == test->connection_close); + test_out(t_strdup_printf("request->expect_100_continue = %s", + (request.expect_100_continue ? "yes" : "no")), + request.expect_100_continue == test->expect_100_continue); + if (payload == NULL || test->payload == NULL) { test_out(t_strdup_printf("request->payload = %s", str_sanitize(payload, 80)), @@ -190,25 +284,71 @@ static void test_http_request_parse_valid(void) buffer_free(&payload_buffer); } -static const char *invalid_request_parse_tests[] = { - "GET: / HTTP/1.1\r\n" - "Host: example.com\r\n" - "\r\n", - "GET % HTTP/1.1\r\n" - "Host: example.com\r\n" - "\r\n", - "GET /frop\" HTTP/1.1\r\n" - "Host: example.com\r\n" - "\r\n", - "GET / HTCPCP/1.0\r\n" - "Host: example.com\r\n" - "\r\n", - "GET / HTTP/1.0.1\r\n" - "Host: example.com\r\n" - "\r\n", - "GET / HTTP/1.1\r\n" - "Host: \"example.com\r\n" - "\r\n", +/* + * Test: invalid requests + */ + +struct http_request_invalid_parse_test { + const char *request; + enum http_request_parse_error error_code; +}; + +static struct http_request_invalid_parse_test +invalid_request_parse_tests[] = { + { .request = + "GET: / HTTP/1.1\r\n" + "Host: example.com\r\n" + "\r\n", + .error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST + },{ .request = + "GET % HTTP/1.1\r\n" + "Host: example.com\r\n" + "\r\n", + .error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST + },{ .request = + "GET /frop\" HTTP/1.1\r\n" + "Host: example.com\r\n" + "\r\n", + .error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST + },{ .request = + "GET / HTCPCP/1.0\r\n" + "Host: example.com\r\n" + "\r\n", + .error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST + },{ .request = + "GET / HTTP/1.0.1\r\n" + "Host: example.com\r\n" + "\r\n", + .error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST + },{ .request = + "GET / HTTP/1.1\r\n" + "Host: \"example.com\r\n" + "\r\n", + .error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST + },{ .request = + "GET / HTTP/1.1\r\n" + "\r\n", + .error_code = HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST + },{ .request = + "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Transfer-Encoding: gzip\r\n" + "\r\n", + .error_code = HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST + },{ .request = + "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Expect: payment\r\n" + "\r\n", + .error_code = HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED + },{ .request = + "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Transfer-Encoding: cuneiform, chunked\r\n" + "\r\n", + .error_code = HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED + } + // FIXME: test request limits }; static unsigned char invalid_request_with_nuls[] = @@ -220,8 +360,35 @@ static unsigned char invalid_request_with_nuls[] = unsigned int invalid_request_parse_test_count = N_ELEMENTS(invalid_request_parse_tests); +static const char * +_request_parse_error(enum http_request_parse_error error) +{ + switch (error) { + case HTTP_REQUEST_PARSE_ERROR_NONE: + return "none?!"; + case HTTP_REQUEST_PARSE_ERROR_BROKEN_STREAM: + return "broken stream"; + case HTTP_REQUEST_PARSE_ERROR_BROKEN_REQUEST: + return "broken request"; + case HTTP_REQUEST_PARSE_ERROR_BAD_REQUEST: + return "bad request"; + case HTTP_REQUEST_PARSE_ERROR_NOT_IMPLEMENTED: + return "not implemented"; + case HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED: + return "expectation failed"; + case HTTP_REQUEST_PARSE_ERROR_METHOD_TOO_LONG: + return "method too long"; + case HTTP_REQUEST_PARSE_ERROR_TARGET_TOO_LONG: + return "target too long"; + case HTTP_REQUEST_PARSE_ERROR_PAYLOAD_TOO_LARGE: + return "payload too large"; + } + return t_strdup_printf("<>", error); +} + static void test_http_request_parse_invalid(void) { + const struct http_request_invalid_parse_test *test; struct http_request_parser *parser; struct http_request request; enum http_request_parse_error error_code; @@ -231,10 +398,8 @@ static void test_http_request_parse_invalid(void) unsigned int i; for (i = 0; i < invalid_request_parse_test_count; i++) T_BEGIN { - const char *test; - - test = invalid_request_parse_tests[i]; - request_text = test; + test = &invalid_request_parse_tests[i]; + request_text = test->request; input = i_stream_create_from_data(request_text, strlen(request_text)); parser = http_request_parser_init(input, NULL); @@ -244,6 +409,10 @@ static void test_http_request_parse_invalid(void) (parser, FALSE, &request, &error_code, &error)) > 0); test_out_reason("parse failure", ret < 0, error); + if (ret < 0) { + test_out(t_strdup_printf("parse error code = %s", + _request_parse_error(error_code)), error_code == test->error_code); + } test_end(); http_request_parser_deinit(&parser); } T_END; -- 2.47.3