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(event_handler, NULL, OPTIONS)) != NULL); ASSERT(mg_download(NULL, port, 0, ebuf, sizeof(ebuf), "") == NULL); ASSERT(mg_download("localhost", 0, 0, ebuf, sizeof(ebuf), "") == NULL); ASSERT(mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "") == 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); // Fetch mongoose.c, should succeed ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s", "GET /mongoose.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("mongoose.c", &len2)) != NULL); ASSERT(len1 == len2); ASSERT(memcmp(p1, p2, len1) == 0); free(p1), free(p2); mg_close_connection(conn); // Fetch in-memory file, should succeed. ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s", "GET /blah HTTP/1.1\r\n\r\n")) != NULL); ASSERT((p1 = read_conn(conn, &len1)) != NULL); ASSERT(len1 == (int) strlen(inmemory_file_data)); ASSERT(memcmp(p1, inmemory_file_data, len1) == 0); free(p1); 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 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, *post_data = NULL; int file_len, post_data_len; ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL); ASSERT((file_data = read_file("mongoose.c", &file_len)) != NULL); post_data_len = alloc_printf(&post_data, 0, "--%s\r\n" "Content-Disposition: form-data; " "name=\"file\"; " "filename=\"%s\"\r\n\r\n" "%.*s\r\n" "--%s\r\n", boundary, upload_filename, file_len, file_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); free(file_data), free(post_data); 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_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_api_calls(void) { char ebuf[100]; struct mg_callbacks callbacks; struct mg_connection *conn; struct mg_context *ctx; static const char *request = "POST /?a=%20&b=&c=xx 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() memset(&callbacks, 0, sizeof(callbacks)); callbacks.begin_request = api_callback; ASSERT((ctx = mg_start(&callbacks, (void *) 123, OPTIONS)) != NULL); ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1, ebuf, sizeof(ebuf), "%s", request)) != NULL); mg_close_connection(conn); mg_stop(ctx); }
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(&CALLBACKS, NULL, OPTIONS)) != 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 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); }
void HttpRequest::ThreadFunction() { String protocol = "http"; String host; String path = "/"; int port = 80; unsigned protocolEnd = url_.Find("://"); if (protocolEnd != String::NPOS) { protocol = url_.Substring(0, protocolEnd); host = url_.Substring(protocolEnd + 3); } else host = url_; unsigned pathStart = host.Find('/'); if (pathStart != String::NPOS) { path = host.Substring(pathStart); host = host.Substring(0, pathStart); } unsigned portStart = host.Find(':'); if (portStart != String::NPOS) { port = ToInt(host.Substring(portStart + 1)); host = host.Substring(0, portStart); } char errorBuffer[ERROR_BUFFER_SIZE]; memset(errorBuffer, 0, sizeof(errorBuffer)); String headersStr; for (unsigned i = 0; i < headers_.Size(); ++i) { // Trim and only add non-empty header strings String header = headers_[i].Trimmed(); if (header.Length()) headersStr += header + "\r\n"; } // Initiate the connection. This may block due to DNS query /// \todo SSL mode will not actually work unless Civetweb's SSL mode is initialized with an external SSL DLL mg_connection* connection = 0; if (postData_.Empty()) { connection = mg_download(host.CString(), port, protocol.Compare("https", false) ? 0 : 1, errorBuffer, sizeof(errorBuffer), "%s %s HTTP/1.0\r\n" "Host: %s\r\n" "%s" "\r\n", verb_.CString(), path.CString(), host.CString(), headersStr.CString()); } else { connection = mg_download(host.CString(), port, protocol.Compare("https", false) ? 0 : 1, errorBuffer, sizeof(errorBuffer), "%s %s HTTP/1.0\r\n" "Host: %s\r\n" "%s" "Content-Length: %d\r\n" "\r\n" "%s", verb_.CString(), path.CString(), host.CString(), headersStr.CString(), postData_.Length(), postData_.CString()); } { MutexLock lock(mutex_); state_ = connection ? HTTP_OPEN : HTTP_ERROR; // If no connection could be made, store the error and exit if (state_ == HTTP_ERROR) { error_ = String(&errorBuffer[0]); return; } } // Loop while should run, read data from the connection, copy to the main thread buffer if there is space while (shouldRun_) { // Read less than full buffer to be able to distinguish between full and empty ring buffer. Reading may block int bytesRead = mg_read(connection, httpReadBuffer_.Get(), READ_BUFFER_SIZE / 4); if (bytesRead <= 0) break; mutex_.Acquire(); // Wait until enough space in the main thread's ring buffer for (;;) { unsigned spaceInBuffer = READ_BUFFER_SIZE - ((writePosition_ - readPosition_) & (READ_BUFFER_SIZE - 1)); if ((int)spaceInBuffer > bytesRead || !shouldRun_) break; mutex_.Release(); Time::Sleep(5); mutex_.Acquire(); } if (!shouldRun_) { mutex_.Release(); break; } if (writePosition_ + bytesRead <= READ_BUFFER_SIZE) memcpy(readBuffer_.Get() + writePosition_, httpReadBuffer_.Get(), bytesRead); else { // Handle ring buffer wrap unsigned part1 = READ_BUFFER_SIZE - writePosition_; unsigned part2 = bytesRead - part1; memcpy(readBuffer_.Get() + writePosition_, httpReadBuffer_.Get(), part1); memcpy(readBuffer_.Get(), httpReadBuffer_.Get() + part1, part2); } writePosition_ += bytesRead; writePosition_ &= READ_BUFFER_SIZE - 1; mutex_.Release(); } // Close the connection mg_close_connection(connection); { MutexLock lock(mutex_); state_ = HTTP_CLOSED; } }
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); }