diff --git a/build/Makefile b/build/Makefile index a83b7aad..a17a7a18 100644 --- a/build/Makefile +++ b/build/Makefile @@ -67,11 +67,11 @@ all: ../mongoose.c: ../mongoose.h Makefile $(SOURCES) cat $(SOURCES) | sed '/#include "internal.h"/d' > $@ -unix_unit_test: $(LUA_SOURCES) Makefile ../test/unit_test.c - $(CC) ../test/unit_test.c lua_5.2.1.c $(CFLAGS) -g -O0 -o t && ./t +unix_unit_test: $(LUA_SOURCES) Makefile ./test/unit_test.c + $(CC) ./test/unit_test.c lua_5.2.1.c $(CFLAGS) -g -O0 -o t && ./t -core_unit_test: Makefile ../test/unit_test.c - $(CC) ../test/unit_test.c $(CFLAGS) -g -O0 -o t && ./t +core_unit_test: Makefile ./test/unit_test.c + $(CC) ./test/unit_test.c $(CFLAGS) -g -O0 -o t && ./t # Make sure that the compiler flags come last in the compilation string. # If not so, this can break some on some Linux distros which use @@ -102,7 +102,7 @@ $(PROG).dll: $(TINY_SOURCES) Makefile # This is broken now due to SSL exclusion windows_unit_test.exe: $(LUA_SOURCES) Makefile - $(CL) ../test/unit_test.c lua_5.2.1.c -DLUA_COMPAT_ALL \ + $(CL) ./test/unit_test.c lua_5.2.1.c -DLUA_COMPAT_ALL \ /link /libpath:$(MSVC)/lib advapi32.lib /out:$@ ./$@ @@ -128,7 +128,7 @@ macos: $(LUA_SOURCES) #rm -rf dmg tests: - perl ../test/test.pl $(TEST) + perl ./test/test.pl $(TEST) tarball: clean rm -rf mongoose-$(VERSION) diff --git a/test/.leading.dot.txt b/build/test/.leading.dot.txt similarity index 100% rename from test/.leading.dot.txt rename to build/test/.leading.dot.txt diff --git a/test/all_build_flags.pl b/build/test/all_build_flags.pl similarity index 100% rename from test/all_build_flags.pl rename to build/test/all_build_flags.pl diff --git a/test/bad.cgi b/build/test/bad.cgi similarity index 100% rename from test/bad.cgi rename to build/test/bad.cgi diff --git a/test/bad2.cgi b/build/test/bad2.cgi similarity index 100% rename from test/bad2.cgi rename to build/test/bad2.cgi diff --git a/test/dir with spaces/hello.cgi b/build/test/dir with spaces/hello.cgi similarity index 100% rename from test/dir with spaces/hello.cgi rename to build/test/dir with spaces/hello.cgi diff --git a/test/dir with spaces/桌面/index.html b/build/test/dir with spaces/桌面/index.html similarity index 100% rename from test/dir with spaces/桌面/index.html rename to build/test/dir with spaces/桌面/index.html diff --git a/test/embed.c b/build/test/embed.c similarity index 100% rename from test/embed.c rename to build/test/embed.c diff --git a/test/env.cgi b/build/test/env.cgi similarity index 100% rename from test/env.cgi rename to build/test/env.cgi diff --git a/test/exploit.pl b/build/test/exploit.pl similarity index 100% rename from test/exploit.pl rename to build/test/exploit.pl diff --git a/test/hello.cgi b/build/test/hello.cgi similarity index 100% rename from test/hello.cgi rename to build/test/hello.cgi diff --git a/test/hello.txt b/build/test/hello.txt similarity index 100% rename from test/hello.txt rename to build/test/hello.txt diff --git a/test/page.lp b/build/test/page.lp similarity index 100% rename from test/page.lp rename to build/test/page.lp diff --git a/test/passfile b/build/test/passfile similarity index 100% rename from test/passfile rename to build/test/passfile diff --git a/test/sh.cgi b/build/test/sh.cgi similarity index 100% rename from test/sh.cgi rename to build/test/sh.cgi diff --git a/build/test/ssi1.shtml b/build/test/ssi1.shtml new file mode 100644 index 00000000..9bffef9c --- /dev/null +++ b/build/test/ssi1.shtml @@ -0,0 +1,5 @@ +
+ssi_begin + +ssi_end +diff --git a/test/ssi2.shtml b/build/test/ssi2.shtml similarity index 100% rename from test/ssi2.shtml rename to build/test/ssi2.shtml diff --git a/test/ssi3.shtml b/build/test/ssi3.shtml similarity index 100% rename from test/ssi3.shtml rename to build/test/ssi3.shtml diff --git a/test/ssi4.shtml b/build/test/ssi4.shtml similarity index 100% rename from test/ssi4.shtml rename to build/test/ssi4.shtml diff --git a/test/ssi5.shtml b/build/test/ssi5.shtml similarity index 100% rename from test/ssi5.shtml rename to build/test/ssi5.shtml diff --git a/test/ssi6.shtml b/build/test/ssi6.shtml similarity index 100% rename from test/ssi6.shtml rename to build/test/ssi6.shtml diff --git a/test/ssi7.shtml b/build/test/ssi7.shtml similarity index 100% rename from test/ssi7.shtml rename to build/test/ssi7.shtml diff --git a/test/ssi8.shtml b/build/test/ssi8.shtml similarity index 100% rename from test/ssi8.shtml rename to build/test/ssi8.shtml diff --git a/build/test/ssi9.shtml b/build/test/ssi9.shtml new file mode 100644 index 00000000..4e837782 --- /dev/null +++ b/build/test/ssi9.shtml @@ -0,0 +1,3 @@ +ssi_begin + +ssi_end diff --git a/test/test.pl b/build/test/test.pl similarity index 99% rename from test/test.pl rename to build/test/test.pl index de4a8fca..23e8fa28 100644 --- a/test/test.pl +++ b/build/test/test.pl @@ -18,8 +18,8 @@ my $num_requests; my $dir_separator = on_windows() ? '\\' : '/'; my $copy_cmd = on_windows() ? 'copy' : 'cp'; my $test_dir_uri = "test_dir"; -my $root = '../test'; -my $abs_root = Cwd::abs_path(dirname($0) . $dir_separator . $root); +my $root = './test'; +my $abs_root = Cwd::abs_path(dirname($0) . $dir_separator); my $test_dir = $abs_root . $dir_separator. $test_dir_uri; #print "$test_dir\n"; exit 0; my $config = 'mongoose.conf'; diff --git a/test/timeout.cgi b/build/test/timeout.cgi similarity index 100% rename from test/timeout.cgi rename to build/test/timeout.cgi diff --git a/build/test/unit_test.c b/build/test/unit_test.c new file mode 100644 index 00000000..c533555f --- /dev/null +++ b/build/test/unit_test.c @@ -0,0 +1,411 @@ +// Unit test for the mongoose web server. + +#define USE_WEBSOCKET + +#ifndef _WIN32 +#define USE_IPV6 +#include
-ssi_begin - -ssi_end -diff --git a/test/ssi9.shtml b/test/ssi9.shtml deleted file mode 100644 index 67aed331..00000000 --- a/test/ssi9.shtml +++ /dev/null @@ -1,3 +0,0 @@ -ssi_begin - -ssi_end diff --git a/test/unit_test.c b/test/unit_test.c deleted file mode 100644 index 983c1cdd..00000000 --- a/test/unit_test.c +++ /dev/null @@ -1,697 +0,0 @@ -// Unit test for the mongoose web server. - -#define USE_WEBSOCKET -#define USE_LUA - -#ifndef _WIN32 -#define USE_IPV6 -#endif - -// USE_* definitions must be made before #include "mongoose.c" ! -#include "src/core.c" - -#define FAIL(str, line) do { \ - printf("Fail on line %d: [%s]\n", line, str); \ - return str; \ -} while (0) - -#define ASSERT(expr) do { \ - static_num_tests++; \ - if (!(expr)) FAIL(#expr, __LINE__); \ -} while (0) - -#define RUN_TEST(test) do { const char *msg = test(); \ - if (msg) return msg; } while (0) - -#define HTTP_PORT "45772" -#define LISTENING_ADDR "127.0.0.1:" HTTP_PORT - -static int static_num_tests = 0; -//static const char *fetch_data = "hello world!\n"; -//static const char *upload_ok_message = "upload successful"; - -static const char *test_parse_http_message() { - struct mg_connection ri; - char req1[] = "GET / HTTP/1.1\r\n\r\n"; - char req2[] = "BLAH / HTTP/1.1\r\n\r\n"; - char req3[] = "GET / HTTP/1.1\r\nBah\r\n"; - char req4[] = "GET / HTTP/1.1\r\nA: foo bar\r\nB: bar\r\nbaz\r\n\r\n"; - char req5[] = "GET / HTTP/1.1\r\n\r\n"; - char req6[] = "G"; - char req7[] = " blah "; - char req8[] = " HTTP/1.1 200 OK \n\n"; - char req9[] = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n"; - - ASSERT(get_request_len("\r\n", 3) == -1); - ASSERT(get_request_len("\r\n", 2) == 0); - ASSERT(get_request_len("GET", 3) == 0); - ASSERT(get_request_len("\n\n", 2) == 2); - ASSERT(get_request_len("\n\r\n", 3) == 3); - ASSERT(get_request_len("\xdd\xdd", 2) == 0); - ASSERT(get_request_len("\xdd\x03", 2) == -1); - ASSERT(get_request_len(req3, sizeof(req3) - 1) == 0); - ASSERT(get_request_len(req6, sizeof(req6) - 1) == 0); - ASSERT(get_request_len(req7, sizeof(req7) - 1) == 0); - - ASSERT(parse_http_message(req9, sizeof(req9) - 1, &ri) == sizeof(req9) - 1); - ASSERT(ri.num_headers == 1); - - ASSERT(parse_http_message(req1, sizeof(req1) - 1, &ri) == sizeof(req1) - 1); - ASSERT(strcmp(ri.http_version, "1.1") == 0); - ASSERT(ri.num_headers == 0); - - ASSERT(parse_http_message(req2, sizeof(req2) - 1, &ri) == -1); - ASSERT(parse_http_message(req6, 0, &ri) == -1); - ASSERT(parse_http_message(req8, sizeof(req8) - 1, &ri) == sizeof(req8) - 1); - - // TODO(lsm): Fix this. Header value may span multiple lines. - ASSERT(parse_http_message(req4, sizeof(req4) - 1, &ri) == sizeof(req4) - 1); - ASSERT(strcmp(ri.http_version, "1.1") == 0); - ASSERT(ri.num_headers == 3); - ASSERT(strcmp(ri.http_headers[0].name, "A") == 0); - ASSERT(strcmp(ri.http_headers[0].value, "foo bar") == 0); - ASSERT(strcmp(ri.http_headers[1].name, "B") == 0); - ASSERT(strcmp(ri.http_headers[1].value, "bar") == 0); - ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0); - ASSERT(strcmp(ri.http_headers[2].value, "") == 0); - - ASSERT(parse_http_message(req5, sizeof(req5) - 1, &ri) == sizeof(req5) - 1); - ASSERT(strcmp(ri.request_method, "GET") == 0); - ASSERT(strcmp(ri.http_version, "1.1") == 0); - - return NULL; -} - -static const char *test_should_keep_alive(void) { - struct mg_connection conn; - char req1[] = "GET / HTTP/1.1\r\n\r\n"; - char req2[] = "GET / HTTP/1.0\r\n\r\n"; - char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"; - char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"; - - memset(&conn, 0, sizeof(conn)); - ASSERT(parse_http_message(req1, sizeof(req1) - 1, &conn) == sizeof(req1) - 1); - ASSERT(should_keep_alive(&conn) != 0); - - parse_http_message(req2, sizeof(req2) - 1, &conn); - ASSERT(should_keep_alive(&conn) == 0); - - parse_http_message(req3, sizeof(req3) - 1, &conn); - ASSERT(should_keep_alive(&conn) == 0); - - parse_http_message(req4, sizeof(req4) - 1, &conn); - ASSERT(should_keep_alive(&conn) != 0); - - return NULL; -} - -static const char *test_match_prefix(void) { - ASSERT(match_prefix("/api", 4, "/api") == 4); - ASSERT(match_prefix("/a/", 3, "/a/b/c") == 3); - ASSERT(match_prefix("/a/", 3, "/ab/c") == -1); - ASSERT(match_prefix("/*/", 3, "/ab/c") == 4); - ASSERT(match_prefix("**", 2, "/a/b/c") == 6); - ASSERT(match_prefix("/*", 2, "/a/b/c") == 2); - ASSERT(match_prefix("*/*", 3, "/a/b/c") == 2); - ASSERT(match_prefix("**/", 3, "/a/b/c") == 5); - ASSERT(match_prefix("**.foo|**.bar", 13, "a.bar") == 5); - ASSERT(match_prefix("a|b|cd", 6, "cdef") == 2); - ASSERT(match_prefix("a|b|c?", 6, "cdef") == 2); - ASSERT(match_prefix("a|?|cd", 6, "cdef") == 1); - ASSERT(match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1); - ASSERT(match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12); - ASSERT(match_prefix("**/", 3, "/a/b/c") == 5); - ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1); - ASSERT(match_prefix("**/$", 4, "/a/b/") == 5); - ASSERT(match_prefix("$", 1, "") == 0); - ASSERT(match_prefix("$", 1, "x") == -1); - ASSERT(match_prefix("*$", 2, "x") == 1); - ASSERT(match_prefix("/$", 2, "/") == 1); - ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1); - ASSERT(match_prefix("**/$", 4, "/a/b/") == 5); - ASSERT(match_prefix("*", 1, "/hello/") == 0); - ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1); - ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6); - ASSERT(match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6); - ASSERT(match_prefix("**o$", 4, "HELLO") == 5); - return NULL; -} - -static const char *test_remove_double_dots() { - struct { char before[20], after[20]; } data[] = { - {"////a", "/a"}, - {"/.....", "/."}, - {"/......", "/"}, - {"...", "..."}, - {"/...///", "/./"}, - {"/a...///", "/a.../"}, - {"/.x", "/.x"}, - {"/\\", "/"}, - {"/a\\", "/a\\"}, - {"/a\\\\...", "/a\\."}, - }; - size_t i; - - for (i = 0; i < ARRAY_SIZE(data); i++) { - remove_double_dots_and_double_slashes(data[i].before); - ASSERT(strcmp(data[i].before, data[i].after) == 0); - } - - return NULL; -} - -#if 0 -static char *read_file(const char *path, int *size) { - FILE *fp; - struct stat st; - char *data = NULL; - if ((fp = fopen(path, "rb")) != NULL && !fstat(fileno(fp), &st)) { - *size = (int) st.st_size; - ASSERT((data = malloc(*size)) != NULL); - ASSERT(fread(data, 1, *size, fp) == (size_t) *size); - fclose(fp); - } - return data; -} - -static void test_upload(struct mg_connection *conn, const char *orig_path, - const char *uploaded_path) { - int len1, len2; - char path[500], *p1, *p2; - FILE *fp; - - ASSERT((fp = mg_upload(conn, ".", path, sizeof(path))) != NULL); - fclose(fp); - ASSERT(!strcmp(path, uploaded_path)); - ASSERT((p1 = read_file(orig_path, &len1)) != NULL); - ASSERT((p2 = read_file(path, &len2)) != NULL); - ASSERT(len1 == len2); - ASSERT(memcmp(p1, p2, len1) == 0); - free(p1), free(p2); - remove(path); -} - -static int event_handler(struct mg_event *event) { - struct mg_request_info *ri = event->request_info; - - if (event->type == MG_REQUEST_BEGIN) { - if (!strcmp(ri->uri, "/data")) { - mg_printf(event->conn, "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n\r\n" - "%s", fetch_data); - close_connection(event->conn); - return 1; - } - - if (!strcmp(ri->uri, "/zerolen")) { - char buf[100]; - mg_printf(event->conn, "%s", - "HTTP/1.0 200 OK\r\nContent-Length: 2\r\n\r\nok"); - printf("[%d]\n", mg_read(event->conn, buf, sizeof(buf))); - ASSERT(mg_read(event->conn, buf, sizeof(buf)) == 0); - return 1; - } - - if (!strcmp(ri->uri, "/upload")) { - test_upload(event->conn, "lua_5.2.1.h", "./f1.txt"); - test_upload(event->conn, "lsqlite3.c", "./f2.txt"); - ASSERT(mg_upload(event->conn, ".", NULL, 0) == NULL); - - mg_printf(event->conn, "HTTP/1.0 200 OK\r\n" - "Content-Type: text/plain\r\n\r\n" - "%s", upload_ok_message); - close_connection(event->conn); - return 1; - } - } else if (event->type == MG_EVENT_LOG) { - printf("%s\n", (const char *) event->event_param); - } - - return 0; -} - -static const char *OPTIONS[] = { - "document_root", ".", - "listening_ports", LISTENING_ADDR, - "enable_keep_alive", "yes", - "ssl_certificate", "ssl_cert.pem", - NULL, -}; - -static char *read_conn(struct mg_connection *conn, int *size) { - char buf[MG_BUF_LEN], *data = NULL; - int len; - *size = 0; - while ((len = mg_read(conn, buf, sizeof(buf))) > 0) { - *size += len; - ASSERT((data = realloc(data, *size)) != NULL); - memcpy(data + *size - len, buf, len); - } - return data; -} - -static void test_mg_download(void) { - char *p1, *p2, ebuf[100]; - int len1, len2, port = atoi(HTTPS_PORT); - struct mg_connection *conn; - struct mg_context *ctx; - - ASSERT((ctx = mg_start(OPTIONS, event_handler, NULL)) != NULL); - - ASSERT(mg_download(NULL, port, 0, ebuf, sizeof(ebuf), "%s", "") == NULL); - ASSERT(mg_download("localhost", 0, 0, ebuf, sizeof(ebuf), "%s", "") == NULL); - ASSERT(mg_download("localhost", port, 1, ebuf, sizeof(ebuf), - "%s", "") == NULL); - - // Fetch nonexistent file, should see 404 - ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s", - "GET /gimbec HTTP/1.0\r\n\r\n")) != NULL); - ASSERT(strcmp(conn->request_info.uri, "404") == 0); - mg_close_connection(conn); - - ASSERT((conn = mg_download("google.com", 443, 1, ebuf, sizeof(ebuf), "%s", - "GET / HTTP/1.0\r\n\r\n")) != NULL); - mg_close_connection(conn); - - // POST with "Content-Length: 0", must not block - ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1, - ebuf, sizeof(ebuf), "%s", - "POST /zerolen HTTP/1.1\r\n" - "Content-Lengh: 0\r\n\r\n ")) != NULL); - ASSERT((p1 = read_conn(conn, &len1)) != NULL); - ASSERT(len1 = 2); - ASSERT(memcmp(p1, "ok", 2) == 0); - mg_close_connection(conn); - - // Fetch main.c, should succeed - ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s", - "GET /main.c HTTP/1.0\r\n\r\n")) != NULL); - ASSERT(!strcmp(conn->request_info.uri, "200")); - ASSERT((p1 = read_conn(conn, &len1)) != NULL); - ASSERT((p2 = read_file("main.c", &len2)) != NULL); - ASSERT(len1 == len2); - ASSERT(memcmp(p1, p2, len1) == 0); - free(p1), free(p2); - mg_close_connection(conn); - - // Test SSL redirect, IP address - ASSERT((conn = mg_download("localhost", atoi(HTTP_PORT), 0, - ebuf, sizeof(ebuf), "%s", - "GET /foo HTTP/1.1\r\n\r\n")) != NULL); - ASSERT(strcmp(conn->request_info.uri, "302") == 0); - ASSERT(strcmp(mg_get_header(conn, "Location"), - "https://127.0.0.1:" HTTPS_PORT "/foo") == 0); - mg_close_connection(conn); - - // Test SSL redirect, Host: - ASSERT((conn = mg_download("localhost", atoi(HTTP_PORT), 0, - ebuf, sizeof(ebuf), "%s", - "GET /foo HTTP/1.1\r\nHost: a.b:77\n\n")) != NULL); - ASSERT(strcmp(conn->request_info.uri, "302") == 0); - ASSERT(strcmp(mg_get_header(conn, "Location"), - "https://a.b:" HTTPS_PORT "/foo") == 0); - mg_close_connection(conn); - - mg_stop(ctx); -} - -static int alloc_printf(char **buf, size_t size, char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - return alloc_vprintf(buf, size, fmt, ap); -} - -static void test_mg_upload(void) { - static const char *boundary = "OOO___MY_BOUNDARY___OOO"; - struct mg_context *ctx; - struct mg_connection *conn; - char ebuf[100], buf[20], *file_data, *file2_data, *post_data; - int file_len, file2_len, post_data_len; - - ASSERT((ctx = mg_start(OPTIONS, event_handler, NULL)) != NULL); - - // Upload two files - ASSERT((file_data = read_file("lua_5.2.1.h", &file_len)) != NULL); - ASSERT((file2_data = read_file("lsqlite3.c", &file2_len)) != NULL); - post_data = NULL; - post_data_len = alloc_printf(&post_data, 0, - // First file - "--%s\r\n" "Content-Disposition: form-data; " "name=\"file\"; " - "filename=\"%s\"\r\n\r\n" "%.*s\r\n" - // Second file - "--%s\r\n" "Content-Disposition: form-data; " "name=\"file\"; " - "filename=\"%s\"\r\n\r\n" "%.*s\r\n" - // Final boundary - "--%s--\r\n", - boundary, "f1.txt", file_len, file_data, boundary, "f2.txt", - file2_len, file2_data, boundary); - ASSERT(post_data_len > 0); - ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1, - ebuf, sizeof(ebuf), - "POST /upload HTTP/1.1\r\n" - "Content-Length: %d\r\n" - "Content-Type: multipart/form-data; " - "boundary=%s\r\n\r\n" - "%.*s", post_data_len, boundary, - post_data_len, post_data)) != NULL); - ASSERT(mg_read(conn, buf, sizeof(buf)) == (int) strlen(upload_ok_message)); - ASSERT(memcmp(buf, upload_ok_message, strlen(upload_ok_message)) == 0); - mg_close_connection(conn); - - mg_stop(ctx); -} - -static void test_mg_get_var(void) { - static const char *post[] = { - "a=1&&b=2&d&=&c=3%20&e=", - "q=&st=2012%2F11%2F13+17%3A05&et=&team_id=", - NULL - }; - char buf[20]; - - ASSERT(mg_get_var(post[0], strlen(post[0]), "a", buf, sizeof(buf)) == 1); - ASSERT(buf[0] == '1' && buf[1] == '\0'); - ASSERT(mg_get_var(post[0], strlen(post[0]), "b", buf, sizeof(buf)) == 1); - ASSERT(buf[0] == '2' && buf[1] == '\0'); - ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, sizeof(buf)) == 2); - ASSERT(buf[0] == '3' && buf[1] == ' ' && buf[2] == '\0'); - ASSERT(mg_get_var(post[0], strlen(post[0]), "e", buf, sizeof(buf)) == 0); - ASSERT(buf[0] == '\0'); - - ASSERT(mg_get_var(post[0], strlen(post[0]), "d", buf, sizeof(buf)) == -1); - ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, 2) == -2); - - ASSERT(mg_get_var(post[0], strlen(post[0]), "x", NULL, 10) == -2); - ASSERT(mg_get_var(post[0], strlen(post[0]), "x", buf, 0) == -2); - ASSERT(mg_get_var(post[1], strlen(post[1]), "st", buf, 16) == -2); - ASSERT(mg_get_var(post[1], strlen(post[1]), "st", buf, 17) == 16); -} - -static void test_next_option(void) { - const char *p, *list = "x/8,/y**=1;2k,z"; - struct vec a, b; - int i; - - ASSERT(next_option(NULL, &a, &b) == NULL); - for (i = 0, p = list; (p = next_option(p, &a, &b)) != NULL; i++) { - ASSERT(i != 0 || (a.ptr == list && a.len == 3 && b.len == 0)); - ASSERT(i != 1 || (a.ptr == list + 4 && a.len == 4 && b.ptr == list + 9 && - b.len == 4)); - - ASSERT(i != 2 || (a.ptr == list + 14 && a.len == 1 && b.len == 0)); - } -} - -#ifdef USE_LUA -static void check_lua_expr(lua_State *L, const char *expr, const char *value) { - const char *v, *var_name = "myVar"; - char buf[100]; - - snprintf(buf, sizeof(buf), "%s = %s", var_name, expr); - (void) luaL_dostring(L, buf); - lua_getglobal(L, var_name); - v = lua_tostring(L, -1); - ASSERT((value == NULL && v == NULL) || - (value != NULL && v != NULL && !strcmp(value, v))); -} - -static void test_lua(void) { - static struct mg_connection conn; - static struct mg_context ctx; - - char http_request[] = "POST /foo/bar HTTP/1.1\r\n" - "Content-Length: 12\r\n" - "Connection: close\r\n\r\nhello world!"; - lua_State *L = luaL_newstate(); - - conn.ctx = &ctx; - conn.buf = http_request; - conn.buf_size = conn.data_len = conn.num_bytes_read = strlen(http_request); - conn.request_len = parse_http_message(conn.buf, conn.data_len, - &conn.request_info); - conn.content_len = conn.data_len - conn.request_len; - - prepare_lua_environment(&conn, L); - ASSERT(lua_gettop(L) == 0); - - check_lua_expr(L, "'hi'", "hi"); - check_lua_expr(L, "mg.request_info.request_method", "POST"); - check_lua_expr(L, "mg.request_info.uri", "/foo/bar"); - check_lua_expr(L, "mg.request_info.num_headers", "2"); - check_lua_expr(L, "mg.request_info.remote_ip", "0"); - check_lua_expr(L, "mg.request_info.http_headers['Content-Length']", "12"); - check_lua_expr(L, "mg.request_info.http_headers['Connection']", "close"); - (void) luaL_dostring(L, "post = mg.read()"); - check_lua_expr(L, "# post", "12"); - check_lua_expr(L, "post", "hello world!"); - lua_close(L); -} -#endif - -static void test_mg_stat(void) { - struct file file = STRUCT_FILE_INITIALIZER; - ASSERT(!mg_stat(" does not exist ", &file)); -} - -static void test_skip_quoted(void) { - char x[] = "a=1, b=2 c='hi \' there'", *s = x, *p; - - p = skip_quoted(&s, ", ", ", ", 0); - ASSERT(p != NULL && !strcmp(p, "a=1")); - - p = skip_quoted(&s, ", ", ", ", 0); - ASSERT(p != NULL && !strcmp(p, "b=2")); - - // TODO(lsm): fix this -#if 0 - p = skip_quoted(&s, "'", ", ", '\\'); - p = skip_quoted(&s, "'", ", ", '\\'); - printf("[%s]\n", p); - ASSERT(p != NULL && !strcmp(p, "hi ' there")); -#endif -} - -static void test_alloc_vprintf(void) { - char buf[MG_BUF_LEN], *p = buf; - - ASSERT(alloc_printf(&p, sizeof(buf), "%s", "hi") == 2); - ASSERT(p == buf); - ASSERT(alloc_printf(&p, sizeof(buf), "%s", "") == 0); - ASSERT(alloc_printf(&p, sizeof(buf), "") == 0); - - // Pass small buffer, make sure alloc_printf allocates - ASSERT(alloc_printf(&p, 1, "%s", "hello") == 5); - ASSERT(p != buf); - free(p); -} - -static void test_request_replies(void) { - char ebuf[100]; - int i, port = atoi(HTTPS_PORT); - struct mg_connection *conn; - struct mg_context *ctx; - static struct { const char *request, *reply_regex; } tests[] = { - { - "GET test/hello.txt HTTP/1.0\r\nRange: bytes=3-5\r\n\r\n", - "^HTTP/1.1 206 Partial Content" - }, - {NULL, NULL}, - }; - - ASSERT((ctx = mg_start(OPTIONS, event_handler, NULL)) != NULL); - for (i = 0; tests[i].request != NULL; i++) { - ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s", - tests[i].request)) != NULL); - mg_close_connection(conn); - } - mg_stop(ctx); -} - -static const char *api_uri = "/?a=%20&b=&c=xx"; -static int api_cb(struct mg_event *event) { - struct mg_request_info *ri = event->request_info; - char post_data[100] = ""; - - if (event->type == MG_REQUEST_BEGIN) { - ASSERT(event->user_data == (void *) 123); - ASSERT(ri->num_headers == 2); - ASSERT(strcmp(mg_get_header(event->conn, "host"), "blah.com") == 0); - ASSERT(mg_read(event->conn, post_data, sizeof(post_data)) == 3); - ASSERT(memcmp(post_data, "b=1", 3) == 0); - ASSERT(ri->query_string != NULL); - ASSERT(strcmp(ri->query_string, api_uri + 2) == 0); - ASSERT(ri->remote_ip > 0); - ASSERT(ri->remote_port > 0); - ASSERT(strcmp(ri->http_version, "1.0") == 0); - - mg_printf(event->conn, "HTTP/1.0 200 OK\r\n\r\n"); - return 1; - } - - return 0; -} - -static const char *test_mg_strcasestr(void) { - static const char *big1 = "abcdef"; - ASSERT(mg_strcasestr("Y", "X") == NULL); - ASSERT(mg_strcasestr("Y", "y") != NULL); - ASSERT(mg_strcasestr(big1, "X") == NULL); - ASSERT(mg_strcasestr(big1, "CD") == big1 + 2); - ASSERT(mg_strcasestr("aa", "AAB") == NULL); - return NULL; -} - -static void test_api_calls(void) { - char ebuf[100]; - struct mg_connection *conn; - struct mg_context *ctx; - static const char *fmt = "POST %s HTTP/1.0\r\n" - "Host: blah.com\n" // More spaces before - "content-length: 3\r\n" // Lower case header name - "\r\nb=123456"; // Content size > content-length, test for mg_read() - - ASSERT((ctx = mg_start(OPTIONS, api_cb, (void *) 123)) != NULL); - ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1, - ebuf, sizeof(ebuf), fmt, api_uri)) != NULL); - mg_close_connection(conn); - mg_stop(ctx); -} - -static void test_mg_get_cookie(void) { - char buf[20]; - - ASSERT(mg_get_cookie("", "foo", NULL, sizeof(buf)) == -2); - ASSERT(mg_get_cookie("", "foo", buf, 0) == -2); - ASSERT(mg_get_cookie("", "foo", buf, sizeof(buf)) == -1); - ASSERT(mg_get_cookie("", NULL, buf, sizeof(buf)) == -1); - ASSERT(mg_get_cookie("a=1; b=2; c; d", "a", buf, sizeof(buf)) == 1); - ASSERT(strcmp(buf, "1") == 0); - ASSERT(mg_get_cookie("a=1; b=2; c; d", "b", buf, sizeof(buf)) == 1); - ASSERT(strcmp(buf, "2") == 0); - ASSERT(mg_get_cookie("a=1; b=123", "b", buf, sizeof(buf)) == 3); - ASSERT(strcmp(buf, "123") == 0); - ASSERT(mg_get_cookie("a=1; b=2; c; d", "c", buf, sizeof(buf)) == -1); -} -#endif - -static const char *test_url_decode(void) { - char buf[100]; - - ASSERT(mg_url_decode("foo", 3, buf, 3, 0) == -1); // No space for \0 - ASSERT(mg_url_decode("foo", 3, buf, 4, 0) == 3); - ASSERT(strcmp(buf, "foo") == 0); - - ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 0) == 2); - ASSERT(strcmp(buf, "a+") == 0); - - ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 1) == 2); - ASSERT(strcmp(buf, "a ") == 0); - - ASSERT(mg_url_decode("%61", 1, buf, sizeof(buf), 1) == 1); - ASSERT(strcmp(buf, "%") == 0); - - ASSERT(mg_url_decode("%61", 2, buf, sizeof(buf), 1) == 2); - ASSERT(strcmp(buf, "%6") == 0); - - ASSERT(mg_url_decode("%61", 3, buf, sizeof(buf), 1) == 1); - ASSERT(strcmp(buf, "a") == 0); - return NULL; -} - -static const char *test_to64(void) { - ASSERT(strtoll("0", NULL, 10) == 0); - ASSERT(strtoll("123", NULL, 10) == 123); - ASSERT(strtoll("-34", NULL, 10) == -34); - ASSERT(strtoll("3566626116", NULL, 10) == 3566626116); - return NULL; -} - -static const char *test_parse_port_string(void) { - static const char *valid[] = { - "1", "1.2.3.4:1", -#if defined(USE_IPV6) - "[::1]:123", "[3ffe:2a00:100:7031::1]:900", -#endif - NULL - }; - static const char *invalid[] = { - "0", "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p", NULL - }; - union socket_address sa; - int i; - - for (i = 0; valid[i] != NULL; i++) { - ASSERT(parse_port_string(valid[i], &sa) != 0); - } - - for (i = 0; invalid[i] != NULL; i++) { - ASSERT(parse_port_string(invalid[i], &sa) == 0); - } - - return NULL; -} - -static const char *test_base64_encode(void) { - const char *in[] = {"a", "ab", "abc", "abcd", NULL}; - const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="}; - char buf[100]; - int i; - - for (i = 0; in[i] != NULL; i++) { - base64_encode((unsigned char *) in[i], strlen(in[i]), buf); - ASSERT(!strcmp(buf, out[i])); - } - - return NULL; -} - -static const char *test_mg_parse_header() { - const char *str = "xx yy, ert=234 ii zz='aa bb', gf=\"xx d=1234"; - char buf[10]; - ASSERT(mg_parse_header(str, "yy", buf, sizeof(buf)) == 0); - ASSERT(mg_parse_header(str, "ert", buf, sizeof(buf)) == 3); - ASSERT(strcmp(buf, "234") == 0); - ASSERT(mg_parse_header(str, "ert", buf, 2) == 0); - ASSERT(mg_parse_header(str, "ert", buf, 3) == 0); - ASSERT(mg_parse_header(str, "ert", buf, 4) == 3); - ASSERT(mg_parse_header(str, "gf", buf, sizeof(buf)) == 0); - ASSERT(mg_parse_header(str, "zz", buf, sizeof(buf)) == 5); - ASSERT(strcmp(buf, "aa bb") == 0); - ASSERT(mg_parse_header(str, "d", buf, sizeof(buf)) == 4); - ASSERT(strcmp(buf, "1234") == 0); - return NULL; -} - -static const char *run_all_tests(void) { - RUN_TEST(test_should_keep_alive); - RUN_TEST(test_match_prefix); - RUN_TEST(test_remove_double_dots); - RUN_TEST(test_parse_http_message); - RUN_TEST(test_parse_port_string); - RUN_TEST(test_to64); - RUN_TEST(test_url_decode); - RUN_TEST(test_base64_encode); - RUN_TEST(test_mg_parse_header); -#if 0 - test_mg_strcasestr(); - test_mg_download(); - test_mg_get_var(); - test_next_option(); - test_mg_stat(); - test_skip_quoted(); - test_mg_upload(); - test_request_replies(); - test_api_calls(); - test_mg_get_cookie(); -#ifdef USE_LUA - test_lua(); -#endif -#endif - return NULL; -} - -int __cdecl main(void) { - const char *fail_msg = run_all_tests(); - printf("%s, tests run: %d\n", fail_msg ? "FAIL" : "PASS", static_num_tests); - return fail_msg == NULL ? EXIT_SUCCESS : EXIT_FAILURE; -}