Add the forward() method to the framework

This commit is contained in:
antao 2019-05-09 16:26:50 +08:00
parent 93388d943f
commit 3e1886a84e
11 changed files with 131 additions and 16 deletions

View File

@ -0,0 +1,8 @@
#include "ForwardCtrl.h"
void ForwardCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback)
{
req->setPath("/repos/an-tao/drogon/git/refs/heads/master");
app().forward(req, [callback = std::move(callback)](const HttpResponsePtr &resp) {
callback(resp);
},"https://api.github.com");
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <drogon/HttpSimpleController.h>
using namespace drogon;
class ForwardCtrl:public drogon::HttpSimpleController<ForwardCtrl>
{
public:
virtual void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
PATH_LIST_BEGIN
//list path definitions here;
PATH_ADD("/forward",Get);
PATH_LIST_END
};

View File

@ -379,6 +379,25 @@ public:
DrClassMap::setSingleInstance(filterPtr);
}
/// Forward the http request
/**
* The @param hostString is the address where the request is forwarded. The following strings are
* valid for the @param hostString:
*
* https://www.baidu.com
* http://www.baidu.com
* https://127.0.0.1:8080/
* http://127.0.0.1
* http://[::1]:8080/
*
* NOTE:
* If the @param hostString is empty, the request is handled by the same application, so in this condition
* one should modify the path of the @param req before forwarding to avoid infinite loop processing.
*
* This method can be used to implement reverse proxy or redirection on the server side.
*/
virtual void forward(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string &hostString = "") = 0;
///Get information about the handlers registered to drogon
/**
* The first item of std::tuple in the return value represents the path pattern of the handler;

View File

@ -70,6 +70,10 @@ public:
virtual const std::string &getHeader(const std::string &key, const std::string &defaultVal = std::string()) const = 0;
virtual const std::string &getHeader(std::string &&key, const std::string &defaultVal = std::string()) const = 0;
/// Remove the header identified by the @param key.
virtual void removeHeader(const std::string &key) = 0;
virtual void removeHeader(std::string &&key) = 0;
/// Get all headers of the response
virtual const std::unordered_map<std::string, std::string> &headers() const = 0;
const std::unordered_map<std::string, std::string> &getHeaders() const { return headers(); }
@ -132,7 +136,7 @@ public:
/// For more details, see the wiki pages, the "View" section.
static HttpResponsePtr newHttpViewResponse(const std::string &viewName, const HttpViewData &data = HttpViewData());
/// Create a response that returns a 302 Found page, redirecting to another page located in the @param location.
static HttpResponsePtr newLocationResponse(const std::string &location);
static HttpResponsePtr newRedirectionResponse(const std::string &location);
/// Create a response that returns a file to the client.
/**
* @param fullPath is the full path to the file.

View File

@ -918,3 +918,46 @@ void HttpAppFrameworkImpl::createDbClient(const std::string &dbType,
}
}
#endif
void HttpAppFrameworkImpl::forward(const HttpRequestImplPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string &hostString)
{
if (hostString.empty())
{
onAsyncRequest(req, std::move(callback));
}
else
{
/// A tiny implementation of a reverse proxy.
static std::unordered_map<std::string, HttpClientImplPtr> clientsMap;
HttpClientImplPtr clientPtr;
static std::mutex mtx;
{
std::lock_guard<std::mutex> lock(mtx);
auto iter = clientsMap.find(hostString);
if (iter != clientsMap.end())
{
clientPtr = iter->second;
}
else
{
clientPtr = std::make_shared<HttpClientImpl>(trantor::EventLoop::getEventLoopOfCurrentThread() ? trantor::EventLoop::getEventLoopOfCurrentThread() : getLoop(),
hostString);
clientsMap[hostString] = clientPtr;
}
}
clientPtr->sendRequest(req, [callback = std::move(callback)](ReqResult result, const HttpResponsePtr &resp) {
if (result == ReqResult::Ok)
{
resp->removeHeader("server");
resp->removeHeader("date");
resp->removeHeader("content-length");
resp->removeHeader("transfer-encoding");
callback(resp);
}
else
{
callback(HttpResponse::newNotFoundResponse());
}
});
}
}

View File

@ -90,12 +90,19 @@ public:
resp->setStatusCode(k404NotFound);
_custom404 = resp;
}
const HttpResponsePtr &getCustom404Page()
{
return _custom404;
}
virtual void forward(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string &hostString = "") override
{
forward(std::dynamic_pointer_cast<HttpRequestImpl>(req), std::move(callback), hostString);
}
void forward(const HttpRequestImplPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string &hostString);
virtual void registerBeginningAdvice(const std::function<void()> &advice) override
{
getLoop()->runInLoop(advice);

View File

@ -306,20 +306,16 @@ void HttpClientImpl::onRecvMessage(const trantor::TcpConnectionPtr &connPtr, tra
{
auto resp = responseParser->responseImpl();
responseParser->reset();
assert(!_pipeliningCallbacks.empty());
auto &type = resp->getHeaderBy("content-type");
if (type.find("application/json") != std::string::npos)
{
resp->parseJson();
}
if (resp->getHeaderBy("content-encoding") == "gzip")
{
resp->gunzip();
}
if (type.find("application/json") != std::string::npos)
{
resp->parseJson();
}
auto cb = std::move(_pipeliningCallbacks.front());
_pipeliningCallbacks.pop();
cb(ReqResult::Ok, resp);

View File

@ -50,4 +50,5 @@ class HttpClientImpl : public HttpClient, public std::enable_shared_from_this<Ht
std::string _domain;
size_t _pipeliningDepth = 0;
};
typedef std::shared_ptr<HttpClientImpl> HttpClientImplPtr;
} // namespace drogon

View File

@ -24,12 +24,15 @@ namespace drogon
{
static void doWhenNoHandlerFound(const HttpRequestImplPtr &req,
const std::function<void(const HttpResponsePtr &)> &callback)
std::function<void(const HttpResponsePtr &)> &&callback)
{
if (req->path() == "/" && !HttpAppFrameworkImpl::instance().getHomePage().empty())
{
auto resp = drogon::HttpResponse::newLocationResponse("/" + HttpAppFrameworkImpl::instance().getHomePage());
callback(resp);
// auto resp = drogon::HttpResponse::newRedirectionResponse("/" + HttpAppFrameworkImpl::instance().getHomePage());
// callback(resp);
// Redirect on the server side
req->setPath("/" + HttpAppFrameworkImpl::instance().getHomePage());
drogon::app().forward(req, std::move(callback));
return;
}
auto resp = drogon::HttpResponse::newNotFoundResponse();
@ -282,13 +285,13 @@ void HttpControllersRouter::route(const HttpRequestImplPtr &req,
else
{
//No handler found
doWhenNoHandlerFound(req, callback);
doWhenNoHandlerFound(req, std::move(callback));
}
}
else
{
//No handler found
doWhenNoHandlerFound(req, callback);
doWhenNoHandlerFound(req, std::move(callback));
}
}

View File

@ -66,7 +66,7 @@ HttpResponsePtr HttpResponse::newNotFoundResponse()
return notFoundResp;
}
}
HttpResponsePtr HttpResponse::newLocationResponse(const std::string &location)
HttpResponsePtr HttpResponse::newRedirectionResponse(const std::string &location)
{
auto res = std::make_shared<HttpResponseImpl>();
res->setStatusCode(k302Found);

View File

@ -122,6 +122,19 @@ public:
return getHeaderBy(key, defaultVal);
}
virtual void removeHeader(const std::string &key) override
{
auto field = key;
transform(field.begin(), field.end(), field.begin(), ::tolower);
removeHeaderBy(field);
}
virtual void removeHeader(std::string &&key) override
{
transform(key.begin(), key.end(), key.begin(), ::tolower);
removeHeaderBy(key);
}
virtual const std::unordered_map<std::string, std::string> &headers() const override
{
return _headers;
@ -137,6 +150,11 @@ public:
return iter->second;
}
void removeHeaderBy(const std::string &lowerKey)
{
_headers.erase(lowerKey);
}
virtual void addHeader(const std::string &key, const std::string &value) override
{
_fullHeaderString.reset();
@ -346,6 +364,7 @@ public:
if (!reader->parse(_bodyPtr->data(), _bodyPtr->data() + _bodyPtr->size(), _jsonPtr.get(), &errs))
{
LOG_ERROR << errs;
LOG_DEBUG << "body: " << *_bodyPtr;
_jsonPtr.reset();
}
}
@ -375,7 +394,10 @@ public:
{
auto gunzipBody = utils::gzipDecompress(_bodyPtr);
if (gunzipBody)
{
removeHeader("content-encoding");
_bodyPtr = gunzipBody;
}
}
protected: