Files
2025-03-26 01:06:45 +08:00

242 lines
10 KiB
C++

// Copyright (C) 2020 Mikhail Svetkin <[email protected]>
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QHTTPSERVER_H
#define QHTTPSERVER_H
#include <QtHttpServer/qabstracthttpserver.h>
#include <QtHttpServer/qhttpserverrouter.h>
#include <QtHttpServer/qhttpserverrouterrule.h>
#include <QtHttpServer/qhttpserverresponse.h>
#include <QtHttpServer/qhttpserverrouterviewtraits.h>
#if QT_CONFIG(future)
# include <QtCore/qfuture.h>
#endif
#include <functional>
#include <tuple>
QT_BEGIN_NAMESPACE
class QHttpServerRequest;
class QHttpServerPrivate;
class Q_HTTPSERVER_EXPORT QHttpServer final : public QAbstractHttpServer
{
Q_OBJECT
Q_DECLARE_PRIVATE(QHttpServer)
template <typename...>
static constexpr bool dependent_false_v = false;
template<typename T>
using ResponseType =
#if QT_CONFIG(future)
std::conditional_t<
std::is_same_v<QFuture<QHttpServerResponse>, T>,
QFuture<QHttpServerResponse>,
QHttpServerResponse
>;
#else
QHttpServerResponse;
#endif
using MissingHandlerPrototype = void (*)(const QHttpServerRequest &request,
QHttpServerResponder &responder);
template <typename T>
using if_missinghandler_prototype_compatible = typename std::enable_if<
QtPrivate::AreFunctionsCompatible<MissingHandlerPrototype, T>::value, bool>::type;
using AfterRequestPrototype = void (*)(const QHttpServerRequest &request,
QHttpServerResponse &responder);
template <typename T>
using if_after_request_prototype_compatible = typename std::enable_if<
QtPrivate::AreFunctionsCompatible<AfterRequestPrototype, T>::value, bool>::type;
public:
explicit QHttpServer(QObject *parent = nullptr);
~QHttpServer() override;
QHttpServerRouter *router();
const QHttpServerRouter *router() const;
#ifdef Q_QDOC
template <typename Rule = QHttpServerRouterRule, typename Functor>
Rule *route(const QString &pathPattern, QHttpServerRequest::Methods method,
const QObject *receiver,
Functor &&slot);
template <typename Rule = QHttpServerRouterRule, typename Functor>
Rule *route(const QString &pathPattern,
const QObject *receiver,
Functor &&slot);
template <typename Rule = QHttpServerRouterRule, typename Functor>
Rule *route(const QString &pathPattern, QHttpServerRequest::Methods method,
Functor &&handler);
template <typename Rule = QHttpServerRouterRule, typename Functor>
Rule *route(const QString &pathPattern,
Functor &&handler);
#else
template<typename Rule = QHttpServerRouterRule, typename ViewHandler>
Rule *route(const QString &pathPattern, QHttpServerRequest::Methods method,
const typename QtPrivate::ContextTypeForFunctor<ViewHandler>::ContextType *context,
ViewHandler &&viewHandler)
{
using ViewTraits = QHttpServerRouterViewTraits<ViewHandler>;
static_assert(ViewTraits::Arguments::StaticAssert,
"ViewHandler arguments are in the wrong order or not supported");
return routeImpl<Rule, ViewHandler, ViewTraits>(pathPattern, method, context,
std::forward<ViewHandler>(viewHandler));
}
template<typename Rule = QHttpServerRouterRule, typename ViewHandler>
Rule *route(const QString &pathPattern,
const typename QtPrivate::ContextTypeForFunctor<ViewHandler>::ContextType *context,
ViewHandler &&viewHandler)
{
using ViewTraits = QHttpServerRouterViewTraits<ViewHandler>;
static_assert(ViewTraits::Arguments::StaticAssert,
"ViewHandler arguments are in the wrong order or not supported");
return routeImpl<Rule, ViewHandler, ViewTraits>(pathPattern, context,
std::forward<ViewHandler>(viewHandler));
}
template<typename Rule = QHttpServerRouterRule, typename ViewHandler>
Rule *route(const QString &pathPattern,
ViewHandler &&viewHandler)
{
return route<Rule>(pathPattern, QHttpServerRequest::Method::AnyKnown,
this, std::forward<ViewHandler>(viewHandler));
}
template<typename Rule = QHttpServerRouterRule, typename ViewHandler>
Rule *route(const QString &pathPattern, QHttpServerRequest::Methods method,
ViewHandler &&viewHandler)
{
return route<Rule>(pathPattern, method,
this, std::forward<ViewHandler>(viewHandler));
}
#endif
#ifdef Q_QDOC
template <typename Functor>
void setMissingHandler(const QObject *receiver, Functor &&slot);
#else
template <typename Handler, if_missinghandler_prototype_compatible<Handler> = true>
void setMissingHandler(const typename QtPrivate::ContextTypeForFunctor<Handler>::ContextType *context,
Handler &&handler)
{
setMissingHandlerImpl(context,
QtPrivate::makeCallableObject<MissingHandlerPrototype>(
std::forward<Handler>(handler)));
}
#endif
#ifdef Q_QDOC
template <typename Functor>
void addAfterRequestHandler(const QObject *receiver, Functor &&slot);
#else
template <typename Handler, if_after_request_prototype_compatible<Handler> = true>
void addAfterRequestHandler(const typename QtPrivate::ContextTypeForFunctor<Handler>::ContextType *context,
Handler &&handler)
{
addAfterRequestHandlerImpl(context,
QtPrivate::makeCallableObject<AfterRequestPrototype>(
std::forward<Handler>(handler)));
}
#endif
void clearMissingHandler();
private:
void setMissingHandlerImpl(const QObject *context, QtPrivate::QSlotObjectBase *handler);
void addAfterRequestHandlerImpl(const QObject *context, QtPrivate::QSlotObjectBase *handler);
template<typename ViewHandler, typename ViewTraits>
auto createRouteHandler(const typename QtPrivate::ContextTypeForFunctor<ViewHandler>::ContextType *context,
ViewHandler &&viewHandler)
{
return [this, context, viewHandler](
const QRegularExpressionMatch &match,
const QHttpServerRequest &request,
QHttpServerResponder &responder) mutable {
auto boundViewHandler = QHttpServerRouterRule::bindCaptured<ViewHandler, ViewTraits>(context,
std::forward<ViewHandler>(viewHandler), match);
responseImpl<ViewTraits>(boundViewHandler, request, std::move(responder));
};
}
template<typename Rule, typename ViewHandler, typename ViewTraits>
Rule *routeImpl(const QString &pathPattern,
const typename QtPrivate::ContextTypeForFunctor<ViewHandler>::ContextType *context,
ViewHandler &&viewHandler)
{
auto routerHandler = createRouteHandler<ViewHandler, ViewTraits>(context,
std::forward<ViewHandler>(viewHandler));
auto rule = std::make_unique<Rule>(pathPattern, context, std::move(routerHandler));
return reinterpret_cast<Rule*>(router()->addRule<ViewHandler, ViewTraits>(std::move(rule)));
}
template<typename Rule, typename ViewHandler, typename ViewTraits>
Rule *routeImpl(const QString &pathPattern, QHttpServerRequest::Methods method,
const typename QtPrivate::ContextTypeForFunctor<ViewHandler>::ContextType *context,
ViewHandler &&viewHandler)
{
auto routerHandler = createRouteHandler<ViewHandler, ViewTraits>(context,
std::forward<ViewHandler>(viewHandler));
auto rule = std::make_unique<Rule>(pathPattern, method, context, std::move(routerHandler));
return reinterpret_cast<Rule*>(router()->addRule<ViewHandler, ViewTraits>(std::move(rule)));
}
template<typename ViewTraits, typename T>
void responseImpl(T &boundViewHandler, const QHttpServerRequest &request,
QHttpServerResponder &&responder)
{
if constexpr (ViewTraits::Arguments::PlaceholdersCount == 0) {
ResponseType<typename ViewTraits::ReturnType> response(boundViewHandler());
sendResponse(std::move(response), request, std::move(responder));
} else if constexpr (ViewTraits::Arguments::PlaceholdersCount == 1) {
if constexpr (ViewTraits::Arguments::Last::IsRequest::Value) {
ResponseType<typename ViewTraits::ReturnType> response(boundViewHandler(request));
sendResponse(std::move(response), request, std::move(responder));
} else {
static_assert(std::is_same_v<typename ViewTraits::ReturnType, void>,
"Handlers with responder argument must have void return type.");
boundViewHandler(responder);
}
} else if constexpr (ViewTraits::Arguments::PlaceholdersCount == 2) {
static_assert(std::is_same_v<typename ViewTraits::ReturnType, void>,
"Handlers with responder argument must have void return type.");
if constexpr (ViewTraits::Arguments::Last::IsRequest::Value) {
boundViewHandler(responder, request);
} else {
boundViewHandler(request, responder);
}
} else {
static_assert(dependent_false_v<ViewTraits>);
}
}
bool handleRequest(const QHttpServerRequest &request,
QHttpServerResponder &responder) override;
void missingHandler(const QHttpServerRequest &request,
QHttpServerResponder &responder) override;
void sendResponse(QHttpServerResponse &&response, const QHttpServerRequest &request,
QHttpServerResponder &&responder);
#if QT_CONFIG(future)
void sendResponse(QFuture<QHttpServerResponse> &&response, const QHttpServerRequest &request,
QHttpServerResponder &&responder);
#endif
};
QT_END_NAMESPACE
#endif // QHTTPSERVER_H