Add the SecureSSLRedirector plugin (#306)
This commit is contained in:
parent
2784a91dcb
commit
5c1c73d9b6
|
@ -4,12 +4,6 @@ project(drogon CXX)
|
|||
|
||||
add_library(${PROJECT_NAME} STATIC)
|
||||
|
||||
#if(MSVC)
|
||||
# target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX)
|
||||
#else()
|
||||
# target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -pedantic)
|
||||
#endif()
|
||||
|
||||
set(DROGON_MAJOR_VERSION 1)
|
||||
set(DROGON_MINOR_VERSION 0)
|
||||
set(DROGON_PATCH_VERSION 0)
|
||||
|
@ -143,6 +137,7 @@ set(DROGON_SOURCES
|
|||
lib/src/MultiPart.cc
|
||||
lib/src/NotFound.cc
|
||||
lib/src/PluginsManager.cc
|
||||
lib/src/SecureSSLRedirector.cc
|
||||
lib/src/SessionManager.cc
|
||||
lib/src/SharedLibManager.cc
|
||||
lib/src/StaticFileRouter.cc
|
||||
|
@ -281,7 +276,8 @@ endif()
|
|||
|
||||
target_sources(${PROJECT_NAME} PRIVATE ${DROGON_SOURCES})
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD ${DROGON_CXX_STANDARD})
|
||||
set_target_properties(${PROJECT_NAME}
|
||||
PROPERTIES CXX_STANDARD ${DROGON_CXX_STANDARD})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD_REQUIRED ON)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_EXTENSIONS OFF)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES EXPORT_NAME Drogon)
|
||||
|
@ -414,7 +410,8 @@ set(DROGON_UTIL_HEADERS
|
|||
install(FILES ${DROGON_UTIL_HEADERS}
|
||||
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/utils)
|
||||
|
||||
set(DROGON_PLUGIN_HEADERS lib/inc/drogon/plugins/Plugin.h)
|
||||
set(DROGON_PLUGIN_HEADERS lib/inc/drogon/plugins/Plugin.h
|
||||
lib/inc/drogon/plugins/SecureSSLRedirector.h)
|
||||
install(FILES ${DROGON_PLUGIN_HEADERS}
|
||||
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/plugins)
|
||||
|
||||
|
|
|
@ -184,13 +184,14 @@
|
|||
//plugins: Define all plugins running in the application
|
||||
"plugins": [{
|
||||
//name: The class name of the plugin
|
||||
//"name": "TestPlugin",
|
||||
//"name": "drogon::plugin::SecureSSLRedirector",
|
||||
//dependencies: Plugins that the plugin depends on. It can be commented out
|
||||
"dependencies": [],
|
||||
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
||||
//It can be commented out
|
||||
"config": {
|
||||
"heartbeat_interval": 2
|
||||
"ssl_redirect_exempt": [".*\\.jpg"],
|
||||
"secure_ssl_host": "localhost:8849"
|
||||
}
|
||||
}],
|
||||
//custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
|
||||
|
|
|
@ -184,13 +184,14 @@
|
|||
//plugins: Define all plugins running in the application
|
||||
"plugins": [{
|
||||
//name: The class name of the plugin
|
||||
//"name": "TestPlugin",
|
||||
//"name": "drogon::plugin::SecureSSLRedirector",
|
||||
//dependencies: Plugins that the plugin depends on. It can be commented out
|
||||
"dependencies": [],
|
||||
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
||||
//It can be commented out
|
||||
"config": {
|
||||
"heartbeat_interval": 2
|
||||
"ssl_redirect_exempt": [".*\\.jpg"],
|
||||
"secure_ssl_host": "localhost:8849"
|
||||
}
|
||||
}],
|
||||
//custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
|
||||
|
|
|
@ -359,6 +359,8 @@ class HttpRequest
|
|||
return toRequest(std::forward<T>(obj));
|
||||
}
|
||||
|
||||
virtual bool isOnSecureConnection() const noexcept = 0;
|
||||
|
||||
virtual ~HttpRequest()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <drogon/utils/Utilities.h>
|
||||
#include <drogon/MultiPart.h>
|
||||
#include <drogon/plugins/Plugin.h>
|
||||
#include <drogon/plugins/SecureSSLRedirector.h>
|
||||
#include <drogon/Cookie.h>
|
||||
#include <drogon/Session.h>
|
||||
#include <drogon/IOThreadStorage.h>
|
||||
|
|
|
@ -22,6 +22,8 @@ namespace drogon
|
|||
{
|
||||
class HttpResponse;
|
||||
using HttpResponsePtr = std::shared_ptr<HttpResponse>;
|
||||
class HttpRequest;
|
||||
using HttpRequestPtr = std::shared_ptr<HttpRequest>;
|
||||
using AdviceCallback = std::function<void(const HttpResponsePtr &)>;
|
||||
using AdviceChainCallback = std::function<void()>;
|
||||
using FilterCallback = std::function<void(const HttpResponsePtr &)>;
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
*
|
||||
* drogon_plugin_SecureSSLRedirector.h
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <drogon/drogon_callbacks.h>
|
||||
#include <drogon/plugins/Plugin.h>
|
||||
#include <regex>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
namespace plugin
|
||||
{
|
||||
/**
|
||||
* @brief This plugin is used to redirect all non-HTTPS requests to HTTPS
|
||||
* (except for those URLs matching a regular expression listed in
|
||||
* the 'ssl_redirect_exempt' list).
|
||||
*
|
||||
* The json configuration is as follows:
|
||||
*
|
||||
* @code
|
||||
{
|
||||
"name": "drogon::plugin::SecureSSLRedirector",
|
||||
"dependencies": [],
|
||||
"config": {
|
||||
"ssl_redirect_exempt": ["^/.*\\.jpg", ...],
|
||||
"secure_ssl_host": "localhost:8849"
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*
|
||||
* ssl_redirect_exempt: a regular expression (for matching the path of a
|
||||
* request) list for URLs that don't have to be redirected.
|
||||
* secure_ssl_host: If this string is not empty, all SSL redirects
|
||||
* will be directed to this host rather than the originally-requested host.
|
||||
*
|
||||
* Enable the plugin by adding the configuration to the list of plugins in the
|
||||
* configuration file.
|
||||
*
|
||||
*/
|
||||
class SecureSSLRedirector : public drogon::Plugin<SecureSSLRedirector>
|
||||
{
|
||||
public:
|
||||
SecureSSLRedirector()
|
||||
{
|
||||
}
|
||||
/// This method must be called by drogon to initialize and start the plugin.
|
||||
/// It must be implemented by the user.
|
||||
virtual void initAndStart(const Json::Value &config) override;
|
||||
|
||||
/// This method must be called by drogon to shutdown the plugin.
|
||||
/// It must be implemented by the user.
|
||||
virtual void shutdown() override;
|
||||
|
||||
private:
|
||||
HttpResponsePtr redirectingAdvice(const HttpRequestPtr &) const;
|
||||
HttpResponsePtr redirectToSSL(const HttpRequestPtr &) const;
|
||||
|
||||
std::regex exemptPegex_;
|
||||
bool regexFlag_{false};
|
||||
std::string secureHost_;
|
||||
};
|
||||
|
||||
} // namespace plugin
|
||||
} // namespace drogon
|
|
@ -85,6 +85,10 @@ class HttpRequestImpl : public HttpRequest
|
|||
}
|
||||
|
||||
bool setMethod(const char *start, const char *end);
|
||||
void setSecure(bool secure)
|
||||
{
|
||||
isOnSecureConnection_ = secure;
|
||||
}
|
||||
|
||||
virtual void setMethod(const HttpMethod method) override
|
||||
{
|
||||
|
@ -404,6 +408,11 @@ class HttpRequestImpl : public HttpRequest
|
|||
{
|
||||
return keepAlive_;
|
||||
}
|
||||
virtual bool isOnSecureConnection() const noexcept override
|
||||
{
|
||||
return isOnSecureConnection_;
|
||||
}
|
||||
|
||||
~HttpRequestImpl();
|
||||
|
||||
protected:
|
||||
|
@ -450,6 +459,7 @@ class HttpRequestImpl : public HttpRequest
|
|||
std::unique_ptr<CacheFile> cacheFilePtr_;
|
||||
std::string expect_;
|
||||
bool keepAlive_{true};
|
||||
bool isOnSecureConnection_{false};
|
||||
|
||||
protected:
|
||||
std::string content_;
|
||||
|
|
|
@ -203,6 +203,8 @@ void HttpServer::onMessage(const TcpConnectionPtr &conn, MsgBuffer *buf)
|
|||
requestParser->requestImpl()->setLocalAddr(conn->localAddr());
|
||||
requestParser->requestImpl()->setCreationDate(
|
||||
trantor::Date::date());
|
||||
requestParser->requestImpl()->setSecure(
|
||||
conn->isSSLConnection());
|
||||
if (requestParser->firstReq() &&
|
||||
isWebSocket(requestParser->requestImpl()))
|
||||
{
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
*
|
||||
* drogon_plugin_SecureSSLRedirector.cc
|
||||
*
|
||||
*/
|
||||
#include <drogon/drogon.h>
|
||||
#include <drogon/plugins/SecureSSLRedirector.h>
|
||||
#include <string>
|
||||
|
||||
using namespace drogon;
|
||||
using namespace drogon::plugin;
|
||||
|
||||
void SecureSSLRedirector::initAndStart(const Json::Value &config)
|
||||
{
|
||||
if (config.isMember("ssl_redirect_exempt") &&
|
||||
config["ssl_redirect_exempt"].isArray())
|
||||
{
|
||||
std::string regexString;
|
||||
for (auto &exempt : config["ssl_redirect_exempt"])
|
||||
{
|
||||
assert(exempt.isString());
|
||||
regexString.append("(").append(exempt.asString()).append(")|");
|
||||
}
|
||||
if (!regexString.empty())
|
||||
{
|
||||
regexString.resize(regexString.length() - 1);
|
||||
exemptPegex_ = std::regex(regexString);
|
||||
regexFlag_ = true;
|
||||
}
|
||||
}
|
||||
secureHost_ = config.get("secure_ssl_host", "").asString();
|
||||
app().registerSyncAdvice([this](const HttpRequestPtr &req) {
|
||||
return this->redirectingAdvice(req);
|
||||
});
|
||||
}
|
||||
|
||||
void SecureSSLRedirector::shutdown()
|
||||
{
|
||||
/// Shutdown the plugin
|
||||
}
|
||||
|
||||
HttpResponsePtr SecureSSLRedirector::redirectingAdvice(
|
||||
const HttpRequestPtr &req) const
|
||||
{
|
||||
if (req->isOnSecureConnection())
|
||||
{
|
||||
return HttpResponsePtr{};
|
||||
}
|
||||
else if (regexFlag_)
|
||||
{
|
||||
std::smatch regexResult;
|
||||
if (std::regex_match(req->path(), regexResult, exemptPegex_))
|
||||
{
|
||||
return HttpResponsePtr{};
|
||||
}
|
||||
else
|
||||
{
|
||||
return redirectToSSL(req);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return redirectToSSL(req);
|
||||
}
|
||||
}
|
||||
|
||||
HttpResponsePtr SecureSSLRedirector::redirectToSSL(
|
||||
const HttpRequestPtr &req) const
|
||||
{
|
||||
if (!secureHost_.empty())
|
||||
{
|
||||
static std::string urlPrefix{"https://" + secureHost_};
|
||||
std::string query{urlPrefix + req->path()};
|
||||
if (!req->query().empty())
|
||||
{
|
||||
query += "?" + req->query();
|
||||
}
|
||||
return HttpResponse::newRedirectionResponse(query);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto &host = req->getHeader("host");
|
||||
if (!host.empty())
|
||||
{
|
||||
std::string query{"https://" + host};
|
||||
query += req->path();
|
||||
if (!req->query().empty())
|
||||
{
|
||||
query += "?" + req->query();
|
||||
}
|
||||
return HttpResponse::newRedirectionResponse(query);
|
||||
}
|
||||
else
|
||||
{
|
||||
return HttpResponse::newNotFoundResponse();
|
||||
}
|
||||
}
|
||||
}
|
2
trantor
2
trantor
|
@ -1 +1 @@
|
|||
Subproject commit 374acaa8aa39aa7a1a2c2d92dbb2c0d940e0f33b
|
||||
Subproject commit f36e2e46a7bfa404e730ec36cf223c75f6fbe34a
|
Loading…
Reference in New Issue