drogon/lib/inc/drogon/HttpAppFramework.h

1659 lines
62 KiB
C++

/**
*
* @file HttpAppFramework.h
* @author An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#pragma once
#ifdef __cpp_impl_coroutine
#include <drogon/utils/coroutine.h>
#endif
#include <drogon/exports.h>
#include <drogon/utils/HttpConstraint.h>
#include <drogon/CacheMap.h>
#include <drogon/DrObject.h>
#include <drogon/HttpBinder.h>
#include <drogon/HttpFilter.h>
#include <drogon/MultiPart.h>
#include <drogon/NotFound.h>
#include <drogon/drogon_callbacks.h>
#include <drogon/utils/Utilities.h>
#include <drogon/plugins/Plugin.h>
#include <drogon/HttpRequest.h>
#include <drogon/HttpResponse.h>
#include <drogon/orm/DbClient.h>
#include <drogon/orm/DbConfig.h>
#include <drogon/nosql/RedisClient.h>
#include <drogon/Cookie.h>
#include <trantor/net/Resolver.h>
#include <trantor/net/EventLoop.h>
#include <trantor/utils/NonCopyable.h>
#include <functional>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
#include <chrono>
namespace drogon
{
// the drogon banner
const char banner[] =
" _ \n"
" __| |_ __ ___ __ _ ___ _ __ \n"
" / _` | '__/ _ \\ / _` |/ _ \\| '_ \\ \n"
"| (_| | | | (_) | (_| | (_) | | | |\n"
" \\__,_|_| \\___/ \\__, |\\___/|_| |_|\n"
" |___/ \n";
DROGON_EXPORT std::string getVersion();
DROGON_EXPORT std::string getGitCommit();
class HttpControllerBase;
class HttpSimpleControllerBase;
class WebSocketControllerBase;
using ExceptionHandler =
std::function<void(const std::exception &,
const HttpRequestPtr &,
std::function<void(const HttpResponsePtr &)> &&)>;
using DefaultHandler =
std::function<void(const HttpRequestPtr &,
std::function<void(const HttpResponsePtr &)> &&)>;
using HttpHandlerInfo = std::tuple<std::string, HttpMethod, std::string>;
#ifdef __cpp_impl_coroutine
class HttpAppFramework;
namespace internal
{
struct [[nodiscard]] ForwardAwaiter
: public CallbackAwaiter<drogon::HttpResponsePtr>
{
public:
ForwardAwaiter(drogon::HttpRequestPtr &&req,
std::string &&host,
double timeout,
HttpAppFramework &app)
: req_(std::move(req)),
host_(std::move(host)),
timeout_(timeout),
app_(app)
{
}
void await_suspend(std::coroutine_handle<> handle) noexcept;
private:
drogon::HttpRequestPtr req_;
std::string host_;
double timeout_;
HttpAppFramework &app_;
};
} // namespace internal
#endif
class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
{
public:
virtual ~HttpAppFramework() = default;
/// Get the instance of HttpAppFramework
/**
* HttpAppFramework works at singleton mode, so any calling of this
* method gets the same instance;
* Calling drogon::HttpAppFramework::instance()
* can be replaced by a simple interface -- drogon::app()
*/
static HttpAppFramework &instance();
/// Run the event loop
/**
* Calling this method starts the IO event loops and the main loop of the
* application;
* This method can be called in the main thread or any other thread.
* This method blocks the current thread until the main event loop exits.
*/
virtual void run() = 0;
/// Return true if the framework is running
virtual bool isRunning() = 0;
/// Quit the event loop
/**
* Calling this method results in stopping all network IO in the
* framework and interrupting the blocking of the run() method. Usually,
* after calling this method, the application exits (when the run() method
* is called in the main thread).
*
* @note
* This method can be called in any thread and anywhere.
* This method should not be called before calling run().
*/
virtual void quit() = 0;
/// Get the main event loop of the framework;
/**
* @note
* The event loop is not the network IO loop, but the main event loop
* of the framework in which only some timer tasks are running;
* User can run some timer tasks or other tasks in this loop;
* This method can be call in any thread.
*/
virtual trantor::EventLoop *getLoop() const = 0;
/// Get an IO loop with id. E.g. 0 <= id < \#Total thread-loops
/**
* @note
* The event loop is one of the network IO loops. Use the loop
* for events/actions rather then the main thread.
* REMARKS : Function assumed the number of threads will not exceed 2^32.
* Change to long long for alien computers.
*/
virtual trantor::EventLoop *getIOLoop(size_t id) const = 0;
/// Set custom 404 page
/**
* @param resp is the object set to 404 response
* After calling this method, the resp object is returned
* by the HttpResponse::newNotFoundResponse() method.
* @param set404 if true, the status code of the resp will
* be set to 404 automatically
*/
virtual HttpAppFramework &setCustom404Page(const HttpResponsePtr &resp,
bool set404 = true) = 0;
/// Set custom error handler
/**
* @param resp_generator is invoked when an error in the framework needs to
* be sent to the client to provide a custom layout.
*/
virtual HttpAppFramework &setCustomErrorHandler(
std::function<HttpResponsePtr(HttpStatusCode,
const HttpRequestPtr &req)>
&&resp_generator) = 0;
HttpAppFramework &setCustomErrorHandler(
std::function<HttpResponsePtr(HttpStatusCode)> &&resp_generator)
{
return setCustomErrorHandler(
[cb = std::move(resp_generator)](HttpStatusCode code,
const HttpRequestPtr &) {
return cb(code);
});
}
/// Get custom error handler
/**
* @return A const-reference to the error handler set using
* setCustomErrorHandler. If none was provided, the default error handler is
* returned.
*/
virtual const std::function<HttpResponsePtr(HttpStatusCode,
const HttpRequestPtr &req)> &
getCustomErrorHandler() const = 0;
/// Get the plugin object registered in the framework
/**
* @note
* This method is usually called after the framework runs.
* Calling this method in the initAndStart() method of plugins is also
* valid.
*/
template <typename T>
T *getPlugin()
{
static_assert(IsPlugin<T>::value,
"The Template parameter must be a subclass of "
"PluginBase");
assert(isRunning());
static auto pluginPtr =
dynamic_cast<T *>(getPlugin(T::classTypeName()));
return pluginPtr;
}
/// Get the shared_ptr plugin object registered in the framework
/**
* @note
* This method is usually called after the framework runs.
* Calling this method in the initAndStart() method of plugins is also
* valid.
*/
template <typename T>
std::shared_ptr<T> getSharedPlugin()
{
static_assert(IsPlugin<T>::value,
"The Template parameter must be a subclass of "
"PluginBase");
assert(isRunning());
static auto pluginPtr =
std::dynamic_pointer_cast<T>(getSharedPlugin(T::classTypeName()));
return pluginPtr;
}
/// @brief the plugin object registered in the framework
/**
* @param name is the class name of the plugin.
*
* @note
* This method is usually called after the framework runs.
* Calling this method in the initAndStart() method of plugins is also
* valid.
*/
virtual PluginBase *getPlugin(const std::string &name) = 0;
/**
* @brief Get the shared_ptr plugin object registered in the framework
*
* @note
* This method is usually called after the framework runs.
* Calling this method in the initAndStart() method of plugins is also
* valid.
*/
virtual std::shared_ptr<PluginBase> getSharedPlugin(
const std::string &name) = 0;
/* The following is a series of methods of AOP */
/// Register a beginning advice
/**
* @param advice is called immediately after the main event loop runs.
*/
virtual HttpAppFramework &registerBeginningAdvice(
const std::function<void()> &advice) = 0;
/// Register an advice for new connections
/**
* @param advice is called immediately when a new connection is
* established. the first parameter of it is the remote address of the new
* connection, the second one is the local address of it.
* If the advice returns a false value, drogon closes the connection.
* Users can use this advice to implement some security policies.
*/
virtual HttpAppFramework &registerNewConnectionAdvice(
const std::function<bool(const trantor::InetAddress &,
const trantor::InetAddress &)> &advice) = 0;
/**
* @brief Register an advice for new HTTP responses.
*
* @param advice is called immediately when a new HTTP response is created.
* Users can use the callback to modify the response if they want.
* @note This advice is called before any subsequent operation on the
* response is performed by drogon or applications, so some modification
* (e.g. modification on the status code) in this callback may be override
* by subsequent operations.
* @return HttpAppFramework&
*/
virtual HttpAppFramework &registerHttpResponseCreationAdvice(
const std::function<void(const HttpResponsePtr &)> &advice) = 0;
/// Register a synchronous advice
/**
* @param advice is called immediately after the request is created. If a
* no-empty response is returned by the advice, it is sent to the client and
* no handler is invoked.
*
* @note The following diagram shows the location of the
* AOP join points during http request processing.
*
* @code
+-----------+ +----------+
| Request | | Response |
+-----------+ +----------+
| ^
v |
sync join point o----------->[HttpResponsePtr]----------->+
| |
v |
Pre-routing join point o----------->[Advice callback]----------->+
| |
v Invalid path |
[Find Handler]---------------->[404]----------->+
| |
v |
Post-routing join point o----------->[Advice callback]----------->+
| |
v Invalid method |
[Check Method]---------------->[405]----------->+
| |
v |
[Filters/Middlewares]------>[Filter callback]------>+
| |
v Y |
[Is OPTIONS method?]------------->[200]----------->+
| |
v |
Pre-handling join point o----------->[Advice callback]----------->+
| |
v |
[Handler] |
| |
v |
Post-handling join point o---------------------------------------->+
| |
v |
[Middlewares post logic]--->[Middleware callback]--->+
@endcode
*
*/
virtual HttpAppFramework &registerSyncAdvice(
const std::function<HttpResponsePtr(const HttpRequestPtr &)>
&advice) = 0;
/// Register an advice called before routing
/**
* @param advice is called after all the synchronous advices return
* nullptr and before the request is routed to any handler. The parameters
* of the advice are same as those of the doFilter method of the Filter
* class.
*/
virtual HttpAppFramework &registerPreRoutingAdvice(
const std::function<void(const HttpRequestPtr &,
AdviceCallback &&,
AdviceChainCallback &&)> &advice) = 0;
/// Register an observer called before routing
/**
* @param advice is called at the same time as the above advice. It can be
* thought of as an observer who cannot respond to http requests.
* @note This advice has less overhead than the above one. If one does not
* intend to intercept the http request, please use this interface.
*/
virtual HttpAppFramework &registerPreRoutingAdvice(
const std::function<void(const HttpRequestPtr &)> &advice) = 0;
/// Register an advice called after routing
/**
* @param advice is called immediately after the request matches a handler
* path and before any filters/middlewares applies. The parameters
* of the advice are same as those of the doFilter method of the Filter
* class.
*/
virtual HttpAppFramework &registerPostRoutingAdvice(
const std::function<void(const HttpRequestPtr &,
AdviceCallback &&,
AdviceChainCallback &&)> &advice) = 0;
/// Register an observer called after routing
/**
* @param advice is called at the same time as the above advice. It can be
* thought of as an observer who cannot respond to http requests.
* @note This advice has less overhead than the above one. If one does not
* intend to intercept the http request, please use this interface.
*/
virtual HttpAppFramework &registerPostRoutingAdvice(
const std::function<void(const HttpRequestPtr &)> &advice) = 0;
/// Register an advice called before the request is handled
/**
* @param advice is called immediately after the request is approved by all
* filters/middlewares and before it is handled. The parameters of the
* advice are same as those of the doFilter method of the Filter class.
*/
virtual HttpAppFramework &registerPreHandlingAdvice(
const std::function<void(const HttpRequestPtr &,
AdviceCallback &&,
AdviceChainCallback &&)> &advice) = 0;
/// Register an observer called before the request is handled
/**
* @param advice is called at the same time as the above advice. It can be
* thought of as an observer who cannot respond to http requests. This
* advice has less overhead than the above one. If one does not intend to
* intercept the http request, please use this interface.
*/
virtual HttpAppFramework &registerPreHandlingAdvice(
const std::function<void(const HttpRequestPtr &)> &advice) = 0;
/// Register an advice called after the request is handled
/**
* @param advice is called immediately after the request is handled and
* a response object is created by handlers.
*/
virtual HttpAppFramework &registerPostHandlingAdvice(
const std::function<void(const HttpRequestPtr &,
const HttpResponsePtr &)> &advice) = 0;
/// Register an advice called before a response is sent to the client.
/**
* @note This advice is different from the PostHandlingAdvice, responses to
* static resources are also handled here.
*/
virtual HttpAppFramework &registerPreSendingAdvice(
const std::function<void(const HttpRequestPtr &,
const HttpResponsePtr &)> &advice) = 0;
/// Setup output of logs to files
/**
* @note
* Logs are output to the standard output by default.
* Logging is setuped only if output path of logs is defined.
* This method is called in run() function, hence use this method only if
* you want to setup logging earlier.
* @return HttpAppFramework&
*/
virtual HttpAppFramework &setupFileLogger() = 0;
/* End of AOP methods */
/// Load the configuration file with json format.
/**
* @param fileName the configuration file
*/
virtual HttpAppFramework &loadConfigFile(
const std::string &fileName) noexcept(false) = 0;
/// Load the configuration from a Json::Value Object.
/**
* @param data Json::Value Object containing the configuration.
* @note Please refer to the configuration file for the content of the json
* object.
*/
virtual HttpAppFramework &loadConfigJson(const Json::Value &data) noexcept(
false) = 0;
/// Load the configuration from a Json::Value Object.
/**
* @param data rvalue reference to a Json::Value object containing the
* configuration.
* @note Please refer to the configuration file for the content of the json
* object.
*/
virtual HttpAppFramework &loadConfigJson(Json::Value &&data) noexcept(
false) = 0;
/// Register a HttpSimpleController object into the framework.
/**
* @param pathName When the path of a http request is equal to the
* pathName, the asyncHandleHttpRequest() method of the controller is
* called.
* @param ctrlName is the name of the controller. It includes the namespace
* to which the controller belongs.
* @param constraints is a vector containing Http methods or middleware
names
*
* Example:
* @code
app.registerHttpSimpleController("/userinfo","UserInfoCtrl",{Get,"LoginFilter"});
@endcode
*
* @note
* Users can perform the same operation through the configuration file or a
* macro in the header file.
*/
virtual HttpAppFramework &registerHttpSimpleController(
const std::string &pathName,
const std::string &ctrlName,
const std::vector<internal::HttpConstraint> &constraints = {}) = 0;
/// Register a handler into the framework.
/**
* @param pathPattern When the path of a http request matches the
* pathPattern, the handler indicated by the function parameter is called.
* @param function indicates any type of callable object with a valid
* processing interface.
* @param constraints is the same as the third parameter in the above
* method.
*
* Example:
* @code
app().registerHandler("/hello?username={1}",
[](const HttpRequestPtr& req,
std::function<void (const HttpResponsePtr&)>
&&callback, const std::string &name)
{
Json::Value json;
json["result"]="ok";
json["message"]=std::string("hello,")+name;
auto
resp=HttpResponse::newHttpJsonResponse(json); callback(resp);
},
{Get,"LoginFilter"});
@endcode
* @note
* As you can see in the above example, this method supports parameters
* mapping.
*/
template <typename FUNCTION>
HttpAppFramework &registerHandler(
const std::string &pathPattern,
FUNCTION &&function,
const std::vector<internal::HttpConstraint> &constraints = {},
const std::string &handlerName = "")
{
LOG_TRACE << "pathPattern:" << pathPattern;
auto binder = std::make_shared<internal::HttpBinder<FUNCTION>>(
std::forward<FUNCTION>(function));
getLoop()->queueInLoop([binder]() { binder->createHandlerInstance(); });
std::vector<HttpMethod> validMethods;
std::vector<std::string> middlewares;
for (auto const &constraint : constraints)
{
if (constraint.type() == internal::ConstraintType::HttpMiddleware)
{
middlewares.push_back(constraint.getMiddlewareName());
}
else if (constraint.type() == internal::ConstraintType::HttpMethod)
{
validMethods.push_back(constraint.getHttpMethod());
}
else
{
LOG_ERROR << "Invalid controller constraint type";
exit(1);
}
}
registerHttpController(
pathPattern, binder, validMethods, middlewares, handlerName);
return *this;
}
/**
* @brief Register a handler into the framework via a regular expression.
*
* @param regExp A regular expression string, when the path of a http
* request matches the regular expression, the handler indicated by the
* function parameter is called.
* @note When the match is successful, Each string that matches a
* subexpression is sequentially mapped to a handler parameter.
* @param function indicates any type of callable object with a valid
* processing interface.
* @param constraints is the same as the third parameter in the
* above method.
* @param handlerName a name for the handler.
* @return HttpAppFramework&
*/
template <typename FUNCTION>
HttpAppFramework &registerHandlerViaRegex(
const std::string &regExp,
FUNCTION &&function,
const std::vector<internal::HttpConstraint> &constraints = {},
const std::string &handlerName = "")
{
LOG_TRACE << "regex:" << regExp;
internal::HttpBinderBasePtr binder;
binder = std::make_shared<internal::HttpBinder<FUNCTION>>(
std::forward<FUNCTION>(function));
std::vector<HttpMethod> validMethods;
std::vector<std::string> middlewares;
for (auto const &constraint : constraints)
{
if (constraint.type() == internal::ConstraintType::HttpMiddleware)
{
middlewares.push_back(constraint.getMiddlewareName());
}
else if (constraint.type() == internal::ConstraintType::HttpMethod)
{
validMethods.push_back(constraint.getHttpMethod());
}
else
{
LOG_ERROR << "Invalid controller constraint type";
exit(1);
}
}
registerHttpControllerViaRegex(
regExp, binder, validMethods, middlewares, handlerName);
return *this;
}
/// Register a WebSocketController into the framework.
/**
* The parameters of this method are the same as those in the
* registerHttpSimpleController() method.
*/
virtual HttpAppFramework &registerWebSocketController(
const std::string &pathName,
const std::string &ctrlName,
const std::vector<internal::HttpConstraint> &constraints = {}) = 0;
/// Register a WebSocketController into the framework.
/**
* The parameters of this method are the same as those in the
* registerHttpSimpleController() method but using regular
* expression string for path.
*/
virtual HttpAppFramework &registerWebSocketControllerRegex(
const std::string &regExp,
const std::string &ctrlName,
const std::vector<internal::HttpConstraint> &constraints =
std::vector<internal::HttpConstraint>{}) = 0;
/// Register controller objects created and initialized by the user
/**
* @details Drogon can only automatically create controllers using the
* default constructor. Sometimes users want to be able to create
* controllers using constructors with parameters. Controllers created by
* user in this way should be registered to the framework via this method.
* The macro or configuration file is still valid for the path routing
* configuration of the controller created by users.
*
* @note
* The declaration of the controller class must be as follows:
* @code
class ApiTest : public drogon::HttpController<ApiTest, false>
{
public:
ApiTest(const std::string &str);
...
};
@endcode
* The second template parameter must be explicitly set to false to disable
* automatic creation.
* And then user can create and register it somewhere as follows:
* @code
auto ctrlPtr=std::make_shared<ApiTest>("hello world");
drogon::app().registerController(ctrlPtr);
@endcode
* This method should be called before calling the app().run() method.
*/
template <typename T>
HttpAppFramework &registerController(const std::shared_ptr<T> &ctrlPtr)
{
static_assert((std::is_base_of<HttpControllerBase, T>::value ||
std::is_base_of<HttpSimpleControllerBase, T>::value ||
std::is_base_of<WebSocketControllerBase, T>::value),
"Error! Only controller objects can be registered here");
static_assert(!T::isAutoCreation,
"Controllers created and initialized "
"automatically by drogon cannot be "
"registered here");
DrClassMap::setSingleInstance(ctrlPtr);
T::initPathRouting();
return *this;
}
/// Register filter objects created and initialized by the user
/**
* This method is similar to the above method.
*/
template <typename T>
HttpAppFramework &registerFilter(const std::shared_ptr<T> &filterPtr)
{
static_assert(std::is_base_of<HttpFilterBase, T>::value,
"Error! Only filter objects can be registered here");
static_assert(!T::isAutoCreation,
"Filters created and initialized "
"automatically by drogon cannot be "
"registered here");
DrClassMap::setSingleInstance(filterPtr);
return *this;
}
/// Register middleware objects created and initialized by the user
/**
* This method is similar to the above method.
*/
template <typename T>
HttpAppFramework &registerMiddleware(
const std::shared_ptr<T> &middlewarePtr)
{
static_assert(std::is_base_of<HttpMiddlewareBase, T>::value,
"Error! Only middleware objects can be registered here");
static_assert(!T::isAutoCreation,
"Middleware created and initialized "
"automatically by drogon cannot be "
"registered here");
DrClassMap::setSingleInstance(middlewarePtr);
return *this;
}
/// Register a default handler into the framework when no handler matches
/// the request. If set, it is executed if the static file router does
/// not find any file corresponding to the request. Thus it replaces
/// the default 404 not found response.
/**
* @param handler function indicates any type of callable object with
* a valid processing interface.
*/
virtual HttpAppFramework &setDefaultHandler(DefaultHandler handler) = 0;
/// Forward the http request
/**
* @param req the HTTP request to be forwarded;
* @param hostString is the address where the request is forwarded. The
* following strings are valid for the parameter:
*
* @code
https://www.baidu.com
http://www.baidu.com
https://127.0.0.1:8080/
http://127.0.0.1
http://[::1]:8080/
@endcode
*
* @param timeout See the timeout parameter of the sendRequest method of the
* HttpClient class. this parameter is only valid when the hostString is not
* empty.
* @param callback is called when the response is created.
*
* @note
* If the hostString parameter is empty, the request is handled by the same
* application, so in this condition
* one should modify the path of the req parameter 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 = "",
double timeout = 0) = 0;
#ifdef __cpp_impl_coroutine
/**
* @brief Forward the http request, this is the coroutine version of the
* above method.
*/
internal::ForwardAwaiter forwardCoro(HttpRequestPtr req,
std::string hostString = "",
double timeout = 0)
{
return internal::ForwardAwaiter(std::move(req),
std::move(hostString),
timeout,
*this);
}
#endif
/// Get information about the handlers registered to drogon
/**
* @return
* The first item of std::tuple in the return value represents the path
* pattern of the handler;
* The last item in std::tuple is the description of the handler.
*/
virtual std::vector<HttpHandlerInfo> getHandlersInfo() const = 0;
/// Get the custom configuration defined by users in the configuration file.
virtual const Json::Value &getCustomConfig() const = 0;
/// Set the number of threads for IO event loops
/**
* @param threadNum the number of threads
* The default value is 1, if the parameter is 0, the number is equal to
* the number of CPU cores.
*
* @note
* This number is usually less than or equal to the number of CPU cores.
* This number can be configured in the configuration file.
*/
virtual HttpAppFramework &setThreadNum(size_t threadNum) = 0;
/// Get the number of threads for IO event loops
virtual size_t getThreadNum() const = 0;
/// Set the global cert file and private key file for https
/// These options can be configured in the configuration file.
virtual HttpAppFramework &setSSLFiles(const std::string &certPath,
const std::string &keyPath) = 0;
/// Supplies file style SSL options to `SSL_CONF_cmd`. Valid options are
/// available at
/// https://www.openssl.org/docs/manmaster/man3/SSL_CONF_cmd.html
virtual HttpAppFramework &setSSLConfigCommands(
const std::vector<std::pair<std::string, std::string>>
&sslConfCmds) = 0;
/// Reload the global cert file and private key file for https server
/// Note: The goal of this method is not to make the framework
/// use the new SSL path, but rather to reload the new content
/// from the old path while the framework is still running.
/// Typically, when our SSL is about to expire,
/// we need to reload the SSL. The purpose of this function
/// is to use the new SSL certificate without stopping the framework.
virtual HttpAppFramework &reloadSSLFiles() = 0;
/// Add plugins
/**
* @param configs The plugins array
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual void addPlugins(const Json::Value &configs) = 0;
/// Add a plugin
/**
* @param name Name of the plugin
* @param dependencies Names of plugins this plugin depends on
* @param config Custom config for the plugin
*/
virtual void addPlugin(const std::string &name,
const std::vector<std::string> &dependencies,
const Json::Value &config) = 0;
/// Add a listener for http or https service
/**
* @param ip is the ip that the listener listens on.
* @param port is the port that the listener listens on.
* @param useSSL if the parameter is true, the listener is used for the
* https service.
* @param certFile
* @param keyFile specify the cert file and the private key file for this
* listener. If they are empty, the global configuration set by the above
* method is used.
* @param useOldTLS if true, the TLS1.0/1.1 are enabled for HTTPS
* connections.
* @param sslConfCmds vector of ssl configuration key/value pairs.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &addListener(
const std::string &ip,
uint16_t port,
bool useSSL = false,
const std::string &certFile = "",
const std::string &keyFile = "",
bool useOldTLS = false,
const std::vector<std::pair<std::string, std::string>> &sslConfCmds =
{}) = 0;
/// Enable sessions supporting.
/**
* @param timeout The number of seconds which is the timeout of a session
* @param sameSite The default value of SameSite attribute
* @param cookieKey The key of the session cookie
*
* @note
* Session support is disabled by default.
* If there isn't any request from a client for timeout(>0) seconds,
* the session of the client is destroyed.
* If the timeout parameter is equal to 0, sessions will remain permanently
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &enableSession(
const size_t timeout = 0,
Cookie::SameSite sameSite = Cookie::SameSite::kNull,
const std::string &cookieKey = "JSESSIONID",
int maxAge = -1,
std::function<std::string()> idGeneratorCallback = nullptr) = 0;
/// A wrapper of the above method.
/**
* Example: Users can set the timeout value as follows:
* @code
app().enableSession(0.2h);
app().enableSession(12min);
@endcode
*/
inline HttpAppFramework &enableSession(
const std::chrono::duration<double> &timeout,
Cookie::SameSite sameSite = Cookie::SameSite::kNull,
const std::string &cookieKey = "JSESSIONID",
int maxAge = -1,
std::function<std::string()> idGeneratorCallback = nullptr)
{
return enableSession((size_t)timeout.count(),
sameSite,
cookieKey,
maxAge,
idGeneratorCallback);
}
/// Register an advice called when starting a new session.
/**
* @param advice is called with the session id.
*/
virtual HttpAppFramework &registerSessionStartAdvice(
const AdviceStartSessionCallback &advice) = 0;
/// Register an advice called when destroying a session.
/**
* @param advice is called with the session id.
*/
virtual HttpAppFramework &registerSessionDestroyAdvice(
const AdviceDestroySessionCallback &advice) = 0;
/// Disable sessions supporting.
/**
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &disableSession() = 0;
/// Set the root path of HTTP document, default path is ./
/**
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setDocumentRoot(const std::string &rootPath) = 0;
/// Get the document root directory.
virtual const std::string &getDocumentRoot() const = 0;
/**
* @brief Set the Static File Headers
*
* @param headers Each pair object in the vector presents the field name and
* field value of a header in an static file response.
*/
virtual HttpAppFramework &setStaticFileHeaders(
const std::vector<std::pair<std::string, std::string>> &headers) = 0;
/**
* @brief Add a location of static files for GET requests.
*
* @param uriPrefix The URI prefix of the location prefixed with "/"
* @param defaultContentType The default content type of the static files
* without an extension.
* @param alias The location in file system, if it is prefixed with "/", it
* presents an absolute path, otherwise it presents a relative path to the
* document_root path.
* @param isCaseSensitive
* @param allowAll If it is set to false, only static files with a valid
* extension can be accessed.
* @param isRecursive If it is set to false, files in sub directories can't
* be accessed.
* @param middlewareNames The list of middlewares which acting on the
* location.
* @return HttpAppFramework&
*/
virtual HttpAppFramework &addALocation(
const std::string &uriPrefix,
const std::string &defaultContentType = "",
const std::string &alias = "",
bool isCaseSensitive = false,
bool allowAll = true,
bool isRecursive = true,
const std::vector<std::string> &middlewareNames = {}) = 0;
/// Set the path to store uploaded files.
/**
* @param uploadPath is the directory where the uploaded files are
* stored. if it isn't prefixed with /, ./ or ../, it is relative path
* of document_root path, The default value is 'uploads'.
*
* @note
* This operation can be performed by an option in the configuration
* file.
*/
virtual HttpAppFramework &setUploadPath(const std::string &uploadPath) = 0;
/// Get the path to store uploaded files.
virtual const std::string &getUploadPath() const = 0;
/// Set types of files that can be downloaded.
/**
* Example:
* @code
app.setFileTypes({"html","txt","png","jpg"});
@endcode
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setFileTypes(
const std::vector<std::string> &types) = 0;
#ifndef _WIN32
/// Enable supporting for dynamic views loading.
/**
*
* @param libPaths is a vector that contains paths to view files.
*
* @param outputPath is the directory where the output source files locate.
* If it is set to an empty string, drogon use libPaths as output paths. If
* the path isn't prefixed with /, it is the relative path of the current
* working directory.
*
* @note
* It is disabled by default.
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &enableDynamicViewsLoading(
const std::vector<std::string> &libPaths,
const std::string &outputPath = "") = 0;
#endif
/// Set the maximum number of all connections.
/**
* The default value is 100000.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setMaxConnectionNum(size_t maxConnections) = 0;
/// Set the maximum number of connections per remote IP.
/**
* The default value is 0 which means no limit.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setMaxConnectionNumPerIP(
size_t maxConnectionsPerIP) = 0;
/// Make the application run as a daemon.
/**
* Disabled by default.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &enableRunAsDaemon() = 0;
/// Disable the handling of SIGTERM signal.
/**
* Enabled by default.
*
* @note
* This operation can be performed by an option in the configuration file.
* When disabled setTermSignalHandler() is useless
*/
virtual HttpAppFramework &disableSigtermHandling() = 0;
/// Make the application restart after crashing.
/**
* Disabled by default.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &enableRelaunchOnError() = 0;
/**
* @brief Set the output path of logs.
* @param logPath The path to logs - logs to console if empty.
* @param logfileBaseName The base name of log files - defaults to "drogon"
* if empty.
* @param logSize indicates the maximum size of a log file.
* @param maxFiles max count of log file - 0 = unlimited.
* @param useSpdlog Use spdlog for logging (if compiled-in).
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setLogPath(
const std::string &logPath,
const std::string &logfileBaseName = "",
size_t logSize = 100000000,
size_t maxFiles = 0,
bool useSpdlog = false) = 0;
/**
* @brief Set the log level.
* @param level is one of TRACE, DEBUG, INFO, WARN. The Default value is
* DEBUG.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setLogLevel(trantor::Logger::LogLevel level) = 0;
/// Set the log time display
/**
* @param on is true to display local time, false to display UTC time. The
* Default value is false.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setLogLocalTime(bool on) = 0;
/// Enable the sendfile system call in linux.
/**
* @param sendFile if the parameter is true, sendfile() system-call is used
* to send static files to clients; The default value is true.
*
* @note
* This operation can be performed by an option in the configuration file.
* Even though sendfile() is enabled, only files larger than 200k are sent
* this way,
* because the advantages of sendfile() can only be reflected in sending
* large files.
*/
virtual HttpAppFramework &enableSendfile(bool sendFile) = 0;
/// Enable gzip compression.
/**
* @param useGzip if the parameter is true, use gzip to compress the
* response body's content;
* The default value is true.
*
* @note
* This operation can be performed by an option in the configuration file.
* After gzip is enabled, gzip is used under the following conditions:
* 1. The content type of response is not a binary type.
* 2. The content length is bigger than 1024 bytes.
*/
virtual HttpAppFramework &enableGzip(bool useGzip) = 0;
/// Return true if gzip is enabled.
virtual bool isGzipEnabled() const = 0;
/// Enable brotli compression.
/**
* @param useBrotli if the parameter is true, use brotli to compress the
* response body's content;
* The default value is true.
*
* @note
* This operation can be performed by an option in the configuration file.
* After brotli is enabled, brotli is used under the following conditions:
* 1. The content type of response is not a binary type.
* 2. The content length is bigger than 1024 bytes.
*/
virtual HttpAppFramework &enableBrotli(bool useBrotli) = 0;
/// Return true if brotli is enabled.
virtual bool isBrotliEnabled() const = 0;
/// Set the time in which the static file response is cached in memory.
/**
* @param cacheTime in seconds. 0 means always cached, negative means no
* cache
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setStaticFilesCacheTime(int cacheTime) = 0;
/// Get the time set by the above method.
virtual int staticFilesCacheTime() const = 0;
/// Set the lifetime of the connection without read or write
/**
* @param timeout in seconds. 60 by default. Setting the timeout to 0 means
* that drogon does not close idle connections.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setIdleConnectionTimeout(size_t timeout) = 0;
/// A wrapper of the above method.
/**
* Example:
* Users can set the timeout value as follows:
* @code
app().setIdleConnectionTimeout(0.5h);
app().setIdleConnectionTimeout(30min);
@endcode
*/
inline HttpAppFramework &setIdleConnectionTimeout(
const std::chrono::duration<double> &timeout)
{
return setIdleConnectionTimeout((size_t)timeout.count());
}
/// Set the 'server' header field in each response sent by drogon.
/**
* @param server empty string by default with which the 'server' header
* field is set to "Server: drogon/version string\r\n"
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setServerHeaderField(
const std::string &server) = 0;
/// Control if the 'Server' header is added to each HTTP response.
/**
* @note
* These operations can be performed by options in the configuration file.
* The headers are sent to clients by default.
*/
virtual HttpAppFramework &enableServerHeader(bool flag) = 0;
/// Control if the 'Date' header is added to each HTTP response.
/**
* @note
* These operations can be performed by options in the configuration file.
* The headers are sent to clients by default.
*/
virtual HttpAppFramework &enableDateHeader(bool flag) = 0;
/// Set the maximum number of requests that can be served through one
/// keep-alive connection.
/**
* After the maximum number of requests are made, the connection is closed.
* The default value is 0 which means no limit.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setKeepaliveRequestsNumber(
const size_t number) = 0;
/// Set the maximum number of unhandled requests that can be cached in
/// pipelining buffer.
/**
* The default value of 0 means no limit.
* After the maximum number of requests cached in pipelining buffer are
* made, the connection is closed.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setPipeliningRequestsNumber(
const size_t number) = 0;
/// Set the gzip_static option.
/**
* If it is set to true, when the client requests a static file, drogon
* first finds the compressed file with the extension ".gz" in the same path
* and send the compressed file to the client. The default value is true.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setGzipStatic(bool useGzipStatic) = 0;
/// Set the br_static option.
/**
* If it is set to true, when the client requests a static file, drogon
* first finds the compressed file with the extension ".br" in the same path
* and send the compressed file to the client. The default value is true.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setBrStatic(bool useGzipStatic) = 0;
/// Set the max body size of the requests received by drogon.
/**
* The default value is 1M.
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setClientMaxBodySize(size_t maxSize) = 0;
/// Set the maximum body size in memory of HTTP requests received by drogon.
/**
* The default value is "64K" bytes. If the body size of a HTTP request
* exceeds this limit, the body is stored to a temporary file for
* processing.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setClientMaxMemoryBodySize(size_t maxSize) = 0;
/// Set the max size of messages sent by WebSocket client.
/**
* The default value is 128K.
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setClientMaxWebSocketMessageSize(
size_t maxSize) = 0;
// Set the HTML file of the home page, the default value is "index.html"
/**
* If there isn't any handler registered to the path "/", the home page file
* in the "document_root"
* is send to clients as a response to the request for "/".
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setHomePage(const std::string &homePageFile) = 0;
/**
* @brief Set the TERM Signal Handler. This method provides a way to users
* for exiting program gracefully. When the TERM signal is received after
* app().run() is called, the handler is invoked. Drogon uses a default
* signal handler for the TERM signal, which calls the 'app().quit()' method
* when the TERM signal is received.
*
* @param handler
* @return HttpAppFramework&
*/
virtual HttpAppFramework &setTermSignalHandler(
const std::function<void()> &handler) = 0;
/**
* @brief Set the INT Signal Handler. This method provides a way to users
* for exiting program gracefully. When the INT signal is received after
* app().run() is called, the handler is invoked. Drogon uses a default
* signal handler for the INT signal, which calls the 'app().quit()' method
* when the INT signal is received.
*
* @param handler
* @return HttpAppFramework&
*/
virtual HttpAppFramework &setIntSignalHandler(
const std::function<void()> &handler) = 0;
/// Get homepage, default is "index.html"
/**
* @note
* This method must be called after the framework has been run.
*/
virtual const std::string &getHomePage() const = 0;
/// Set to enable implicit pages, enabled by default
/**
* @brief Implicit pages are used when the server detects if the user
* requested a directory. By default, it will try to append index.html to
* the path, see setImplicitPage() if you want to customize this
* (http://localhost/a-directory resolves to
* http://localhost/a-directory/index.html by default).
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setImplicitPageEnable(bool useImplicitPage) = 0;
/// Return true if implicit pages are enabled
/**
* @note
* This method must be called after the framework has been run.
*/
virtual bool isImplicitPageEnabled() const = 0;
/// Set the HTML file that a directory would resolve to by default, default
/// is "index.html"
/**
* @brief Set the page which would the server load in if it detects that
* the user requested a directory
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setImplicitPage(
const std::string &implicitPageFile) = 0;
/// Get the implicit HTML page
/**
* @note
* This method must be called after the framework has been run.
*/
virtual const std::string &getImplicitPage() const = 0;
/// Get a database client by name
/**
* @note
* This method must be called after the framework has been run.
*/
virtual orm::DbClientPtr getDbClient(
const std::string &name = "default") = 0;
/// Get a 'fast' database client by name
/**
* @note
* This method must be called after the framework has been run.
*/
virtual orm::DbClientPtr getFastDbClient(
const std::string &name = "default") = 0;
/**
* @brief Check if all database clients in the framework are available
* (connect to the database successfully).
*/
virtual bool areAllDbClientsAvailable() const noexcept = 0;
/// Get a redis client by name
/**
* @note
* This method must be called after the framework has been run.
*/
virtual nosql::RedisClientPtr getRedisClient(
const std::string &name = "default") = 0;
/// Get a 'fast' redis client by name
/**
* @note
* This method must be called after the framework has been run.
*/
virtual nosql::RedisClientPtr getFastRedisClient(
const std::string &name = "default") = 0;
/**
* @brief Set the maximum stack depth of the json parser when reading a json
* string, the default value is 1000.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &setJsonParserStackLimit(
size_t limit) noexcept = 0;
/**
* @brief Get the maximum stack depth of the json parser when reading a json
* string.
*/
virtual size_t getJsonParserStackLimit() const noexcept = 0;
/**
* @brief This method is to enable or disable the unicode escaping (\\u) in
* the json string of HTTP responses or requests. it works (disable
* successfully) when the version of JsonCpp >= 1.9.3, the unicode escaping
* is enabled by default.
*/
virtual HttpAppFramework &setUnicodeEscapingInJson(
bool enable) noexcept = 0;
/**
* @brief Check if the unicode escaping is used in the json string of HTTP
* requests and responses.
*/
virtual bool isUnicodeEscapingUsedInJson() const noexcept = 0;
/**
* @brief Set the float precision in Json string of HTTP requests or
* responses with json content.
*
* @param precision The maximum digits length.
* @param precisionType Must be "significant" or "decimal", defaults to
* "significant" that means setting max number of significant digits in
* string, "decimal" means setting max number of digits after "." in string
* @return HttpAppFramework&
*/
virtual HttpAppFramework &setFloatPrecisionInJson(
unsigned int precision,
const std::string &precisionType = "significant") noexcept = 0;
/**
* @brief Get the float precision set by the above method.
*
* @return std::pair<size_t, std::string>
*/
virtual const std::pair<unsigned int, std::string> &
getFloatPrecisionInJson() const noexcept = 0;
/// Create a database client
/**
* @param dbType The database type is one of
* "postgresql","mysql","sqlite3".
* @param host IP or host name.
* @param port The port on which the database server is listening.
* @param databaseName Database name
* @param userName User name
* @param password Password for the database server
* @param connectionNum The number of connections to the database server.
* It's valid only if @p isFast is false.
* @param filename The file name of sqlite3 database file.
* @param name The client name.
* @param isFast Indicates if the client is a fast database client.
* @param characterSet The character set of the database server.
* @param timeout The timeout in seconds for executing SQL queries. zero or
* negative value means no timeout.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
[[deprecated("Use addDbClient() instead")]] virtual HttpAppFramework &
createDbClient(const std::string &dbType,
const std::string &host,
unsigned short port,
const std::string &databaseName,
const std::string &userName,
const std::string &password,
size_t connectionNum = 1,
const std::string &filename = "",
const std::string &name = "default",
bool isFast = false,
const std::string &characterSet = "",
double timeout = -1.0,
bool autoBatch = false) = 0;
virtual HttpAppFramework &addDbClient(const orm::DbConfig &config) = 0;
/// Create a redis client
/**
* @param ip IP of redis server.
* @param port The port on which the redis server is listening.
* @param name The client name.
* @param username Username for redis server
* @param password Password for the redis server
* @param connectionNum The number of connections to the redis server.
* @param isFast Indicates if the client is a fast database client.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual HttpAppFramework &createRedisClient(
const std::string &ip,
unsigned short port,
const std::string &name = "default",
const std::string &password = "",
size_t connectionNum = 1,
bool isFast = false,
double timeout = -1.0,
unsigned int db = 0,
const std::string &username = "") = 0;
/// Get the DNS resolver
/**
* @note
* When the c-ares library is installed in the system, it runs with the best
* performance.
*/
virtual const std::shared_ptr<trantor::Resolver> &getResolver() const = 0;
/// Return true is drogon supports SSL(https)
virtual bool supportSSL() const = 0;
/**
* @brief Get the Current Thread Index whose range is [0, the total number
* of IO threads]
*
* @return size_t If the current thread is the main EventLoop thread (in
* which the app().run() is called), the number of the IO threads is
* returned. If the current thread is a network IO thread, the index of it
* in the range [0, the number of IO threads) is returned. otherwise the
* maximum value of type size_t is returned.
*
* @note Basically this method is used for storing thread-related various in
* an array and users can use indexes returned by this method to access
* them. This is much faster than using a map. If the array is properly
* initialized at the beginning, users can access it without locks.
*/
virtual size_t getCurrentThreadIndex() const = 0;
/**
* @brief Get the addresses of listeners.
*
* @return std::vector<trantor::InetAddress>
* @note This method should be called after calling the app().run(). One
* could run this method in an AOP join point (such as the BeginningAdvice).
*/
virtual std::vector<trantor::InetAddress> getListeners() const = 0;
/**
* @brief Enable ReusePort mode or not. If the mode is enabled, one can run
* multiple processes listening to the same port at the same time. If this
* method is not called, the feature is disabled.
*
* @note
* This operation can be performed by an option in the configuration file.
*/
virtual void enableReusePort(bool enable = true) = 0;
/**
* @brief Return if the ReusePort mode is enabled.
*/
virtual bool reusePort() const = 0;
/**
* @brief handler will be called upon an exception escapes a request handler
*/
virtual HttpAppFramework &setExceptionHandler(ExceptionHandler handler) = 0;
/**
* @brief returns the excaption handler
*/
virtual const ExceptionHandler &getExceptionHandler() const = 0;
/**
* @brief Adds a new custom extension to MIME type mapping
*/
virtual HttpAppFramework &registerCustomExtensionMime(
const std::string &ext,
const std::string &mime) = 0;
virtual HttpAppFramework &enableCompressedRequest(bool enable = true) = 0;
virtual bool isCompressedRequestEnabled() const = 0;
/*
* @brief get the number of active connections.
*/
virtual int64_t getConnectionCount() const = 0;
/**
* @brief Set the before listen setsockopt callback.
*
* @param cb This callback will be called before the listen
*/
virtual HttpAppFramework &setBeforeListenSockOptCallback(
std::function<void(int)> cb) = 0;
/**
* @brief Set the after accept setsockopt callback.
*
* @param cb This callback will be called after accept
*/
virtual HttpAppFramework &setAfterAcceptSockOptCallback(
std::function<void(int)> cb) = 0;
virtual HttpAppFramework &enableRequestStream(bool enable = true) = 0;
virtual bool isRequestStreamEnabled() const = 0;
private:
virtual void registerHttpController(
const std::string &pathPattern,
const internal::HttpBinderBasePtr &binder,
const std::vector<HttpMethod> &validMethods = {},
const std::vector<std::string> &middlewareNames = {},
const std::string &handlerName = "") = 0;
virtual void registerHttpControllerViaRegex(
const std::string &regExp,
const internal::HttpBinderBasePtr &binder,
const std::vector<HttpMethod> &validMethods,
const std::vector<std::string> &middlewareNames,
const std::string &handlerName) = 0;
};
/// A wrapper of the instance() method
inline HttpAppFramework &app()
{
return HttpAppFramework::instance();
}
#ifdef __cpp_impl_coroutine
namespace internal
{
inline void ForwardAwaiter::await_suspend(
std::coroutine_handle<> handle) noexcept
{
app_.forward(
req_,
[this, handle](const drogon::HttpResponsePtr &resp) {
setValue(resp);
handle.resume();
},
host_,
timeout_);
}
} // namespace internal
#endif
} // namespace drogon