5#include "localserver.h"
7#include "protocolfastcgi.h"
8#include "protocolhttp.h"
9#include "protocolhttp2.h"
11#include "serverengine.h"
13#include "tcpserverbalancer.h"
18# include "windowsfork.h"
22# include "../EventLoopEPoll/eventdispatcher_epoll.h"
23# include "systemdnotify.h"
28#include <QCommandLineParser>
29#include <QCoreApplication>
31#include <QLoggingCategory>
32#include <QMetaProperty>
33#include <QPluginLoader>
35#include <QSocketNotifier>
40Q_LOGGING_CATEGORY(CUTELYST_SERVER,
"cutelyst.server", QtWarningMsg)
47 , d_ptr(new ServerPrivate(this))
51 if (!qEnvironmentVariableIsSet(
"QT_MESSAGE_PATTERN")) {
52 if (qEnvironmentVariableIsSet(
"JOURNAL_STREAM")) {
54 qSetMessagePattern(u
"%{category}[%{type}] %{message}"_s);
56 qSetMessagePattern(u
"%{pid}:%{threadid} %{category}[%{type}] %{message}"_s);
61 if (!qEnvironmentVariableIsSet(
"CUTELYST_QT_EVENT_LOOP")) {
62 qCInfo(CUTELYST_SERVER) <<
"Trying to install EPoll event loop";
67 auto cleanUp = [
this]() {
70 d->protoHTTP =
nullptr;
73 d->protoHTTP2 =
nullptr;
76 d->protoFCGI =
nullptr;
78 if (!d->engines.empty()) {
79 qDeleteAll(d->engines);
82 d->mainEngine =
nullptr;
84 qDeleteAll(d->servers);
87 delete d->genericFork;
88 d->genericFork =
nullptr;
98 std::cout <<
"Cutelyst-Server terminated" <<
'\n';
109 qtTrId(
"cutelystd-cli-desc"));
118 qtTrId(
"cutelystd-opt-ini-desc"),
121 qtTrId(
"cutelystd-opt-value-file"));
129 qtTrId(
"cutelystd-opt-json-desc"),
130 qtTrId(
"cutelystd-opt-value-file"));
137 qtTrId(
"cutelystd-opt-chdir-desc"),
140 qtTrId(
"cutelystd-opt-value-directory"));
147 qtTrId(
"cutelystd-opt-chdir2-desc"),
148 qtTrId(
"cutelystd-opt-value-directory"));
155 qtTrId(
"cutelystd-opt-lazy-desc"));
161 qtTrId(
"cutelystd-opt-application-desc"),
162 qtTrId(
"cutelystd-opt-value-file"));
169 qtTrId(
"cutelystd-opt-threads-desc"),
172 qtTrId(
"cutelystd-opt-threads-value"));
180 qtTrId(
"cutelystd-opt-processes-desc"),
183 qtTrId(
"cutelystd-opt-processes-value"));
190 qtTrId(
"cutelystd-opt-master-desc"));
196 qtTrId(
"cutelystd-opt-listen-desc"),
199 qtTrId(
"cutelystd-opt-value-size"));
205 qtTrId(
"cutelystd-opt-buffer-size-desc"),
208 qtTrId(
"cutelystd-opt-value-bytes"));
217 qtTrId(
"cutelystd-opt-post-buffering-desc"),
218 qtTrId(
"cutelystd-opt-value-bytes"));
222 u
"post-buffering-bufsize"_s,
225 qtTrId(
"cutelystd-opt-post-buffering-bufsize-desc"),
226 qtTrId(
"cutelystd-opt-value-bytes"));
227 parser.
addOption(postBufferingBufsizeOpt);
232 qtTrId(
"cutelystd-opt-http-socket-desc"),
235 qtTrId(
"cutelystd-opt-value-address"));
239 {u
"http2-socket"_s, u
"h2"_s},
242 qtTrId(
"cutelystd-opt-http2-socket-desc"),
243 qtTrId(
"cutelystd-opt-value-address"));
249 qtTrId(
"cutelystd-opt-http2-header-table-size-desc"),
250 qtTrId(
"cutelystd-opt-value-size"));
251 parser.
addOption(http2HeaderTableSizeOpt);
256 qtTrId(
"cutelystd-opt-upgrade-h2c-desc"));
262 qtTrId(
"cutelystd-opt-https-h2-desc"));
268 qtTrId(
"cutelystd-opt-https-socket-desc"),
270 qtTrId(
"cutelystd-opt-value-httpsaddress"));
277 qtTrId(
"cutelystd-opt-fastcgi-socket-desc"),
278 qtTrId(
"cutelystd-opt-value-address"));
285 qtTrId(
"cutelystd-opt-socket-access-desc"),
288 qtTrId(
"cutelystd-opt-socket-access-value"));
294 qtTrId(
"cutelystd-opt-socket-timeout-desc"),
297 qtTrId(
"cutelystd-opt-socket-timeout-value"));
306 qtTrId(
"cutelystd-opt-static-map-desc"),
309 qtTrId(
"cutelystd-opt-value-static-map"));
316 qtTrId(
"cutelystd-opt-static-map2-desc"),
319 qtTrId(
"cutelystd-opt-value-static-map"));
326 qtTrId(
"cutelystd-opt-auto-restart-desc"));
334 qtTrId(
"cutelystd-opt-touch-reload-desc"),
335 qtTrId(
"cutelystd-opt-value-file"));
341 qtTrId(
"cutelystd-opt-tcp-nodelay-desc"));
347 qtTrId(
"cutelystd-opt-so-keepalive-desc"));
354 qtTrId(
"cutelystd-opt-socket-sndbuf-desc"),
355 qtTrId(
"cutelystd-opt-value-bytes"));
362 qtTrId(
"cutelystd-opt-socket-rcvbuf-desc"),
363 qtTrId(
"cutelystd-opt-value-bytes"));
370 qtTrId(
"cutelystd-opt-websocket-max-size-desc"),
373 qtTrId(
"cutelystd-opt-websocket-max-size-value"));
379 qtTrId(
"cutelystd-opt-pidfile-desc"),
382 qtTrId(
"cutelystd-opt-value-pidfile"));
388 qtTrId(
"cutelystd-opt-pidfile2-desc"),
389 qtTrId(
"cutelystd-opt-value-pidfile"));
396 qtTrId(
"cutelystd-opt-stop-desc"),
397 qtTrId(
"cutelystd-opt-value-pidfile"));
403 qtTrId(
"cutelystd-opt-uid-desc"),
406 qtTrId(
"cutelystd-opt-uid-value"));
412 qtTrId(
"cutelystd-opt-gid-desc"),
415 qtTrId(
"cutelystd-opt-gid-value"));
421 qtTrId(
"cutelystd-opt-no-init-groups-desc"));
427 qtTrId(
"cutelystd-opt-chown-socket-desc"),
430 qtTrId(
"cutelystd-opt-chown-socket-value"));
436 qtTrId(
"cutelystd-opt-umask-desc"),
439 qtTrId(
"cutelystd-opt-umask-value"));
446 qtTrId(
"cutelystd-opt-cpu-affinity-desc"),
449 qtTrId(
"cutelystd-opt-cpu-affinity-value"));
457 qtTrId(
"cutelystd-opt-reuse-port-desc"));
462 u
"experimental-thread-balancer"_s,
465 qtTrId(
"cutelystd-opt-experimental-thread-balancer-desc"));
471 qtTrId(
"cutelystd-opt-using-frontend-proxy-desc"));
477 setIni(parser.
values(iniOpt));
479 setJson(parser.
values(jsonOpt));
481 if (parser.
isSet(chdirOpt)) {
482 setChdir(parser.
value(chdirOpt));
485 if (parser.
isSet(chdir2Opt)) {
486 setChdir2(parser.
value(chdir2Opt));
489 if (parser.
isSet(threadsOpt)) {
490 setThreads(parser.
value(threadsOpt));
493 if (parser.
isSet(socketAccessOpt)) {
494 setSocketAccess(parser.
value(socketAccessOpt));
497 if (parser.
isSet(socketTimeoutOpt)) {
499 auto size = parser.
value(socketTimeoutOpt).
toInt(&ok);
500 setSocketTimeout(size);
501 if (!ok || size < 0) {
506 if (parser.
isSet(pidfileOpt)) {
507 setPidfile(parser.
value(pidfileOpt));
510 if (parser.
isSet(pidfile2Opt)) {
511 setPidfile2(parser.
value(pidfile2Opt));
515 if (parser.
isSet(stopOpt)) {
516 UnixFork::stopSERVER(parser.
value(stopOpt));
519 if (parser.
isSet(processesOpt)) {
520 setProcesses(parser.
value(processesOpt));
523 if (parser.
isSet(uidOpt)) {
524 setUid(parser.
value(uidOpt));
527 if (parser.
isSet(gidOpt)) {
528 setGid(parser.
value(gidOpt));
531 if (parser.
isSet(noInitgroupsOpt)) {
532 setNoInitgroups(
true);
535 if (parser.
isSet(chownSocketOpt)) {
536 setChownSocket(parser.
value(chownSocketOpt));
539 if (parser.
isSet(umaskOpt)) {
540 setUmask(parser.
value(umaskOpt));
543 if (parser.
isSet(cpuAffinityOpt)) {
545 auto value = parser.
value(cpuAffinityOpt).
toInt(&ok);
546 setCpuAffinity(value);
547 if (!ok || value < 0) {
554 if (parser.
isSet(reusePortOpt)) {
559 if (parser.
isSet(lazyOpt)) {
563 if (parser.
isSet(listenQueueOpt)) {
565 auto size = parser.
value(listenQueueOpt).
toInt(&ok);
566 setListenQueue(size);
567 if (!ok || size < 1) {
572 if (parser.
isSet(bufferSizeOpt)) {
574 auto size = parser.
value(bufferSizeOpt).
toInt(&ok);
576 if (!ok || size < 1) {
581 if (parser.
isSet(postBufferingOpt)) {
584 setPostBuffering(size);
585 if (!ok || size < 1) {
590 if (parser.
isSet(postBufferingBufsizeOpt)) {
593 setPostBufferingBufsize(size);
594 if (!ok || size < 1) {
599 if (parser.
isSet(applicationOpt)) {
600 setApplication(parser.
value(applicationOpt));
603 if (parser.
isSet(masterOpt)) {
607 if (parser.
isSet(autoReloadOpt)) {
611 if (parser.
isSet(tcpNoDelay)) {
615 if (parser.
isSet(soKeepAlive)) {
616 setSoKeepalive(
true);
619 if (parser.
isSet(upgradeH2cOpt)) {
623 if (parser.
isSet(httpsH2Opt)) {
627 if (parser.
isSet(socketSndbufOpt)) {
629 auto size = parser.
value(socketSndbufOpt).
toInt(&ok);
630 setSocketSndbuf(size);
631 if (!ok || size < 1) {
636 if (parser.
isSet(socketRcvbufOpt)) {
638 auto size = parser.
value(socketRcvbufOpt).
toInt(&ok);
639 setSocketRcvbuf(size);
640 if (!ok || size < 1) {
645 if (parser.
isSet(wsMaxSize)) {
647 auto size = parser.
value(wsMaxSize).
toInt(&ok);
648 setWebsocketMaxSize(size);
649 if (!ok || size < 1) {
654 if (parser.
isSet(http2HeaderTableSizeOpt)) {
656 auto size = parser.
value(http2HeaderTableSizeOpt).
toUInt(&ok);
657 setHttp2HeaderTableSize(size);
658 if (!ok || size < 1) {
663 if (parser.
isSet(frontendProxy)) {
664 setUsingFrontendProxy(
true);
667 setHttpSocket(httpSocket() + parser.
values(httpSocketOpt));
669 setHttp2Socket(http2Socket() + parser.
values(http2SocketOpt));
671 setHttpsSocket(httpsSocket() + parser.
values(httpsSocketOpt));
673 setFastcgiSocket(fastcgiSocket() + parser.
values(fastcgiSocketOpt));
675 setStaticMap(staticMap() + parser.
values(staticMapOpt));
677 setStaticMap2(staticMap2() + parser.
values(staticMap2Opt));
679 setTouchReload(touchReload() + parser.
values(touchReloadOpt));
681 d->threadBalancer = parser.
isSet(threadBalancerOpt);
687 std::cout <<
"Cutelyst-Server starting" <<
'\n';
689 if (!qEnvironmentVariableIsSet(
"CUTELYST_SERVER_IGNORE_MASTER") && !d->master) {
691 <<
"*** WARNING: you are running Cutelyst-Server without its master process manager ***"
696 if (d->processes == -1 && d->threads == -1) {
697 d->processes = UnixFork::idealProcessCount();
698 d->threads = UnixFork::idealThreadCount() / d->processes;
699 }
else if (d->processes == -1) {
700 d->processes = UnixFork::idealThreadCount();
701 }
else if (d->threads == -1) {
702 d->threads = UnixFork::idealThreadCount();
705 if (d->processes == 0 && d->master) {
708 delete d->genericFork;
709 d->genericFork =
new UnixFork(d->processes, qMax(d->threads, 1), !d->userEventLoop,
this);
711 if (d->processes == -1) {
714 if (d->threads == -1) {
717 delete d->genericFork;
726 if (d->master && d->lazy) {
727 if (d->autoReload && !d->application.isEmpty()) {
728 d->touchReload.append(d->application);
730 d->genericFork->setTouchReload(d->touchReload);
734 if (d->master && !d->genericFork->continueMaster(&ret)) {
739 if (systemdNotify::is_systemd_notify_available()) {
741 sd->setWatchdog(
true, systemdNotify::sd_watchdog_enabled(
true));
743 sd->sendStatus(qApp->applicationName().toLatin1() +
" is ready");
746 connect(d, &ServerPrivate::postForked, sd, [sd] { sd->setWatchdog(
false); });
747 qInfo(CUTELYST_SERVER) <<
"systemd notify detected";
755 if (!d->listenTcpSockets()) {
757 ? QStringLiteral(
"No specified sockets were able to be opened")
758 : d->lastListenError;
764 if (!d->writePidFile(d->pidfile)) {
770 bool isListeningLocalSockets =
false;
771 if (!d->chownSocket.isEmpty()) {
772 if (!d->listenLocalSockets()) {
777 isListeningLocalSockets =
true;
780 if (!d->umask.isEmpty() && !UnixFork::setUmask(d->umask.toLatin1())) {
784 if (!UnixFork::setGidUid(d->gid, d->uid, d->noInitgroups)) {
790 if (!isListeningLocalSockets) {
792 d->listenLocalSockets();
798 if (!d->listenTcpSockets()) {
800 ? QStringLiteral(
"No specified sockets were able to be opened")
801 : d->lastListenError;
807 if (d->servers.empty()) {
808 std::cout <<
"Please specify a socket to listen to" <<
'\n';
814 d->writePidFile(d->pidfile2);
816 if (!d->chdir.isEmpty()) {
817 std::cout <<
"Changing directory to: " << d->chdir.toLatin1().constData() <<
'\n';
828 if (!d->setupApplication()) {
835 if (d->userEventLoop) {
840 ret = d->genericFork->exec(d->lazy, d->master);
851 QStringLiteral(
"Server not fully stopped. Wait for shutdown to complete."));
858 d->userEventLoop =
true;
859 d->workersNotRunning = 1;
860 d->lastListenError.clear();
865 qputenv(
"CUTELYST_SERVER_IGNORE_MASTER", QByteArrayLiteral(
"1"));
867 if (
exec(app) == 0) {
877 if (d->userEventLoop) {
878 for (
QObject *obj : d->servers) {
879 if (
auto *tcp = qobject_cast<QTcpServer *>(obj)) {
881 }
else if (
auto *local = qobject_cast<LocalServer *>(obj)) {
889ServerPrivate::~ServerPrivate()
896bool ServerPrivate::listenTcpSockets()
898 lastListenError.clear();
900 if (httpSockets.isEmpty() && httpsSockets.isEmpty() && http2Sockets.isEmpty() &&
901 fastcgiSockets.isEmpty()) {
907 bool httpOk = std::ranges::all_of(httpSockets, [
this](
const auto &socket) {
908 return listenTcp(socket, getHttpProto(),
false);
915 bool httpsOk = std::ranges::all_of(httpsSockets, [
this](
const auto &socket) {
916 return listenTcp(socket, getHttpProto(),
true);
923 bool http2Ok = std::ranges::all_of(http2Sockets, [
this](
const auto &socket) {
924 return listenTcp(socket, getHttp2Proto(),
false);
931 bool allOk = std::ranges::all_of(fastcgiSockets, [
this](
const QString &socket) {
932 return listenTcp(socket, getFastCgiProto(),
false);
938bool ServerPrivate::listenTcp(
const QString &line,
Protocol *protocol,
bool secure)
947 server->setBalancer(threadBalancer);
948 const bool ret = server->listen(line, protocol, secure);
950 if (!ret || !server->socketDescriptor()) {
952 server->bindError().
isEmpty() ? server->errorString() : server->bindError();
954 lastListenError = QStringLiteral(
"Failed to listen on %1: %2").arg(line, err);
957 QStringLiteral(
"Failed to listen on %1: no socket descriptor").arg(line);
959 qCWarning(CUTELYST_SERVER) << lastListenError;
964 auto qEnum = Protocol::staticMetaObject.enumerator(0);
965 std::cout << qEnum.valueToKey(
static_cast<int>(protocol->type())) <<
" socket "
967 <<
" bound to TCP address " << server->serverName().constData() <<
" fd "
969 servers.emplace_back(server);
973bool ServerPrivate::listenLocalSockets()
982 std::vector<int> fds = systemdNotify::listenFds();
985 if (server->listen(fd)) {
986 const QString name = server->serverName();
987 const QString fullName = server->fullServerName();
991 protocol = getHttpProto();
993 protocol = getHttp2Proto();
995 protocol = getFastCgiProto();
997 std::cerr <<
"systemd activated socket does not match any configured socket"
1001 server->setProtocol(protocol);
1002 server->pauseAccepting();
1004 auto qEnum = Protocol::staticMetaObject.enumerator(0);
1005 std::cout << qEnum.valueToKey(
static_cast<int>(protocol->type())) <<
" socket "
1007 <<
" bound to LOCAL address " << qPrintable(fullName) <<
" fd "
1009 servers.push_back(server);
1011 std::cerr <<
"Failed to listen on activated LOCAL FD: "
1013 << qPrintable(server->errorString()) <<
'\n';
1020 const auto httpConst = http;
1021 for (
const auto &socket : httpConst) {
1022 ret |= listenLocal(socket, getHttpProto());
1025 const auto http2Const = http2;
1026 for (
const auto &socket : http2Const) {
1027 ret |= listenLocal(socket, getHttp2Proto());
1030 const auto fastcgiConst = fastcgi;
1031 for (
const auto &socket : fastcgiConst) {
1032 ret |= listenLocal(socket, getFastCgiProto());
1038bool ServerPrivate::listenLocal(
const QString &line,
Protocol *protocol)
1045 server->setProtocol(protocol);
1046 if (!socketAccess.isEmpty()) {
1048 if (socketAccess.contains(u
'u')) {
1052 if (socketAccess.contains(u
'g')) {
1056 if (socketAccess.contains(u
'o')) {
1059 server->setSocketOptions(options);
1063 server->setListenBacklogSize(listenQueue);
1064 ret = server->listen(line);
1065 server->pauseAccepting();
1067 if (!ret || !server->socket()) {
1068 std::cerr <<
"Failed to listen on LOCAL: " << qPrintable(line) <<
" : "
1069 << qPrintable(server->errorString()) <<
'\n';
1074 if (!chownSocket.isEmpty()) {
1075 UnixFork::chownSocket(line, chownSocket);
1078 auto qEnum = Protocol::staticMetaObject.enumerator(0);
1079 std::cout << qEnum.valueToKey(
static_cast<int>(protocol->type())) <<
" socket "
1081 <<
" bound to LOCAL address " << qPrintable(line) <<
" fd "
1083 servers.push_back(server);
1089void Server::setApplication(
const QString &application)
1094 if (loader.fileName().isEmpty()) {
1099 d->application = loader.fileName();
1107 return d->application;
1110void Server::setThreads(
const QString &threads)
1124 if (d->threads == -1) {
1130void Server::setProcesses(
const QString &process)
1137 d->processes = process.
toInt();
1146 if (d->processes == -1) {
1152void Server::setChdir(
const QString &chdir)
1165void Server::setHttpSocket(
const QStringList &httpSocket)
1168 d->httpSockets = httpSocket;
1175 return d->httpSockets;
1178void Server::setHttp2Socket(
const QStringList &http2Socket)
1181 d->http2Sockets = http2Socket;
1188 return d->http2Sockets;
1191void Server::setHttp2HeaderTableSize(quint32 headerTableSize)
1194 d->http2HeaderTableSize = headerTableSize;
1198quint32 Server::http2HeaderTableSize()
const
1201 return d->http2HeaderTableSize;
1204void Server::setUpgradeH2c(
bool enable)
1207 d->upgradeH2c = enable;
1211bool Server::upgradeH2c()
const
1214 return d->upgradeH2c;
1217void Server::setHttpsH2(
bool enable)
1220 d->httpsH2 = enable;
1224bool Server::httpsH2()
const
1230void Server::setHttpsSocket(
const QStringList &httpsSocket)
1233 d->httpsSockets = httpsSocket;
1240 return d->httpsSockets;
1243void Server::setFastcgiSocket(
const QStringList &fastcgiSocket)
1246 d->fastcgiSockets = fastcgiSocket;
1253 return d->fastcgiSockets;
1256void Server::setSocketAccess(
const QString &socketAccess)
1259 d->socketAccess = socketAccess;
1263QString Server::socketAccess()
const
1266 return d->socketAccess;
1269void Server::setSocketTimeout(
int timeout)
1272 d->socketTimeout = timeout;
1276int Server::socketTimeout()
const
1279 return d->socketTimeout;
1282void Server::setChdir2(
const QString &chdir2)
1298 d->ini.append(files);
1299 d->ini.removeDuplicates();
1302 for (
const QString &file : files) {
1303 if (!d->configLoaded.contains(file)) {
1304 auto fileToLoad = std::make_pair(file, ServerPrivate::ConfigFormat::Ini);
1305 if (!d->configToLoad.contains(fileToLoad)) {
1306 qCDebug(CUTELYST_SERVER) <<
"Enqueue INI config file:" << file;
1307 d->configToLoad.enqueue(fileToLoad);
1324 d->json.append(files);
1325 d->json.removeDuplicates();
1328 for (
const QString &file : files) {
1329 if (!d->configLoaded.contains(file)) {
1330 auto fileToLoad = std::make_pair(file, ServerPrivate::ConfigFormat::Json);
1331 if (!d->configToLoad.contains(fileToLoad)) {
1332 qCDebug(CUTELYST_SERVER) <<
"Enqueue JSON config file:" << file;
1333 d->configToLoad.enqueue(fileToLoad);
1347void Server::setStaticMap(
const QStringList &staticMap)
1350 d->staticMaps = staticMap;
1357 return d->staticMaps;
1360void Server::setStaticMap2(
const QStringList &staticMap)
1363 d->staticMaps2 = staticMap;
1370 return d->staticMaps2;
1373void Server::setMaster(
bool enable)
1376 if (!qEnvironmentVariableIsSet(
"CUTELYST_SERVER_IGNORE_MASTER")) {
1388void Server::setAutoReload(
bool enable)
1392 d->autoReload =
true;
1397bool Server::autoReload()
const
1400 return d->autoReload;
1403void Server::setTouchReload(
const QStringList &files)
1406 d->touchReload = files;
1413 return d->touchReload;
1416void Server::setListenQueue(
int size)
1419 d->listenQueue = size;
1423int Server::listenQueue()
const
1426 return d->listenQueue;
1429void Server::setBufferSize(
int size)
1433 qCWarning(CUTELYST_SERVER) <<
"Buffer size must be at least 4096 bytes, ignoring";
1436 d->bufferSize = size;
1440int Server::bufferSize()
const
1443 return d->bufferSize;
1446void Server::setPostBuffering(qint64 size)
1449 d->postBuffering = size;
1453qint64 Server::postBuffering()
const
1456 return d->postBuffering;
1459void Server::setPostBufferingBufsize(qint64 size)
1463 qCWarning(CUTELYST_SERVER) <<
"Post buffer size must be at least 4096 bytes, ignoring";
1466 d->postBufferingBufsize = size;
1470qint64 Server::postBufferingBufsize()
const
1473 return d->postBufferingBufsize;
1476void Server::setTcpNodelay(
bool enable)
1479 d->tcpNodelay = enable;
1483bool Server::tcpNodelay()
const
1486 return d->tcpNodelay;
1489void Server::setSoKeepalive(
bool enable)
1492 d->soKeepalive = enable;
1496bool Server::soKeepalive()
const
1499 return d->soKeepalive;
1502void Server::setSocketSndbuf(
int value)
1505 d->socketSendBuf = value;
1509int Server::socketSndbuf()
const
1512 return d->socketSendBuf;
1515void Server::setSocketRcvbuf(
int value)
1518 d->socketReceiveBuf = value;
1522int Server::socketRcvbuf()
const
1525 return d->socketReceiveBuf;
1528void Server::setWebsocketMaxSize(
int value)
1531 d->websocketMaxSize = value * 1024;
1535int Server::websocketMaxSize()
const
1538 return d->websocketMaxSize / 1024;
1541void Server::setPidfile(
const QString &file)
1554void Server::setPidfile2(
const QString &file)
1567void Server::setUid(
const QString &uid)
1582void Server::setGid(
const QString &gid)
1597void Server::setNoInitgroups(
bool enable)
1601 d->noInitgroups = enable;
1606bool Server::noInitgroups()
const
1609 return d->noInitgroups;
1612void Server::setChownSocket(
const QString &chownSocket)
1616 d->chownSocket = chownSocket;
1621QString Server::chownSocket()
const
1624 return d->chownSocket;
1627void Server::setUmask(
const QString &value)
1642void Server::setCpuAffinity(
int value)
1646 d->cpuAffinity = value;
1651int Server::cpuAffinity()
const
1654 return d->cpuAffinity;
1657void Server::setReusePort(
bool enable)
1661 d->reusePort = enable;
1668bool Server::reusePort()
const
1671 return d->reusePort;
1674void Server::setLazy(
bool enable)
1687void Server::setUsingFrontendProxy(
bool enable)
1690 d->usingFrontendProxy = enable;
1694bool Server::usingFrontendProxy()
const
1697 return d->usingFrontendProxy;
1706bool ServerPrivate::setupApplication()
1712 if (!engines.empty() || mainEngine) {
1713 qDeleteAll(engines);
1715 mainEngine =
nullptr;
1719 std::cout <<
"Loading application: " << application.
toLatin1().
constData() <<
'\n';
1722 if (!loader.load()) {
1723 qCCritical(CUTELYST_SERVER) <<
"Could not load application:" << loader.errorString();
1727 QObject *instance = loader.instance();
1729 qCCritical(CUTELYST_SERVER) <<
"Could not get a QObject instance: %s\n"
1730 << loader.errorString();
1734 localApp = qobject_cast<Cutelyst::Application *>(instance);
1736 qCCritical(CUTELYST_SERVER)
1737 <<
"Could not cast Cutelyst::Application from instance: %s\n"
1738 << loader.errorString();
1749 if (!chdir2.isEmpty()) {
1750 std::cout <<
"Changing directory2 to: " << chdir2.toLatin1().constData() <<
'\n';
1759 mainEngine = createEngine(localApp, 0);
1760 for (
int i = 1; i < threads; ++i) {
1761 if (createEngine(localApp, i)) {
1762 ++workersNotRunning;
1766 mainEngine = createEngine(localApp, 0);
1767 workersNotRunning = 1;
1771 std::cerr <<
"Application failed to init, cheaping..." <<
'\n';
1778void ServerPrivate::engineShutdown(
ServerEngine *engine)
1780 const auto engineThread = engine->
thread();
1783 auto [first, last] = std::ranges::remove(engines, engine);
1784 engines.erase(first, last);
1785 checkEngineShutdown();
1787 engineThread->quit();
1789 auto [first, last] = std::ranges::remove(engines, engine);
1790 engines.erase(first, last);
1793 checkEngineShutdown();
1796void ServerPrivate::checkEngineShutdown()
1798 if (engines.empty()) {
1799 if (userEventLoop) {
1801 Q_EMIT q->stopped();
1808void ServerPrivate::workerStarted()
1813 if (--workersNotRunning == 0) {
1818bool ServerPrivate::postFork(
int workerId)
1823 if (!setupApplication()) {
1824 Q_EMIT q->errorOccured(qtTrId(
"cutelystd-err-fail-setup-app"));
1829 if (engines.size() > 1) {
1830 qCDebug(CUTELYST_SERVER) <<
"Starting threads";
1835 if (thread != qApp->thread()) {
1837 if (!qEnvironmentVariableIsSet(
"CUTELYST_QT_EVENT_LOOP")) {
1847 Q_EMIT postForked(workerId);
1855 qApp->processEvents();
1861bool ServerPrivate::writePidFile(
const QString &filename)
1867 QFile file(filename);
1869 std::cerr <<
"Failed write pid file " << qPrintable(filename) <<
'\n';
1873 std::cout <<
"Writing pidfile to " << qPrintable(filename) <<
'\n';
1884 if (workerCore > 0) {
1887 qFatal(
"*** FATAL *** Could not create a NEW instance of your Cutelyst::Application, "
1888 "make sure your constructor has Q_INVOKABLE macro or disable threaded mode.");
1892 auto engine =
new ServerEngine(app, workerCore, opt, q);
1896 connect(
this, &ServerPrivate::postForked, engine, &ServerEngine::postFork, forkConnection);
1898 &ServerEngine::shutdownCompleted,
1900 &ServerPrivate::engineShutdown,
1902 connect(engine, &ServerEngine::started,
this, &ServerPrivate::workerStarted, forkConnection);
1905 engine->setServers(servers);
1906 if (!engine->
init()) {
1907 std::cerr <<
"Application failed to init(), cheaping core: " << workerCore <<
'\n';
1912 engines.push_back(engine);
1915 if (workerCore > 0) {
1920 auto thread =
new QThread(
this);
1929void ServerPrivate::loadConfig()
1931 if (loadingConfig) {
1935 loadingConfig =
true;
1937 if (configToLoad.isEmpty()) {
1938 loadingConfig =
false;
1942 auto fileToLoad = configToLoad.dequeue();
1944 if (fileToLoad.first.isEmpty()) {
1945 qCWarning(CUTELYST_SERVER) <<
"Can not load config from empty config file name";
1946 loadingConfig =
false;
1950 if (configLoaded.contains(fileToLoad.first)) {
1951 loadingConfig =
false;
1955 configLoaded.append(fileToLoad.first);
1957 QVariantMap loadedConfig;
1958 switch (fileToLoad.second) {
1959 case ConfigFormat::Ini:
1960 qCInfo(CUTELYST_SERVER) <<
"Loading INI configuratin:" << fileToLoad.first;
1963 case ConfigFormat::Json:
1964 qCInfo(CUTELYST_SERVER) <<
"Loading JSON configuration:" << fileToLoad.first;
1969 for (
const auto &[key, value] : std::as_const(loadedConfig).asKeyValueRange()) {
1970 if (config.contains(key)) {
1971 QVariantMap currentMap = config.value(key).toMap();
1972 const QVariantMap loadedMap = value.toMap();
1973 for (
const auto &[mapKey, mapValue] : loadedMap.asKeyValueRange()) {
1974 currentMap.insert(mapKey, mapValue);
1976 config.insert(key, currentMap);
1978 config.insert(key, value);
1982 QVariantMap sessionConfig = loadedConfig.value(u
"server"_s).toMap();
1984 applyConfig(sessionConfig);
1986 opt.insert(sessionConfig);
1988 loadingConfig =
false;
1990 if (!configToLoad.empty()) {
1995void ServerPrivate::applyConfig(
const QVariantMap &config)
1999 for (
const auto &[key, value] : config.asKeyValueRange()) {
2009 if (prop.
userType() == value.userType()) {
2012 prop.
write(q, currentValues + value.toStringList());
2014 prop.
write(q, value);
2020 prop.
write(q, value);
2025Protocol *ServerPrivate::getHttpProto()
2047Protocol *ServerPrivate::getFastCgiProto()
2056#include "moc_server.cpp"
2057#include "moc_server_p.cpp"
The Cutelyst application.
static QVariantMap loadJsonConfig(const QString &filename)
void setConfig(const QVariantMap &config)
static QVariantMap loadIniConfig(const QString &filename)
virtual bool init() override
void errorOccured(const QString &error)
bool start(Cutelyst::Application *app=nullptr)
int exec(Cutelyst::Application *app=nullptr)
void parseCommandLine(const QStringList &args)
Server(QObject *parent=nullptr)
QVariantMap config() const noexcept
The Cutelyst namespace holds all public Cutelyst API.
const char * constData() const const
QByteArray number(double n, char format, int precision)
QCommandLineOption addHelpOption()
bool addOption(const QCommandLineOption &option)
QCommandLineOption addVersionOption()
bool isSet(const QCommandLineOption &option) const const
void process(const QCoreApplication &app)
void setApplicationDescription(const QString &description)
void showHelp(int exitCode)
QString value(const QCommandLineOption &option) const const
QStringList values(const QCommandLineOption &option) const const
void addLibraryPath(const QString &path)
void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher)
bool setCurrent(const QString &path)
bool removeOne(const AT &t)
bool removeServer(const QString &name)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void moveToThread(QThread *targetThread)
void setParent(QObject *parent)
QThread * thread() const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
QByteArray toLatin1() const const
qlonglong toLongLong(bool *ok, int base) const const
uint toUInt(bool *ok, int base) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QThread * currentThread()
void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher)
void start(QThread::Priority priority)
QStringList toStringList() const const