Revision control

Copy as Markdown

// Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#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 &response);
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 *context,
Functor &&slot);
template <typename Rule = QHttpServerRouterRule, typename Functor>
Rule *route(const QString &pathPattern,
const QObject *context,
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 *context, 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 *context, 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 !QT_CONFIG(future)
if constexpr (ViewTraits::Arguments::SpecialsCount == 0) {
// No specials, only capturables
static_assert(!std::is_void_v<typename ViewTraits::ReturnType>,
"Handlers without responder argument must have return value");
ResponseType<typename ViewTraits::ReturnType> response(boundViewHandler());
sendResponse(std::move(response), request, std::move(responder));
} else if constexpr (ViewTraits::Arguments::SpecialsCount == 1) {
// One special
static_assert(ViewTraits::Arguments::Last::IsSpecial::Value,
"Request or responder must be the last argument");
if constexpr (ViewTraits::Arguments::Last::IsRequest::Value) {
// One special: A request
static_assert(!std::is_void_v<typename ViewTraits::ReturnType>,
"Handlers without responder argument must have return value");
ResponseType<typename ViewTraits::ReturnType> response(boundViewHandler(request));
sendResponse(std::move(response), request, std::move(responder));
} else {
// One special: A responder
static_assert(std::is_void_v<typename ViewTraits::ReturnType>,
"Handlers with responder argument must have void return type");
if constexpr (ViewTraits::Arguments::Last::IsResponderRvalue::Value)
boundViewHandler(std::move(responder));
else
boundViewHandler(responder);
}
} else if constexpr (ViewTraits::Arguments::SpecialsCount == 2) {
// Both specials: A request and a responder
static_assert(ViewTraits::Arguments::Last::IsSpecial::Value
&& ViewTraits::Arguments::SecondLast::IsSpecial::Value,
"The two specials must be the two last arguments");
static_assert(!(ViewTraits::Arguments::Last::IsRequest::Value
&& ViewTraits::Arguments::SecondLast::IsRequest::Value),
"Cannot have multiple request arguments");
static_assert(!(ViewTraits::Arguments::Last::IsResponder::Value
&& ViewTraits::Arguments::SecondLast::IsResponder::Value),
"Cannot have multiple responder arguments");
static_assert(std::is_void_v<typename ViewTraits::ReturnType>,
"Handlers with a responder argument must have void return type");
if constexpr (ViewTraits::Arguments::Last::IsResponder::Value) {
// Both specials: Responder last
if constexpr (ViewTraits::Arguments::Last::IsResponderRvalue::Value)
boundViewHandler(request, std::move(responder));
else
boundViewHandler(request, responder);
} else {
// Both specials: Request last
if constexpr (ViewTraits::Arguments::SecondLast::IsResponderRvalue::Value)
boundViewHandler(std::move(responder), request);
else
boundViewHandler(responder, request);
}
} else {
static_assert(dependent_false_v<ViewTraits>, "Handlers will accept up to two specials");
}
#else // QT_CONFIG(future)
constexpr bool returnsVoidOrFutureVoid =
std::disjunction_v<std::is_void<typename ViewTraits::ReturnType>,
std::is_same<typename ViewTraits::ReturnType, QFuture<void>>>;
if constexpr (ViewTraits::Arguments::SpecialsCount == 0) {
// No specials, only capturables
static_assert(!returnsVoidOrFutureVoid,
"Handlers without responder argument must have return value");
ResponseType<typename ViewTraits::ReturnType> response(boundViewHandler());
sendResponse(std::move(response), request, std::move(responder));
} else if constexpr (ViewTraits::Arguments::SpecialsCount == 1) {
// One special
static_assert(ViewTraits::Arguments::Last::IsSpecial::Value,
"Request or responder must be the last argument");
if constexpr (ViewTraits::Arguments::Last::IsRequest::Value) {
// One special: A request
static_assert(!returnsVoidOrFutureVoid,
"Handlers without responder argument must have return value");
ResponseType<typename ViewTraits::ReturnType> response(boundViewHandler(request));
sendResponse(std::move(response), request, std::move(responder));
} else {
// One special: A responder
static_assert(
returnsVoidOrFutureVoid,
"Handlers with responder argument must have void or QFuture<void> return "
"type");
if constexpr (std::is_same_v<typename ViewTraits::ReturnType, QFuture<void>>) {
static_assert(ViewTraits::Arguments::Last::IsResponderRvalue::Value,
"Responder argument must be captured as Rvalue reference when "
"returning QFuture<void>");
(void)boundViewHandler(std::move(responder));
} else if constexpr (ViewTraits::Arguments::Last::IsResponderRvalue::Value) {
boundViewHandler(std::move(responder));
} else {
boundViewHandler(responder);
}
}
} else if constexpr (ViewTraits::Arguments::SpecialsCount == 2) {
// Both specials: A request and a responder
static_assert(ViewTraits::Arguments::Last::IsSpecial::Value
&& ViewTraits::Arguments::SecondLast::IsSpecial::Value,
"The two specials must be the two last arguments");
static_assert(!(ViewTraits::Arguments::Last::IsRequest::Value
&& ViewTraits::Arguments::SecondLast::IsRequest::Value),
"Cannot have multiple request arguments");
static_assert(!(ViewTraits::Arguments::Last::IsResponder::Value
&& ViewTraits::Arguments::SecondLast::IsResponder::Value),
"Cannot have multiple responder arguments");
static_assert(returnsVoidOrFutureVoid,
"Handlers with responder argument must have void or QFuture<void> "
"return type");
if constexpr (ViewTraits::Arguments::Last::IsResponder::Value) {
// Both specials: Responder last
if constexpr (std::is_same_v<typename ViewTraits::ReturnType, QFuture<void>>) {
static_assert(ViewTraits::Arguments::SecondLast::IsRequestValue::Value,
"Request argument must be passed by value when returning "
"QFuture<void>");
static_assert(ViewTraits::Arguments::Last::IsResponderRvalue::Value,
"Responder argument must be captured as Rvalue reference when "
"returning QFuture<void>");
(void)boundViewHandler(request, std::move(responder));
} else if constexpr (ViewTraits::Arguments::Last::IsResponderRvalue::Value) {
boundViewHandler(request, std::move(responder));
} else {
boundViewHandler(request, responder);
}
} else {
// Both specials: Request last
if constexpr (std::is_same_v<typename ViewTraits::ReturnType, QFuture<void>>) {
static_assert(ViewTraits::Arguments::Last::IsRequestValue::Value,
"Request argument must be captured by value when returning "
"QFuture<void>");
static_assert(ViewTraits::Arguments::SecondLast::IsResponderRvalue::Value,
"Responder argument must be captured as Rvalue reference when "
"returning QFuture<void>");
(void)boundViewHandler(std::move(responder), request);
} else if constexpr (ViewTraits::Arguments::SecondLast::IsResponderRvalue::Value) {
boundViewHandler(std::move(responder), request);
} else {
boundViewHandler(responder, request);
}
}
} else {
static_assert(dependent_false_v<ViewTraits>, "Handlers will accept up to two specials");
}
#endif
}
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