/** * * @file HttpBinder.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 * */ /// The classes in the file are internal tool classes. Do not include this /// file directly and use any of these classes directly. #pragma once #include #include #include #include #include #include #include #include #include #include namespace drogon { namespace internal { // we only accept value type or const lreference type or right reference type as // the handle method parameters type template struct BinderArgTypeTraits { static const bool isValid = true; }; template struct BinderArgTypeTraits { static const bool isValid = false; }; template struct BinderArgTypeTraits { static const bool isValid = false; }; template struct BinderArgTypeTraits { static const bool isValid = true; }; template struct BinderArgTypeTraits { static const bool isValid = false; }; template struct BinderArgTypeTraits { static const bool isValid = true; }; class HttpBinderBase { public: virtual void handleHttpRequest( std::deque &pathArguments, const HttpRequestPtr &req, std::function &&callback) = 0; virtual size_t paramCount() = 0; virtual const std::string &handlerName() const = 0; virtual ~HttpBinderBase() { } }; template T &getControllerObj() { // Initialization of function-local statics is guaranteed to occur only once // even when // called from multiple threads, and may be more efficient than the // equivalent code using std::call_once. static T obj; return obj; } DROGON_EXPORT void handleException( const std::exception &, const HttpRequestPtr &, std::function &&); using HttpBinderBasePtr = std::shared_ptr; template class HttpBinder : public HttpBinderBase { public: using traits = FunctionTraits; using FunctionType = FUNCTION; void handleHttpRequest( std::deque &pathArguments, const HttpRequestPtr &req, std::function &&callback) override { run(pathArguments, req, std::move(callback)); } size_t paramCount() override { return traits::arity; } HttpBinder(FUNCTION &&func) : func_(std::forward(func)) { static_assert(traits::isHTTPFunction, "Your API handler function interface is wrong!"); handlerName_ = DrClassMap::demangle(typeid(FUNCTION).name()); } void test() { std::cout << "argument_count=" << argument_count << " " << traits::isHTTPFunction << std::endl; } const std::string &handlerName() const override { return handlerName_; } template typename std::enable_if::type createHandlerInstance() { auto objPtr = DrClassMap::getSingleInstance(); LOG_TRACE << "create handler class object: " << objPtr.get(); } template typename std::enable_if::type createHandlerInstance() { auto &obj = getControllerObj(); LOG_TRACE << "create handler class object: " << &obj; } template typename std::enable_if::type createHandlerInstance() { } private: FUNCTION func_; template using nth_argument_type = typename traits::template argument; static const size_t argument_count = traits::arity; std::string handlerName_; template typename std::enable_if::value, void>::type getHandlerArgumentValue(T &value, std::string &&p) { if (!p.empty()) { std::stringstream ss(std::move(p)); ss >> value; } } template typename std::enable_if::value), void>::type getHandlerArgumentValue(T &value, std::string &&p) { } void getHandlerArgumentValue(std::string &value, std::string &&p) { value = std::move(p); } void getHandlerArgumentValue(int &value, std::string &&p) { value = std::stoi(p); } void getHandlerArgumentValue(long &value, std::string &&p) { value = std::stol(p); } void getHandlerArgumentValue(long long &value, std::string &&p) { value = std::stoll(p); } void getHandlerArgumentValue(unsigned long &value, std::string &&p) { value = std::stoul(p); } void getHandlerArgumentValue(unsigned long long &value, std::string &&p) { value = std::stoull(p); } void getHandlerArgumentValue(float &value, std::string &&p) { value = std::stof(p); } void getHandlerArgumentValue(double &value, std::string &&p) { value = std::stod(p); } void getHandlerArgumentValue(long double &value, std::string &&p) { value = std::stold(p); } template typename std::enable_if<(sizeof...(Values) < Boundary), void>::type run( std::deque &pathArguments, const HttpRequestPtr &req, std::function &&callback, Values &&...values) { // Call this function recursively until parameter's count equals to the // count of target function parameters static_assert( BinderArgTypeTraits>::isValid, "your handler argument type must be value type or const left " "reference type or right reference type"); using ValueType = typename std::remove_cv>::type>::type; ValueType value = ValueType(); if (!pathArguments.empty()) { std::string v = std::move(pathArguments.front()); pathArguments.pop_front(); try { if (v.empty() == false) getHandlerArgumentValue(value, std::move(v)); } catch (const std::exception &e) { handleException(e, req, std::move(callback)); return; } } else { try { value = req->as(); } catch (const std::exception &e) { handleException(e, req, std::move(callback)); return; } catch (...) { LOG_ERROR << "Exception not derived from std::exception"; return; } } run(pathArguments, req, std::move(callback), std::forward(values)..., std::move(value)); } template typename std::enable_if<(sizeof...(Values) == Boundary) && !isCoroutine, void>::type run(std::deque &, const HttpRequestPtr &req, std::function &&callback, Values &&...values) { try { // Explcit copy because `callFunction` moves it auto cb = callback; callFunction(req, cb, std::move(values)...); } catch (const std::exception &except) { handleException(except, req, std::move(callback)); } catch (...) { LOG_ERROR << "Exception not derived from std::exception"; return; } } #ifdef __cpp_impl_coroutine template typename std::enable_if<(sizeof...(Values) == Boundary) && isCoroutine, void>::type run(std::deque &, const HttpRequestPtr &req, std::function &&callback, Values &&...values) { [this](HttpRequestPtr req, std::function callback, Values &&...values) -> AsyncTask { try { if constexpr (std::is_same_v) { // Explcit copy because `callFunction` moves it auto cb = callback; callFunction(req, cb, std::move(values)...); } else if constexpr (std::is_same_v, typename traits::return_type>) { // Explcit copy because `callFunction` moves it auto cb = callback; co_await callFunction(req, cb, std::move(values)...); } else if constexpr (std::is_same_v, typename traits::return_type>) { auto resp = co_await callFunction(req, std::move(values)...); callback(std::move(resp)); } } catch (const std::exception &except) { handleException(except, req, std::move(callback)); } catch (...) { LOG_ERROR << "Exception not derived from std::exception"; } }(req, std::move(callback), std::move(values)...); } #endif template ::value> typename std::enable_if::type callFunction(const HttpRequestPtr &req, Values &&...values) { static auto &obj = getControllerObj(); return (obj.*func_)(req, std::move(values)...); } template ::value> typename std::enable_if::type callFunction(const HttpRequestPtr &req, Values &&...values) { static auto objPtr = DrClassMap::getSingleInstance(); return (*objPtr.*func_)(req, std::move(values)...); } template ::value> typename std::enable_if::type callFunction(const HttpRequestPtr &req, Values &&...values) { return func_(req, std::move(values)...); } template ::value> typename std::enable_if::type callFunction(const HttpRequestPtr &req, Values &&...values) { static auto &obj = getControllerObj(); return (obj.*func_)((*req), std::move(values)...); } template ::value> typename std::enable_if::type callFunction(const HttpRequestPtr &req, Values &&...values) { static auto objPtr = DrClassMap::getSingleInstance(); return (*objPtr.*func_)((*req), std::move(values)...); } template ::value> typename std::enable_if::type callFunction(const HttpRequestPtr &req, Values &&...values) { return func_((*req), std::move(values)...); } }; } // namespace internal } // namespace drogon