354 lines
12 KiB
C++
354 lines
12 KiB
C++
|
|
#include "CustomCtrl.h"
|
|
#include "CustomHeaderFilter.h"
|
|
#include "DigestAuthFilter.h"
|
|
#include <drogon/drogon.h>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <iostream>
|
|
|
|
using namespace drogon;
|
|
using namespace std::chrono_literals;
|
|
class A : public DrObjectBase
|
|
{
|
|
public:
|
|
void handle(const HttpRequestPtr &req,
|
|
std::function<void(const HttpResponsePtr &)> &&callback,
|
|
int p1,
|
|
const std::string &p2,
|
|
const std::string &p3,
|
|
int p4) const
|
|
{
|
|
HttpViewData data;
|
|
data.insert("title", std::string("ApiTest::get"));
|
|
std::unordered_map<std::string, std::string> para;
|
|
para["int p1"] = std::to_string(p1);
|
|
para["string p2"] = p2;
|
|
para["string p3"] = p3;
|
|
para["int p4"] = std::to_string(p4);
|
|
|
|
data.insert("parameters", para);
|
|
auto res = HttpResponse::newHttpViewResponse("ListParaView", data);
|
|
callback(res);
|
|
}
|
|
static void staticHandle(
|
|
const HttpRequestPtr &req,
|
|
std::function<void(const HttpResponsePtr &)> &&callback,
|
|
int p1,
|
|
const std::string &p2,
|
|
const std::string &p3,
|
|
int p4)
|
|
{
|
|
HttpViewData data;
|
|
data.insert("title", std::string("ApiTest::get"));
|
|
std::unordered_map<std::string, std::string> para;
|
|
para["int p1"] = std::to_string(p1);
|
|
para["string p2"] = p2;
|
|
para["string p3"] = p3;
|
|
para["int p4"] = std::to_string(p4);
|
|
|
|
data.insert("parameters", para);
|
|
auto res = HttpResponse::newHttpViewResponse("ListParaView", data);
|
|
callback(res);
|
|
}
|
|
};
|
|
class B : public DrObjectBase
|
|
{
|
|
public:
|
|
void operator()(const HttpRequestPtr &req,
|
|
std::function<void(const HttpResponsePtr &)> &&callback,
|
|
int p1,
|
|
int p2)
|
|
{
|
|
HttpViewData data;
|
|
data.insert("title", std::string("ApiTest::get"));
|
|
std::unordered_map<std::string, std::string> para;
|
|
para["p1"] = std::to_string(p1);
|
|
para["p2"] = std::to_string(p2);
|
|
data.insert("parameters", para);
|
|
auto res = HttpResponse::newHttpViewResponse("ListParaView", data);
|
|
callback(res);
|
|
}
|
|
};
|
|
|
|
class C : public drogon::HttpController<C>
|
|
{
|
|
public:
|
|
METHOD_LIST_BEGIN
|
|
ADD_METHOD_TO(C::priv, "/priv/resource", Get, "DigestAuthFilter");
|
|
METHOD_LIST_END
|
|
void priv(const HttpRequestPtr &req,
|
|
std::function<void(const HttpResponsePtr &)> &&callback) const
|
|
{
|
|
auto resp = HttpResponse::newHttpResponse();
|
|
resp->setBody("<P>private content, only for authenticated users</P>");
|
|
callback(resp);
|
|
}
|
|
};
|
|
|
|
namespace api
|
|
{
|
|
namespace v1
|
|
{
|
|
class Test : public HttpController<Test>
|
|
{
|
|
public:
|
|
METHOD_LIST_BEGIN
|
|
METHOD_ADD(Test::get,
|
|
"get/{2}/{1}",
|
|
Get); // path is /api/v1/test/get/{arg2}/{arg1}
|
|
METHOD_ADD(Test::list,
|
|
"/{2}/info",
|
|
Get); // path is /api/v1/test/{arg2}/info
|
|
METHOD_LIST_END
|
|
void get(const HttpRequestPtr &req,
|
|
std::function<void(const HttpResponsePtr &)> &&callback,
|
|
int p1,
|
|
int p2) const
|
|
{
|
|
HttpViewData data;
|
|
data.insert("title", std::string("ApiTest::get"));
|
|
std::unordered_map<std::string, std::string> para;
|
|
para["p1"] = std::to_string(p1);
|
|
para["p2"] = std::to_string(p2);
|
|
data.insert("parameters", para);
|
|
auto res = HttpResponse::newHttpViewResponse("ListParaView", data);
|
|
callback(res);
|
|
}
|
|
void list(const HttpRequestPtr &req,
|
|
std::function<void(const HttpResponsePtr &)> &&callback,
|
|
int p1,
|
|
int p2) const
|
|
{
|
|
HttpViewData data;
|
|
data.insert("title", std::string("ApiTest::get"));
|
|
std::unordered_map<std::string, std::string> para;
|
|
para["p1"] = std::to_string(p1);
|
|
para["p2"] = std::to_string(p2);
|
|
data.insert("parameters", para);
|
|
auto res = HttpResponse::newHttpViewResponse("ListParaView", data);
|
|
callback(res);
|
|
}
|
|
};
|
|
} // namespace v1
|
|
} // namespace api
|
|
|
|
using namespace std::placeholders;
|
|
using namespace drogon;
|
|
|
|
namespace drogon
|
|
{
|
|
template <>
|
|
string_view fromRequest(const HttpRequest &req)
|
|
{
|
|
return req.body();
|
|
}
|
|
} // namespace drogon
|
|
|
|
/// Some examples in the main function show some common functions of drogon. In
|
|
/// practice, we don't need such a lengthy main function.
|
|
int main()
|
|
{
|
|
std::cout << banner << std::endl;
|
|
// app().addListener("::1", 8848); //ipv6
|
|
app().addListener("0.0.0.0", 8848);
|
|
// https
|
|
if (app().supportSSL())
|
|
{
|
|
drogon::app()
|
|
.setSSLFiles("server.pem", "server.pem")
|
|
.addListener("0.0.0.0", 8849, true);
|
|
}
|
|
// Class function example
|
|
app().registerHandler("/api/v1/handle1/{}/{}/?p3={}&p4={}", &A::handle);
|
|
app().registerHandler(
|
|
"/api/v1/handle11/{int p1}/{string p2}/?p3={string p3}&p4={int p4}",
|
|
&A::staticHandle);
|
|
// Lambda example
|
|
app().registerHandler(
|
|
"/api/v1/handle2/{int a}/{float b}",
|
|
[](const HttpRequestPtr &req,
|
|
std::function<void(const HttpResponsePtr &)> &&callback,
|
|
int a, // here the `a` parameter is converted from the number 1
|
|
// parameter in the path.
|
|
float b, // here the `b` parameter is converted from the number 2
|
|
// parameter in the path.
|
|
string_view &&body, // here the `body` parameter is converted from
|
|
// req->as<string_view>();
|
|
const std::shared_ptr<Json::Value>
|
|
&jsonPtr // here the `jsonPtr` parameter is converted from
|
|
// req->as<std::shared_ptr<Json::Value>>();
|
|
) {
|
|
HttpViewData data;
|
|
data.insert("title", std::string("ApiTest::get"));
|
|
std::unordered_map<std::string, std::string> para;
|
|
para["a"] = std::to_string(a);
|
|
para["b"] = std::to_string(b);
|
|
data.insert("parameters", para);
|
|
auto res = HttpResponse::newHttpViewResponse("ListParaView", data);
|
|
callback(res);
|
|
LOG_DEBUG << body.data();
|
|
assert(!jsonPtr);
|
|
});
|
|
|
|
// Functor example
|
|
B b;
|
|
app().registerHandler("/api/v1/handle3/{1}/{2}", b);
|
|
|
|
// API example for std::function
|
|
A tmp;
|
|
std::function<void(const HttpRequestPtr &,
|
|
std::function<void(const HttpResponsePtr &)> &&,
|
|
int,
|
|
const std::string &,
|
|
const std::string &,
|
|
int)>
|
|
func = std::bind(&A::handle, &tmp, _1, _2, _3, _4, _5, _6);
|
|
app().registerHandler("/api/v1/handle4/{4:p4}/{3:p3}/{1:p1}", func);
|
|
|
|
app().setDocumentRoot("./");
|
|
app().enableSession(60);
|
|
|
|
std::map<std::string, std::string> config_credentials;
|
|
std::string realm("drogonRealm");
|
|
std::string opaque("drogonOpaque");
|
|
// Load configuration
|
|
app().loadConfigFile("config.example.json");
|
|
auto &json = app().getCustomConfig();
|
|
if (json.empty())
|
|
{
|
|
std::cout << "empty custom config!" << std::endl;
|
|
}
|
|
else
|
|
{
|
|
if (!json["realm"].empty())
|
|
{
|
|
realm = json["realm"].asString();
|
|
}
|
|
if (!json["opaque"].empty())
|
|
{
|
|
opaque = json["opaque"].asString();
|
|
}
|
|
for (auto &&i : json["credentials"])
|
|
{
|
|
config_credentials[i["user"].asString()] = i["password"].asString();
|
|
}
|
|
}
|
|
|
|
// Install Digest Authentication Filter using custom config credentials,
|
|
// used by C HttpController (/C/priv/resource)
|
|
auto auth_filter =
|
|
std::make_shared<DigestAuthFilter>(config_credentials, realm, opaque);
|
|
app().registerFilter(auth_filter);
|
|
|
|
// Install custom controller
|
|
auto ctrlPtr = std::make_shared<CustomCtrl>("Hi");
|
|
app().registerController(ctrlPtr);
|
|
|
|
// Install custom filter
|
|
auto filterPtr =
|
|
std::make_shared<CustomHeaderFilter>("custom_header", "yes");
|
|
app().registerFilter(filterPtr);
|
|
app().setIdleConnectionTimeout(30s);
|
|
|
|
// AOP example
|
|
app().registerBeginningAdvice(
|
|
[]() { LOG_DEBUG << "Event loop is running!"; });
|
|
app().registerNewConnectionAdvice([](const trantor::InetAddress &peer,
|
|
const trantor::InetAddress &local) {
|
|
LOG_DEBUG << "New connection: " << peer.toIpPort() << "-->"
|
|
<< local.toIpPort();
|
|
return true;
|
|
});
|
|
app().registerPreRoutingAdvice([](const drogon::HttpRequestPtr &req,
|
|
drogon::AdviceCallback &&acb,
|
|
drogon::AdviceChainCallback &&accb) {
|
|
LOG_DEBUG << "preRouting1";
|
|
accb();
|
|
});
|
|
app().registerPostRoutingAdvice([](const drogon::HttpRequestPtr &req,
|
|
drogon::AdviceCallback &&acb,
|
|
drogon::AdviceChainCallback &&accb) {
|
|
LOG_DEBUG << "postRouting1";
|
|
LOG_DEBUG << "Matched path=" << req->matchedPathPatternData();
|
|
for (auto &cookie : req->cookies())
|
|
{
|
|
LOG_DEBUG << "cookie: " << cookie.first << "=" << cookie.second;
|
|
}
|
|
accb();
|
|
});
|
|
app().registerPreHandlingAdvice([](const drogon::HttpRequestPtr &req,
|
|
drogon::AdviceCallback &&acb,
|
|
drogon::AdviceChainCallback &&accb) {
|
|
LOG_DEBUG << "preHandling1";
|
|
accb();
|
|
});
|
|
app().registerPostHandlingAdvice([](const drogon::HttpRequestPtr &,
|
|
const drogon::HttpResponsePtr &resp) {
|
|
LOG_DEBUG << "postHandling1";
|
|
resp->addHeader("Access-Control-Allow-Origin", "*");
|
|
});
|
|
app().registerPreRoutingAdvice([](const drogon::HttpRequestPtr &req) {
|
|
LOG_DEBUG << "preRouting observer";
|
|
});
|
|
app().registerPostRoutingAdvice([](const drogon::HttpRequestPtr &req) {
|
|
LOG_DEBUG << "postRouting observer";
|
|
});
|
|
app().registerPreHandlingAdvice([](const drogon::HttpRequestPtr &req) {
|
|
LOG_DEBUG << "preHanding observer";
|
|
});
|
|
app().registerSyncAdvice([](const HttpRequestPtr &req) -> HttpResponsePtr {
|
|
static const HttpResponsePtr nullResp;
|
|
if (req->path() == "/plaintext")
|
|
{
|
|
auto resp = HttpResponse::newHttpResponse();
|
|
resp->setBody("Hello, World!");
|
|
resp->setContentTypeCodeAndCustomString(
|
|
CT_TEXT_PLAIN, "Content-Type: text/plain\r\n");
|
|
return resp;
|
|
}
|
|
return nullResp;
|
|
});
|
|
// Output information of all handlers
|
|
auto handlerInfo = app().getHandlersInfo();
|
|
for (auto &info : handlerInfo)
|
|
{
|
|
std::cout << std::get<0>(info);
|
|
switch (std::get<1>(info))
|
|
{
|
|
case Get:
|
|
std::cout << " (GET) ";
|
|
break;
|
|
case Post:
|
|
std::cout << " (POST) ";
|
|
break;
|
|
case Delete:
|
|
std::cout << " (DELETE) ";
|
|
break;
|
|
case Put:
|
|
std::cout << " (PUT) ";
|
|
break;
|
|
case Options:
|
|
std::cout << " (OPTIONS) ";
|
|
break;
|
|
case Head:
|
|
std::cout << " (Head) ";
|
|
break;
|
|
case Patch:
|
|
std::cout << " (PATCH) ";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
std::cout << std::get<2>(info) << std::endl;
|
|
}
|
|
auto resp = HttpResponse::newFileResponse("index.html");
|
|
resp->setExpiredTime(0);
|
|
app().setCustom404Page(resp);
|
|
std::cout << "Date: "
|
|
<< std::string{drogon::utils::getHttpFullDate(
|
|
trantor::Date::now())}
|
|
<< std::endl;
|
|
app().run();
|
|
}
|