[test] Switch to C++ HTTP server
This commit is contained in:
@@ -110,15 +110,26 @@ add_library(
|
||||
${MBGL_ROOT}/test/util/url.test.cpp
|
||||
)
|
||||
|
||||
find_program(MBGL_TEST_NODEJS NAMES nodejs node)
|
||||
find_program(MBGL_TEST_NPM NAMES npm)
|
||||
|
||||
if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL Android OR NOT MBGL_TEST_NODEJS OR NOT MBGL_TEST_NPM)
|
||||
if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL Android)
|
||||
message("Target platform does not support HTTP tests or dependencies not found.")
|
||||
|
||||
set(MBGL_TEST_HAS_TEST_SERVER 0)
|
||||
else()
|
||||
set(MBGL_TEST_HAS_TEST_SERVER 1)
|
||||
target_sources(
|
||||
mbgl-test
|
||||
PRIVATE ${MBGL_ROOT}/test/src/mbgl/test/http_server.cpp
|
||||
)
|
||||
set_source_files_properties(
|
||||
${MBGL_ROOT}/test/src/mbgl/test/http_server.cpp
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS
|
||||
-Wno-shadow
|
||||
)
|
||||
target_include_directories(
|
||||
mbgl-test
|
||||
PRIVATE ${MBGL_ROOT}/vendor/cpp-httplib
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED ENV{CI})
|
||||
@@ -129,7 +140,7 @@ endif()
|
||||
|
||||
target_compile_definitions(
|
||||
mbgl-test
|
||||
PRIVATE NODE_EXECUTABLE=${MBGL_TEST_NODEJS} TEST_HAS_SERVER=${MBGL_TEST_HAS_TEST_SERVER} CI_BUILD=${MBGL_TEST_BUILD_ON_CI}
|
||||
PRIVATE TEST_HAS_SERVER=${MBGL_TEST_HAS_TEST_SERVER} CI_BUILD=${MBGL_TEST_BUILD_ON_CI}
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
|
||||
@@ -54,9 +54,14 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace httplib {
|
||||
class Server;
|
||||
}
|
||||
|
||||
namespace mbgl {
|
||||
namespace test {
|
||||
|
||||
@@ -69,6 +74,16 @@ private:
|
||||
int fd = -1;
|
||||
};
|
||||
|
||||
class HttpServer {
|
||||
public:
|
||||
HttpServer();
|
||||
~HttpServer();
|
||||
|
||||
private:
|
||||
std::unique_ptr<httplib::Server> server;
|
||||
std::thread serverThread;
|
||||
};
|
||||
|
||||
void checkImage(const std::string& base,
|
||||
const PremultipliedImage& actual,
|
||||
double imageThreshold = 0,
|
||||
|
||||
199
test/src/mbgl/test/http_server.cpp
Normal file
199
test/src/mbgl/test/http_server.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
#include <mbgl/test/util.hpp>
|
||||
|
||||
#include <mbgl/util/chrono.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
|
||||
// Limit to 4 threads (default is the nbr of CPU cores)
|
||||
#define CPPHTTPLIB_THREAD_POOL_COUNT 4
|
||||
#include <httplib.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace mbgl {
|
||||
namespace test {
|
||||
|
||||
void runServer(std::unique_ptr<httplib::Server>& server) {
|
||||
using namespace httplib;
|
||||
|
||||
server->Get("/test", [](const Request& req, Response& res) {
|
||||
if (req.has_param("modified")) {
|
||||
std::string str = util::rfc1123(util::parseTimestamp(std::stoi(req.get_param_value("modified"))));
|
||||
res.set_header("Last-Modified", str);
|
||||
}
|
||||
if (req.has_param("expires")) {
|
||||
std::string str = util::rfc1123(util::parseTimestamp(std::stoi(req.get_param_value("expires"))));
|
||||
res.set_header("Expires", str);
|
||||
}
|
||||
if (req.has_param("etag")) {
|
||||
res.set_header("ETag", req.get_param_value("etag"));
|
||||
}
|
||||
if (req.has_param("cachecontrol")) {
|
||||
res.set_header("Cache-Control", "max-age=" + req.get_param_value("cachecontrol"));
|
||||
}
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
server->Get("/stale", [](const Request&, Response&) {
|
||||
// Never respond.
|
||||
});
|
||||
|
||||
std::atomic_int cacheCounter(0);
|
||||
server->Get("/cache", [&](const Request&, Response& res) {
|
||||
res.set_header("Cache-Control", "max-age=30"); // Allow caching for 30 seconds
|
||||
res.set_content("Response " + std::to_string(++cacheCounter), "text/plain");
|
||||
});
|
||||
|
||||
server->Get("/revalidate-same", [](const Request& req, Response& res) {
|
||||
if (req.get_header_value("if-none-match") == "snowfall") {
|
||||
// Second request can be cached for 1 second.
|
||||
res.set_header("Cache-Control", "max-age=1, must-revalidate");
|
||||
res.status = 304;
|
||||
} else {
|
||||
// First request must always be revalidated.
|
||||
res.set_header("ETag", "snowfall");
|
||||
res.set_header("Cache-Control", "must-revalidate");
|
||||
res.status = 200;
|
||||
res.set_content("Response", "text/plain");
|
||||
}
|
||||
});
|
||||
|
||||
std::atomic_bool expiredOnce(true);
|
||||
server->Get("/clockskew", [&](const Request&, Response& res) {
|
||||
std::string dateStr;
|
||||
if (expiredOnce) {
|
||||
dateStr = "jan 1 10:00:00 2010 utc";
|
||||
expiredOnce = false;
|
||||
} else {
|
||||
dateStr = "jan 1 10:01:00 2010 utc";
|
||||
}
|
||||
res.set_header("Expires", util::rfc1123(util::parseTimestamp(dateStr.c_str())));
|
||||
res.status = 200;
|
||||
res.set_content("Response", "text/plain");
|
||||
});
|
||||
|
||||
server->Get("/revalidate-modified", [&](const Request& req, Response& res) {
|
||||
auto jan1 = util::parseTimestamp("jan 1 2015 utc");
|
||||
|
||||
if (req.has_header("if-modified-since")) {
|
||||
auto modified_since = util::parseTimestamp(req.get_header_value("if-modified-since").c_str());
|
||||
if (modified_since >= jan1) {
|
||||
res.set_header("Cache-Control", "max-age=1, must-revalidate");
|
||||
res.status = 304;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// First request must always be revalidated.
|
||||
res.set_header("Last-Modified", util::rfc1123(jan1));
|
||||
res.set_header("Cache-Control", "must-revalidate");
|
||||
res.status = 200;
|
||||
res.set_content("Response", "text/plain");
|
||||
});
|
||||
|
||||
std::atomic_int revalidateEtagCounter(1);
|
||||
server->Get("/revalidate-etag", [&](const Request&, Response& res) {
|
||||
res.set_header("ETag", "response-" + std::to_string(revalidateEtagCounter));
|
||||
res.set_header("Cache-Control", "must-revalidate");
|
||||
|
||||
res.status = 200;
|
||||
res.set_content("Response " + std::to_string(revalidateEtagCounter), "text/plain");
|
||||
revalidateEtagCounter++;
|
||||
});
|
||||
|
||||
server->Get("/empty-data", [](const Request&, Response& res) { res.status = 200; });
|
||||
|
||||
server->Get("/no-content", [](const Request&, Response& res) { res.status = 204; });
|
||||
|
||||
server->Get("/not-found", [](const Request&, Response& res) {
|
||||
res.status = 404;
|
||||
res.set_content("Not Found!", "text/plain");
|
||||
});
|
||||
|
||||
server->Get("/permanent-error", [](const Request&, Response& res) {
|
||||
res.status = 500;
|
||||
res.set_content("Server Error!", "text/plain");
|
||||
});
|
||||
|
||||
std::atomic_int temporaryErrorCounter(0);
|
||||
server->Get("/temporary-error", [&](const Request&, Response& res) {
|
||||
if (temporaryErrorCounter == 0) {
|
||||
res.status = 500;
|
||||
} else {
|
||||
res.status = 200;
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
}
|
||||
temporaryErrorCounter++;
|
||||
});
|
||||
|
||||
server->Get("/rate-limit", [](const Request& req, Response& res) {
|
||||
if (req.has_param("std")) {
|
||||
res.set_header("Retry-After", "1");
|
||||
} else if (req.has_param("mbx")) {
|
||||
res.set_header("x-rate-limit-reset", std::to_string(util::now().time_since_epoch().count() + 1));
|
||||
}
|
||||
res.status = 429;
|
||||
});
|
||||
|
||||
std::atomic_bool styleFailOnce500(true);
|
||||
server->Get("/style-fail-once-500", [&](const Request&, Response& res) {
|
||||
if (styleFailOnce500) {
|
||||
res.status = 500;
|
||||
res.set_content("Not found!", "text/plain");
|
||||
styleFailOnce500 = false;
|
||||
} else {
|
||||
res.status = 200;
|
||||
res.set_content(R"({ "version": 8, "name": "Test Style" })", "text/plain");
|
||||
}
|
||||
});
|
||||
|
||||
std::atomic_bool styleFailOnce404(true);
|
||||
server->Get("/style-fail-once-404", [&](const Request&, Response& res) {
|
||||
if (styleFailOnce404) {
|
||||
res.status = 404;
|
||||
res.set_content("Not found!", "text/plain");
|
||||
styleFailOnce404 = false;
|
||||
} else {
|
||||
res.status = 200;
|
||||
res.set_content(R"({ "version": 8, "name": "Test Style" })", "text/plain");
|
||||
}
|
||||
});
|
||||
|
||||
std::atomic_bool styleFailOnce404Cache(true);
|
||||
server->Get("/style-fail-once-404-cache", [&](const Request&, Response& res) {
|
||||
if (styleFailOnce404Cache) {
|
||||
res.set_header("Cache-Control", "max-age=30");
|
||||
res.status = 404;
|
||||
res.set_content("Not found!", "text/plain");
|
||||
styleFailOnce404Cache = false;
|
||||
} else {
|
||||
res.status = 200;
|
||||
res.set_content(R"({ "version": 8, "name": "Teste Style" })", "text/plain");
|
||||
}
|
||||
});
|
||||
|
||||
server->Get("/delayed", [](const Request&, Response& res) {
|
||||
usleep(200000);
|
||||
res.status = 200;
|
||||
res.set_content("Response", "text/plain");
|
||||
});
|
||||
|
||||
server->Get(R"(/load/(\d+))", [](const Request req, Response& res) {
|
||||
auto numbers = req.matches[1];
|
||||
res.set_content("Request " + std::string(numbers), "text/plain");
|
||||
});
|
||||
|
||||
server->listen("127.0.0.1", 3000);
|
||||
}
|
||||
|
||||
HttpServer::HttpServer() {
|
||||
server = std::make_unique<httplib::Server>();
|
||||
serverThread = std::thread(runServer, std::ref(server));
|
||||
}
|
||||
|
||||
HttpServer::~HttpServer() {
|
||||
server->stop();
|
||||
serverThread.join();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace mbgl
|
||||
@@ -8,7 +8,7 @@ namespace mbgl {
|
||||
|
||||
int runTests(int argc, char *argv[]) {
|
||||
#if TEST_HAS_SERVER
|
||||
auto server = std::make_unique<test::Server>("test/storage/server.js");
|
||||
auto server = std::make_unique<test::HttpServer>();
|
||||
#endif
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"test/sprite/sprite_parser.test.cpp",
|
||||
"test/src/mbgl/test/fixture_log_observer.cpp",
|
||||
"test/src/mbgl/test/getrss.cpp",
|
||||
"test/src/mbgl/test/http_server.cpp",
|
||||
"test/src/mbgl/test/sqlite3_test_fs.cpp",
|
||||
"test/src/mbgl/test/stub_file_source.cpp",
|
||||
"test/src/mbgl/test/test.cpp",
|
||||
|
||||
Reference in New Issue
Block a user