#define DROGON_TEST_MAIN #include #include #include #include #include #include #include #include using namespace drogon; DROGON_TEST(RealIpResolver) { auto client = HttpClient::newHttpClient("http://127.0.0.1:8017", HttpAppFramework::instance().getLoop()); auto newRequest = []() { auto req = HttpRequest::newHttpRequest(); req->setPath("/RealIpController/my-ip"); return req; }; // 1. No headers { auto req = newRequest(); client->sendRequest( req, [TEST_CTX](ReqResult res, const HttpResponsePtr &resp) { REQUIRE(res == ReqResult::Ok); CHECK(resp->getStatusCode() == HttpStatusCode::k200OK); CHECK(resp->contentType() == drogon::CT_TEXT_PLAIN); CHECK(resp->body() == "127.0.0.1"); }); } // 2. header only contains real ip { auto req = newRequest(); req->addHeader("x-forwarded-for", "1.1.1.1"); client->sendRequest( req, [TEST_CTX](ReqResult res, const HttpResponsePtr &resp) { REQUIRE(res == ReqResult::Ok); CHECK(resp->getStatusCode() == HttpStatusCode::k200OK); CHECK(resp->contentType() == drogon::CT_TEXT_PLAIN); CHECK(resp->body() == "1.1.1.1"); }); } // 3. Ip with port { auto req = newRequest(); req->addHeader("x-forwarded-for", "1.1.1.1:7777"); client->sendRequest( req, [TEST_CTX](ReqResult res, const HttpResponsePtr &resp) { REQUIRE(res == ReqResult::Ok); CHECK(resp->getStatusCode() == HttpStatusCode::k200OK); CHECK(resp->contentType() == drogon::CT_TEXT_PLAIN); CHECK(resp->body() == "1.1.1.1"); }); } // 3. multiple ips { auto req = newRequest(); req->addHeader("x-forwarded-for", "2.2.2.2,1.1.1.1:7001, 172.16.0.100:7002,127.0.0.1"); client->sendRequest( req, [TEST_CTX](ReqResult res, const HttpResponsePtr &resp) { REQUIRE(res == ReqResult::Ok); CHECK(resp->getStatusCode() == HttpStatusCode::k200OK); CHECK(resp->contentType() == drogon::CT_TEXT_PLAIN); CHECK(resp->body() == "1.1.1.1"); }); } // 4. Ignore error in header { auto req = newRequest(); req->addHeader("x-forwarded-for", "2.2.2.2,1.1.1.1:7001, wrong,9.9.9.9"); client->sendRequest( req, [TEST_CTX](ReqResult res, const HttpResponsePtr &resp) { REQUIRE(res == ReqResult::Ok); CHECK(resp->getStatusCode() == HttpStatusCode::k200OK); CHECK(resp->contentType() == drogon::CT_TEXT_PLAIN); CHECK(resp->body() == "1.1.1.1"); }); } }; class RealIpController : public drogon::HttpController { public: METHOD_LIST_BEGIN METHOD_ADD(RealIpController::getRealIp, "/my-ip", Get); METHOD_LIST_END void getRealIp(const HttpRequestPtr &req, std::function &&callback) { auto addr = req->attributes()->get("real-ip"); auto resp = HttpResponse::newHttpResponse(); resp->setContentTypeCode(drogon::CT_TEXT_PLAIN); resp->setBody(addr.toIp()); callback(resp); } }; // -- main int main(int argc, char **argv) { trantor::Logger::setLogLevel(trantor::Logger::kInfo); std::promise p1; std::future f1 = p1.get_future(); std::stringstream ss; ss << R"({ "listeners": [ { "address": "0.0.0.0", "port": 8017 } ], "plugins": [ { "name": "drogon::plugin::RealIpResolver", "config": { "trust_ips": ["127.0.0.1", "172.16.0.0/12", "9.9.9.9/32"], "from_header": "x-forwarded-for", "attribute_key": "real-ip" } } ] })"; Json::Value config; ss >> config; std::thread thr([&]() { app().loadConfigJson(config); app().getLoop()->queueInLoop([&p1]() { p1.set_value(); }); app().run(); }); f1.get(); int testStatus = test::run(argc, argv); app().getLoop()->queueInLoop([]() { app().quit(); }); thr.join(); return testStatus; }