Add the forward() method to the framework
This commit is contained in:
parent
93388d943f
commit
3e1886a84e
|
@ -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");
|
||||
}
|
|
@ -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
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue