#include #include #include #include #include #include "stellar/utils.h" #ifdef __cplusplus extern "C" { #endif #include #ifdef __cplusplus } #endif #ifndef UNUSED #define UNUSED __attribute__((unused)) #endif struct gtest_http_counter { uint8_t flag_on_message_begin; uint8_t flag_on_url; uint8_t flag_on_method; uint8_t flag_on_status; uint8_t flag_on_version; uint8_t flag_on_header_field; uint8_t flag_on_header_field_complete; uint8_t flag_on_header_value; uint8_t flag_on_header_value_complete; uint8_t flag_on_headers_complete; uint8_t flag_on_body; uint8_t flag_on_message_complete; }; struct gtest_http_parm { llhttp_t llhttp_parser; llhttp_settings_t settings; struct gtest_http_counter count; }; static int on_message_begin(llhttp_t *parser) { printf("Message begin cb\n"); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_message_begin++; return 0; } static int on_url(llhttp_t *parser, const char *at, size_t length) { printf("URI cb: %.*s\n", (int)length, at); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_url++; return 0; } static int on_method(llhttp_t *parser, const char *at, size_t length) { printf("method cb: %.*s\n", (int)length, at); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_method++; return 0; } static int on_status(llhttp_t *parser, const char *at, size_t length) { printf("status cb: %.*s\n", (int)length, at); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_status++; return 0; } static int on_version(llhttp_t *parser, const char *at, size_t length) { printf("version cb: %.*s\n", (int)length, at); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_version++; return 0; } static int on_header_field(llhttp_t *parser, const char *at, size_t length) { printf("Header field cb: %.*s\n", (int)length, at); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_header_field++; return 0; } static int on_header_field_complete(llhttp_t *parser) { printf("Header field complete cb\n"); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_header_field_complete++; return 0; } static int on_header_value(llhttp_t *parser, const char *at, size_t length) { printf("Header value cb: %.*s\n", (int)length, at); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_header_value++; return 0; } static int on_header_value_complete(llhttp_t *parser) { printf("Header value complete cb\n"); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_header_value_complete++; return 0; } static int on_headers_complete(llhttp_t *parser) { printf("All Headers complete cb\n"); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_headers_complete++; return 0; } static int on_message_complete(llhttp_t *parser) { printf("Message complete cb\n"); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_message_complete++; return 0; } static int on_body(llhttp_t *parser, const char *at, size_t length) { printf("on_body cb: %.*s\n", (int)length, at); struct gtest_http_parm *para = container_of(parser, struct gtest_http_parm, llhttp_parser); para->count.flag_on_body++; return 0; } /******************************** request test case *********************************/ static int gtest_llhttp_init(llhttp_t *parser, llhttp_type_t type, llhttp_settings_t *settings) { llhttp_settings_init(settings); settings->on_message_begin = on_message_begin; settings->on_url = on_url; settings->on_version = on_version; settings->on_status = on_status; settings->on_method = on_method; settings->on_header_field = on_header_field; settings->on_header_field_complete = on_header_field_complete; settings->on_header_value = on_header_value; settings->on_header_value_complete = on_header_value_complete; settings->on_headers_complete = on_headers_complete; settings->on_message_complete = on_message_complete; settings->on_body = on_body; llhttp_init(parser, type, settings); return 0; } TEST(HTTP_llhttp, request_base) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\n\r\n"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 1); ASSERT_EQ(para.count.flag_on_url, 1); ASSERT_EQ(para.count.flag_on_method, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_message_complete, 1); } TEST(HTTP_llhttp, request_dir_error) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings); const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\n\r\n"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request)); ASSERT_TRUE(lerr != HPE_OK); } TEST(HTTP_llhttp, request_uncompleted) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-Length:"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 1); ASSERT_EQ(para.count.flag_on_url, 1); ASSERT_EQ(para.count.flag_on_method, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_message_complete, 0); } TEST(HTTP_llhttp, request_hdr_pipeline) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example1.com\r\nContent-Length: 0\r\n\r\nGET /path/index.html HTTP/1.1\r\nHost: example2.com\r\nContent-Length: 0\r\n\r\nGET /path/index.html HTTP/1.1\r\nHost: example3.com\r\nContent-Length: 0\r\n\r\n"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 3); ASSERT_EQ(para.count.flag_on_url, 3); ASSERT_EQ(para.count.flag_on_method, 3); ASSERT_EQ(para.count.flag_on_version, 3); ASSERT_EQ(para.count.flag_on_headers_complete, 3); ASSERT_EQ(para.count.flag_on_message_complete, 3); } TEST(HTTP_llhttp, request_hdr_body_pipeline) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example1.com\r\nContent-Length: 1\r\n\r\nxGET /path/index.html HTTP/1.1\r\nHost: example2.com\r\nContent-Length: 2\r\n\r\nxxGET /path/index.html HTTP/1.1\r\nHost: example3.com\r\nContent-Length: 3\r\n\r\nxxx"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 3); ASSERT_EQ(para.count.flag_on_url, 3); ASSERT_EQ(para.count.flag_on_method, 3); ASSERT_EQ(para.count.flag_on_version, 3); ASSERT_EQ(para.count.flag_on_headers_complete, 3); ASSERT_EQ(para.count.flag_on_body, 3); ASSERT_EQ(para.count.flag_on_message_complete, 3); } TEST(HTTP_llhttp, request_body_chunked) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n5\r\nworld\r\n0\r\n\r\n"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 1); ASSERT_EQ(para.count.flag_on_url, 1); ASSERT_EQ(para.count.flag_on_method, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_body, 2); // 2 chunks ASSERT_EQ(para.count.flag_on_message_complete, 1); } TEST(HTTP_llhttp, v11_GET_no_content_length_hdr_with_body) { /* * v1.1 * if no obvious content-length header, the body will be parsed as new header, * and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error! */ struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); const char *request = "GET /path/index.html HTTP/1.1\r\nHost: example.com\r\n\r\n\r\nsome content"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request)); ASSERT_EQ(lerr, HPE_INVALID_METHOD); ASSERT_EQ(para.count.flag_on_message_begin, 2); ASSERT_EQ(para.count.flag_on_url, 1); ASSERT_EQ(para.count.flag_on_method, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_body, 0); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_message_complete, 1); } TEST(HTTP_llhttp, v10_GET_no_content_length_hdr_with_body) { /* v1.0 * if no obvious content-length header, the body will be not parsed! * then raise a HPE_CLOSED_CONNECTION error! */ struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); const char *request = "GET /path/index.html HTTP/1.0\r\nHost: example.com\r\n\r\n\r\nsome content"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request)); ASSERT_EQ(lerr, HPE_CLOSED_CONNECTION); ASSERT_EQ(para.count.flag_on_message_begin, 1); ASSERT_EQ(para.count.flag_on_url, 1); ASSERT_EQ(para.count.flag_on_method, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_body, 0); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_message_complete, 1); } TEST(HTTP_llhttp, v11_POST_no_content_length_hdr_with_body) { /* * v1.1 * if no obvious content-length header, the body will be parsed as new header, * and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error! */ struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); const char *request = "POST /path/index.html HTTP/1.1\r\nHost: example.com\r\n\r\n\r\nsome content"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request)); ASSERT_EQ(lerr, HPE_INVALID_METHOD); ASSERT_EQ(para.count.flag_on_message_begin, 2); ASSERT_EQ(para.count.flag_on_url, 1); ASSERT_EQ(para.count.flag_on_method, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_body, 0); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_message_complete, 1); } TEST(HTTP_llhttp, v11_POST_no_len_has_type_with_body) { /* * v1.1 POST * if no obvious content-length header, the body will be parsed as new header, * and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error! */ struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); const char *request = "POST /path/index.html HTTP/1.1\r\nHost: example.com\r\nContent-type: text/html\r\n\r\nsome content"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request)); ASSERT_EQ(lerr, HPE_INVALID_METHOD); ASSERT_EQ(para.count.flag_on_message_begin, 2); ASSERT_EQ(para.count.flag_on_url, 1); ASSERT_EQ(para.count.flag_on_method, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_body, 0); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_message_complete, 1); } TEST(HTTP_llhttp, v10_POST_no_len_has_type_with_body) { /* * v1.0 POST * if no obvious content-length header, the body will be parsed as new header, * and the next transaction will call on_message_begin(), then raise a HPE_INVALID_METHOD error! */ struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); const char *request = "POST /path/index.html HTTP/1.0\r\nHost: example.com\r\nContent-type: text/html\r\n\r\nsome content"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, request, strlen(request)); ASSERT_EQ(lerr, HPE_CLOSED_CONNECTION); ASSERT_EQ(para.count.flag_on_message_begin, 1); ASSERT_EQ(para.count.flag_on_url, 1); ASSERT_EQ(para.count.flag_on_method, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_body, 0); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_message_complete, 1); } /******************************** response test case *********************************/ TEST(HTTP_llhttp, response_base) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings); const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 11\r\n\r\nhello,world"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 1); ASSERT_EQ(para.count.flag_on_status, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_message_complete, 1); ASSERT_EQ(para.count.flag_on_body, 1); } TEST(HTTP_llhttp, response_dir_error) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_REQUEST, ¶.settings); const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 11\r\n\r\nhello,world"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_TRUE(lerr != HPE_OK); } TEST(HTTP_llhttp, response_uncompleted) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings); const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 11\r\n\r\nxx"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 1); ASSERT_EQ(para.count.flag_on_status, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_body, 1); ASSERT_EQ(para.count.flag_on_message_complete, 0); } TEST(HTTP_llhttp, response_hdr_pipeline) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings); const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx2\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\n"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 3); ASSERT_EQ(para.count.flag_on_status, 3); ASSERT_EQ(para.count.flag_on_version, 3); ASSERT_EQ(para.count.flag_on_headers_complete, 3); ASSERT_EQ(para.count.flag_on_body, 0); ASSERT_EQ(para.count.flag_on_message_complete, 3); } TEST(HTTP_llhttp, response_hdr_body_pipeline) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings); const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 1\r\n\r\nxHTTP/1.1 200 OK\r\nServer: nginx2\r\nContent-Type: text/html\r\nContent-Length: 2\r\n\r\nxxHTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nContent-Length: 3\r\n\r\nxxx"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 3); ASSERT_EQ(para.count.flag_on_status, 3); ASSERT_EQ(para.count.flag_on_version, 3); ASSERT_EQ(para.count.flag_on_headers_complete, 3); ASSERT_EQ(para.count.flag_on_body, 3); ASSERT_EQ(para.count.flag_on_message_complete, 3); } TEST(HTTP_llhttp, response_no_len_no_type_body) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings); const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\n\r\nxxx"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 1); ASSERT_EQ(para.count.flag_on_status, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_body, 1); ASSERT_EQ(para.count.flag_on_message_complete, 0); // no completed } TEST(HTTP_llhttp, response_no_len_has_type_body) { struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings); const char *response = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\n\r\nxxx"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 1); ASSERT_EQ(para.count.flag_on_status, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_body, 1); ASSERT_EQ(para.count.flag_on_message_complete, 0); // no completed } TEST(HTTP_llhttp, response_no_len_no_type_pipeline) { /* * if no obvious content-length header, the body will be parsed until connection cloesd! * */ struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings); const char *response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx2\r\n\r\nHTTP/1.1 200 OK\r\nServer: nginx2\r\n\r\n"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 1); ASSERT_EQ(para.count.flag_on_status, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_body, 1); ASSERT_EQ(para.count.flag_on_message_complete, 0); } TEST(HTTP_llhttp, response_no_len_no_type_and_reset) { /* * if no obvious content-length header, the body will be parsed until connection cloesd! * */ struct gtest_http_parm para = {}; gtest_llhttp_init(¶.llhttp_parser, HTTP_RESPONSE, ¶.settings); const char *response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\n"; enum llhttp_errno lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_EQ(lerr, HPE_OK); lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_EQ(lerr, HPE_OK); lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_EQ(lerr, HPE_OK); llhttp_reset(¶.llhttp_parser); memset(¶.count, 0, sizeof(para.count)); response = "HTTP/1.1 200 OK\r\nServer: nginx1\r\n\r\n"; lerr = llhttp_execute(¶.llhttp_parser, response, strlen(response)); ASSERT_EQ(lerr, HPE_OK); ASSERT_EQ(para.count.flag_on_message_begin, 1); ASSERT_EQ(para.count.flag_on_status, 1); ASSERT_EQ(para.count.flag_on_version, 1); ASSERT_EQ(para.count.flag_on_headers_complete, 1); ASSERT_EQ(para.count.flag_on_body, 0); ASSERT_EQ(para.count.flag_on_message_complete, 0); // no completed if no content-length filed } int main(int argc, char const *argv[]) { ::testing::InitGoogleTest(&argc, (char **)argv); return RUN_ALL_TESTS(); }