/** * * @file HttpAppFrameworkImpl.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 #include #include #include #include #include #include #include #include "SessionManager.h" #include "drogon/utils/Utilities.h" #include "impl_forwards.h" namespace trantor { class EventLoopThreadPool; } namespace drogon { HttpResponsePtr defaultErrorHandler(HttpStatusCode code, const HttpRequestPtr &req); void defaultExceptionHandler(const std::exception &, const HttpRequestPtr &, std::function &&); struct InitBeforeMainFunction { explicit InitBeforeMainFunction(const std::function &func) { func(); } }; class HttpAppFrameworkImpl final : public HttpAppFramework { public: HttpAppFrameworkImpl(); const Json::Value &getCustomConfig() const override { return jsonConfig_["custom_config"]; } PluginBase *getPlugin(const std::string &name) override; std::shared_ptr getSharedPlugin( const std::string &name) override; void addPlugins(const Json::Value &configs) override; void addPlugin(const std::string &name, const std::vector &dependencies, const Json::Value &config) override; HttpAppFramework &addListener( const std::string &ip, uint16_t port, bool useSSL, const std::string &certFile, const std::string &keyFile, bool useOldTLS, const std::vector> &sslConfCmds) override; HttpAppFramework &setThreadNum(size_t threadNum) override; size_t getThreadNum() const override { return threadNum_; } HttpAppFramework &setSSLConfigCommands( const std::vector> &sslConfCmds) override; HttpAppFramework &setSSLFiles(const std::string &certPath, const std::string &keyPath) override; void run() override; HttpAppFramework ®isterWebSocketController( const std::string &pathName, const std::string &ctrlName, const std::vector &constraints) override; HttpAppFramework ®isterWebSocketControllerRegex( const std::string ®Exp, const std::string &ctrlName, const std::vector &constraints) override; HttpAppFramework ®isterHttpSimpleController( const std::string &pathName, const std::string &ctrlName, const std::vector &constraints) override; HttpAppFramework &setCustom404Page(const HttpResponsePtr &resp, bool set404) override { if (set404) { resp->setStatusCode(k404NotFound); } custom404_ = resp; return *this; } HttpAppFramework &setCustomErrorHandler( std::function &&resp_generator) override; const HttpResponsePtr &getCustom404Page(); void forward(const HttpRequestPtr &req, std::function &&callback, const std::string &hostString, double timeout) override; void forward(const HttpRequestImplPtr &req, std::function &&callback, const std::string &hostString, double timeout = 0); HttpAppFramework ®isterBeginningAdvice( const std::function &advice) override { beginningAdvices_.emplace_back(advice); return *this; } HttpAppFramework ®isterNewConnectionAdvice( const std::function &advice) override; HttpAppFramework ®isterHttpResponseCreationAdvice( const std::function &advice) override; HttpAppFramework ®isterSyncAdvice( const std::function &advice) override; HttpAppFramework ®isterPreRoutingAdvice( const std::function &advice) override; HttpAppFramework ®isterPostRoutingAdvice( const std::function &advice) override; HttpAppFramework ®isterPreHandlingAdvice( const std::function &advice) override; HttpAppFramework ®isterPreRoutingAdvice( const std::function &advice) override; HttpAppFramework ®isterPostRoutingAdvice( const std::function &advice) override; HttpAppFramework ®isterPreHandlingAdvice( const std::function &advice) override; HttpAppFramework ®isterPostHandlingAdvice( const std::function &advice) override; HttpAppFramework ®isterPreSendingAdvice( const std::function &advice) override; HttpAppFramework &setDefaultHandler(DefaultHandler handler) override; HttpAppFramework &setupFileLogger() override; HttpAppFramework &enableSession( const size_t timeout, Cookie::SameSite sameSite = Cookie::SameSite::kNull, const std::string &cookieKey = "JSESSIONID", int maxAge = -1, SessionManager::IdGeneratorCallback idGeneratorCallback = nullptr) override { useSession_ = true; sessionTimeout_ = timeout; sessionSameSite_ = sameSite; sessionCookieKey_ = cookieKey; sessionMaxAge_ = maxAge; return setSessionIdGenerator(idGeneratorCallback); } HttpAppFramework &setSessionIdGenerator( SessionManager::IdGeneratorCallback idGeneratorCallback = nullptr) { sessionIdGeneratorCallback_ = idGeneratorCallback ? idGeneratorCallback : []() { return utils::getUuid(true); }; return *this; } HttpAppFramework &disableSession() override { useSession_ = false; return *this; } HttpAppFramework ®isterSessionStartAdvice( const AdviceStartSessionCallback &advice) override { sessionStartAdvices_.emplace_back(advice); return *this; } HttpAppFramework ®isterSessionDestroyAdvice( const AdviceDestroySessionCallback &advice) override { sessionDestroyAdvices_.emplace_back(advice); return *this; } const std::string &getDocumentRoot() const override { return rootPath_; } HttpAppFramework &setDocumentRoot(const std::string &rootPath) override { rootPath_ = rootPath; return *this; } HttpAppFramework &setStaticFileHeaders( const std::vector> &headers) override; HttpAppFramework &addALocation( const std::string &uriPrefix, const std::string &defaultContentType, const std::string &alias, bool isCaseSensitive, bool allowAll, bool isRecursive, const std::vector &middlewareNames) override; const std::string &getUploadPath() const override { return uploadPath_; } const std::shared_ptr &getResolver() const override { static auto resolver = trantor::Resolver::newResolver(getLoop()); return resolver; } HttpAppFramework &setUploadPath(const std::string &uploadPath) override; HttpAppFramework &setFileTypes( const std::vector &types) override; #ifndef _WIN32 HttpAppFramework &enableDynamicViewsLoading( const std::vector &libPaths, const std::string &outputPath) override; #endif HttpAppFramework &setMaxConnectionNum(size_t maxConnections) override; HttpAppFramework &setMaxConnectionNumPerIP( size_t maxConnectionsPerIP) override; HttpAppFramework &loadConfigFile(const std::string &fileName) noexcept( false) override; HttpAppFramework &loadConfigJson(const Json::Value &data) noexcept( false) override; HttpAppFramework &loadConfigJson(Json::Value &&data) noexcept( false) override; HttpAppFramework &enableRunAsDaemon() override { runAsDaemon_ = true; return *this; } HttpAppFramework &disableSigtermHandling() override { handleSigterm_ = false; return *this; } HttpAppFramework &enableRelaunchOnError() override { relaunchOnError_ = true; return *this; } HttpAppFramework &setLogPath(const std::string &logPath, const std::string &logfileBaseName, size_t logfileSize, size_t maxFiles, bool useSpdlog) override; HttpAppFramework &setLogLevel(trantor::Logger::LogLevel level) override; HttpAppFramework &setLogLocalTime(bool on) override; HttpAppFramework &enableSendfile(bool sendFile) override { useSendfile_ = sendFile; return *this; } HttpAppFramework &enableGzip(bool useGzip) override { useGzip_ = useGzip; return *this; } bool isGzipEnabled() const override { return useGzip_; } HttpAppFramework &enableBrotli(bool useBrotli) override { useBrotli_ = useBrotli; return *this; } bool isBrotliEnabled() const override { return useBrotli_; } HttpAppFramework &setStaticFilesCacheTime(int cacheTime) override; int staticFilesCacheTime() const override; HttpAppFramework &setIdleConnectionTimeout(size_t timeout) override { idleConnectionTimeout_ = timeout; return *this; } size_t getIdleConnectionTimeout() const // could expose in base class { return idleConnectionTimeout_; } HttpAppFramework &setKeepaliveRequestsNumber(const size_t number) override { keepaliveRequestsNumber_ = number; return *this; } HttpAppFramework &setPipeliningRequestsNumber(const size_t number) override { pipeliningRequestsNumber_ = number; return *this; } HttpAppFramework &setGzipStatic(bool useGzipStatic) override; HttpAppFramework &setBrStatic(bool useGzipStatic) override; HttpAppFramework &setClientMaxBodySize(size_t maxSize) override { clientMaxBodySize_ = maxSize; return *this; } HttpAppFramework &setClientMaxMemoryBodySize(size_t maxSize) override { clientMaxMemoryBodySize_ = maxSize; return *this; } HttpAppFramework &setClientMaxWebSocketMessageSize(size_t maxSize) override { clientMaxWebSocketMessageSize_ = maxSize; return *this; } HttpAppFramework &setHomePage(const std::string &homePageFile) override { homePageFile_ = homePageFile; return *this; } const std::string &getHomePage() const override { return homePageFile_; } HttpAppFramework &setTermSignalHandler( const std::function &handler) override { termSignalHandler_ = handler; return *this; } const std::function &getTermSignalHandler() const { return termSignalHandler_; } HttpAppFramework &setIntSignalHandler( const std::function &handler) override { intSignalHandler_ = handler; return *this; } const std::function &getIntSignalHandler() const { return intSignalHandler_; } HttpAppFramework &setImplicitPageEnable(bool useImplicitPage) override; bool isImplicitPageEnabled() const override; HttpAppFramework &setImplicitPage( const std::string &implicitPageFile) override; const std::string &getImplicitPage() const override; size_t getClientMaxBodySize() const { return clientMaxBodySize_; } size_t getClientMaxMemoryBodySize() const { return clientMaxMemoryBodySize_; } size_t getClientMaxWebSocketMessageSize() const { return clientMaxWebSocketMessageSize_; } std::vector getHandlersInfo() const override; size_t keepaliveRequestsNumber() const { return keepaliveRequestsNumber_; } size_t pipeliningRequestsNumber() const { return pipeliningRequestsNumber_; } ~HttpAppFrameworkImpl() noexcept override; bool isRunning() override { return running_; } HttpAppFramework &setJsonParserStackLimit(size_t limit) noexcept override { jsonStackLimit_ = limit; return *this; } size_t getJsonParserStackLimit() const noexcept override { return jsonStackLimit_; } HttpAppFramework &setUnicodeEscapingInJson(bool enable) noexcept override { usingUnicodeEscaping_ = enable; return *this; } bool isUnicodeEscapingUsedInJson() const noexcept override { return usingUnicodeEscaping_; } HttpAppFramework &setFloatPrecisionInJson( unsigned int precision, const std::string &precisionType) noexcept override { floatPrecisionInJson_ = std::make_pair(precision, precisionType); return *this; } const std::pair &getFloatPrecisionInJson() const noexcept override { return floatPrecisionInJson_; } trantor::EventLoop *getLoop() const override; trantor::EventLoop *getIOLoop(size_t id) const override; void quit() override; HttpAppFramework &setServerHeaderField(const std::string &server) override { assert(!running_); assert(server.find("\r\n") == std::string::npos); serverHeader_ = "server: " + server + "\r\n"; return *this; } HttpAppFramework &enableServerHeader(bool flag) override { enableServerHeader_ = flag; return *this; } HttpAppFramework &enableDateHeader(bool flag) override { enableDateHeader_ = flag; return *this; } bool sendServerHeader() const { return enableServerHeader_; } bool sendDateHeader() const { return enableDateHeader_; } const std::string &getServerHeaderString() const { return serverHeader_; } orm::DbClientPtr getDbClient(const std::string &name) override; orm::DbClientPtr getFastDbClient(const std::string &name) override; 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, const std::string &filename, const std::string &name, bool isFast, const std::string &characterSet, double timeout, bool autoBatch) override; // a helper method void addDbClient(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, const std::string &filename, const std::string &name, bool isFast, const std::string &characterSet, double timeout, bool autoBatch, std::unordered_map options); HttpAppFramework &addDbClient(const orm::DbConfig &config) override; HttpAppFramework &createRedisClient(const std::string &ip, unsigned short port, const std::string &name, const std::string &password, size_t connectionNum, bool isFast, double timeout, unsigned int db, const std::string &username) override; nosql::RedisClientPtr getRedisClient(const std::string &name) override; nosql::RedisClientPtr getFastRedisClient(const std::string &name) override; std::vector getListeners() const override; inline static HttpAppFrameworkImpl &instance() { static HttpAppFrameworkImpl instance; return instance; } bool useSendfile() const { return useSendfile_; } bool supportSSL() const override { return trantor::utils::tlsBackend() != "None"; } size_t getCurrentThreadIndex() const override { auto *loop = trantor::EventLoop::getEventLoopOfCurrentThread(); if (loop) { return loop->index(); } return (std::numeric_limits::max)(); } bool areAllDbClientsAvailable() const noexcept override; const std::function & getCustomErrorHandler() const override; bool isUsingCustomErrorHandler() const { return usingCustomErrorHandler_; } void enableReusePort(bool enable) override { reusePort_ = enable; } bool reusePort() const override { return reusePort_; } HttpAppFramework &setExceptionHandler(ExceptionHandler handler) override { exceptionHandler_ = std::move(handler); return *this; } const ExceptionHandler &getExceptionHandler() const override { return exceptionHandler_; } HttpAppFramework &enableCompressedRequest(bool enable) override { enableCompressedRequest_ = enable; return *this; } bool isCompressedRequestEnabled() const override { return enableCompressedRequest_; } HttpAppFramework ®isterCustomExtensionMime( const std::string &ext, const std::string &mime) override; // should return unsigned type! int64_t getConnectionCount() const override; // TODO: move session related codes to its own singleton class void findSessionForRequest(const HttpRequestImplPtr &req); HttpResponsePtr handleSessionForResponse(const HttpRequestImplPtr &req, const HttpResponsePtr &resp); HttpAppFramework &setBeforeListenSockOptCallback( std::function cb) override; HttpAppFramework &setAfterAcceptSockOptCallback( std::function cb) override; HttpAppFramework &enableRequestStream(bool enable) override; bool isRequestStreamEnabled() const override; private: void registerHttpController(const std::string &pathPattern, const internal::HttpBinderBasePtr &binder, const std::vector &validMethods, const std::vector &middlewareNames, const std::string &handlerName) override; void registerHttpControllerViaRegex( const std::string ®Exp, const internal::HttpBinderBasePtr &binder, const std::vector &validMethods, const std::vector &middlewareNames, const std::string &handlerName) override; // We use an uuid string as session id; // set sessionTimeout_=0 to make location session valid forever based on // cookies; size_t sessionTimeout_{0}; Cookie::SameSite sessionSameSite_{Cookie::SameSite::kNull}; std::string sessionCookieKey_{"JSESSIONID"}; int sessionMaxAge_{-1}; size_t idleConnectionTimeout_{60}; bool useSession_{false}; std::string serverHeader_{"server: drogon/" + drogon::getVersion() + "\r\n"}; std::unique_ptr listenerManagerPtr_; std::unique_ptr pluginsManagerPtr_; std::unique_ptr dbClientManagerPtr_; std::unique_ptr redisClientManagerPtr_; std::string rootPath_{"./"}; std::string uploadPath_; std::atomic_bool running_{false}; std::atomic_bool routersInit_{false}; size_t threadNum_{1}; std::unique_ptr ioLoopThreadPool_; #ifndef _WIN32 std::vector libFilePaths_; std::string libFileOutputPath_; std::unique_ptr sharedLibManagerPtr_; #endif std::vector> sslConfCmds_; std::string sslCertPath_; std::string sslKeyPath_; bool runAsDaemon_{false}; bool handleSigterm_{true}; bool relaunchOnError_{false}; bool logWithSpdlog_{false}; std::string logPath_; std::string logfileBaseName_; size_t logfileSize_{100000000}; size_t logfileMaxNum_{0}; size_t keepaliveRequestsNumber_{0}; size_t pipeliningRequestsNumber_{0}; size_t jsonStackLimit_{1000}; bool useSendfile_{true}; bool useGzip_{true}; bool useBrotli_{false}; bool usingUnicodeEscaping_{true}; std::pair floatPrecisionInJson_{0, "significant"}; bool usingCustomErrorHandler_{false}; size_t clientMaxBodySize_{1024 * 1024}; size_t clientMaxMemoryBodySize_{64 * 1024}; size_t clientMaxWebSocketMessageSize_{128 * 1024}; std::string homePageFile_{"index.html"}; std::function termSignalHandler_{[]() { app().quit(); }}; std::function intSignalHandler_{[]() { app().quit(); }}; std::unique_ptr sessionManagerPtr_; std::vector sessionStartAdvices_; std::vector sessionDestroyAdvices_; SessionManager::IdGeneratorCallback sessionIdGeneratorCallback_; std::shared_ptr asyncFileLoggerPtr_; Json::Value jsonConfig_; Json::Value jsonRuntimeConfig_; HttpResponsePtr custom404_; std::function customErrorHandler_ = &defaultErrorHandler; static InitBeforeMainFunction initFirst_; bool enableServerHeader_{true}; bool enableDateHeader_{true}; bool reusePort_{false}; std::vector> beginningAdvices_; ExceptionHandler exceptionHandler_{defaultExceptionHandler}; bool enableCompressedRequest_{false}; bool enableRequestStream_{false}; }; } // namespace drogon