<commit_before>/*
 * Copyright (C) 2016-2018 <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
#include "wsgi_p.h"

#include "protocol.h"
#include "protocolhttp.h"
#include "protocolhttp2.h"
#include "protocolfastcgi.h"
#include "cwsgiengine.h"
#include "socket.h"
#include "tcpserverbalancer.h"
#include "localserver.h"

#ifdef Q_OS_UNIX
#include "unixfork.h"
#else
#include "windowsfork.h"
#endif

#ifdef Q_OS_LINUX
#include "../EventLoopEPoll/eventdispatcher_epoll.h"
#include "systemdnotify.h"
#endif

#include <QCoreApplication>
#include <QCommandLineParser>
#include <QUrl>
#include <QPluginLoader>
#include <QThread>
#include <QLoggingCategory>
#include <QSettings>
#include <QSocketNotifier>
#include <QMetaProperty>
#include <QTimer>
#include <QDir>
#include <QMutex>

#include <iostream>

Q_LOGGING_CATEGORY(CUTELYST_WSGI, "wsgi", QtWarningMsg)

using namespace CWSGI;
using namespace Cutelyst;

WSGI::WSGI(QObject *parent) : QObject(parent),
    d_ptr(new WSGIPrivate(this))
{
    if (qEnvironmentVariableIsEmpty("QT_MESSAGE_PATTERN")) {
        qSetMessagePattern(QLatin1String("%{pid}:%{threadid} %{category}[%{type}] %{message}"));
    }

#ifdef Q_OS_LINUX
    if (!qEnvironmentVariableIsSet("CUTELYST_QT_EVENT_LOOP")) {
        std::cout << "Installing EPoll event loop" << std::endl;
        QCoreApplication::setEventDispatcher(new EventDispatcherEPoll);
    }
#endif
}

WSGI::~WSGI()
{
    Q_D(WSGI);

    delete d->protoHTTP;
    delete d->protoHTTP2;
    delete d->protoFCGI;

    std::cout << "Cutelyst-WSGI terminated" << std::endl;
}

void WSGI::parseCommandLine(const QStringList &arguments)
{
    Q_D(WSGI);

    QCommandLineParser parser;
    parser.setApplicationDescription(QCoreApplication::translate("main", "Fast, developer-friendly WSGI server"));
    parser.addHelpOption();
    parser.addVersionOption();

    QCommandLineOption iniOpt(QStringLiteral("ini"),
                              QCoreApplication::translate("main", "load config from ini file"),
                              QCoreApplication::translate("main", "file"));
    parser.addOption(iniOpt);

    QCommandLineOption jsonOpt({ QStringLiteral("j"), QStringLiteral("json") },
                           QCoreApplication::translate("main", "load config from JSON file"),
                           QCoreApplication::translate("main", "file"));
    parser.addOption(jsonOpt);

    QCommandLineOption chdir(QStringLiteral("chdir"),
                             QCoreApplication::translate("main", "chdir to specified directory before apps loading"),
                             QCoreApplication::translate("main", "directory"));
    parser.addOption(chdir);

    QCommandLineOption chdir2(QStringLiteral("chdir2"),
                              QCoreApplication::translate("main", "chdir to specified directory afterapps loading"),
                              QCoreApplication::translate("main", "directory"));
    parser.addOption(chdir2);

    QCommandLineOption lazyOption(QStringLiteral("lazy"),
                                  QCoreApplication::translate("main", "set lazy mode (load app in workers instead of master)"));
    parser.addOption(lazyOption);

    QCommandLineOption application({ QStringLiteral("application"), QStringLiteral("a") },
                                   QCoreApplication::translate("main", "Application to load"),
                                   QCoreApplication::translate("main", "file"));
    parser.addOption(application);

    QCommandLineOption threads({ QStringLiteral("threads"), QStringLiteral("t") },
                               QCoreApplication::translate("main", "Number of thread to use"),
                               QCoreApplication::translate("main", "threads"));
    parser.addOption(threads);

#ifdef Q_OS_UNIX
    QCommandLineOption processes({ QStringLiteral("processes"), QStringLiteral("p") },
                                 QCoreApplication::translate("main", "spawn the specified number of processes"),
                                 QCoreApplication::translate("main", "processes"));
    parser.addOption(processes);
#endif

    QCommandLineOption master({ QStringLiteral("master"), QStringLiteral("M") },
                              QCoreApplication::translate("main", "Enable master process"));
    parser.addOption(master);

    QCommandLineOption bufferSize({ QStringLiteral("buffer-size"), QStringLiteral("b") },
                                  QCoreApplication::translate("main", "set internal buffer size"),
                                  QCoreApplication::translate("main", "bytes"));
    parser.addOption(bufferSize);

    QCommandLineOption postBuffering(QStringLiteral("post-buffering"),
                                     QCoreApplication::translate("main", "set size after which will buffer to disk instead of memory"),
                                     QCoreApplication::translate("main", "bytes"));
    parser.addOption(postBuffering);

    QCommandLineOption postBufferingBufsize(QStringLiteral("post-buffering-bufsize"),
                                            QCoreApplication::translate("main", "set buffer size for read() in post buffering mode"),
                                            QCoreApplication::translate("main", "bytes"));
    parser.addOption(postBufferingBufsize);

    QCommandLineOption httpSocketOpt({ QStringLiteral("http-socket"), QStringLiteral("h1") },
                                     QCoreApplication::translate("main", "bind to the specified TCP socket using HTTP protocol"),
                                     QCoreApplication::translate("main", "address"));
    parser.addOption(httpSocketOpt);

    QCommandLineOption http2SocketOpt({ QStringLiteral("http2-socket"), QStringLiteral("h2") },
                                     QCoreApplication::translate("main", "bind to the specified TCP socket using HTTP/2 protocol"),
                                     QCoreApplication::translate("main", "address"));
    parser.addOption(http2SocketOpt);

    QCommandLineOption http2HeaderTableSizeOpt(QStringLiteral("http2-header-table-size"),
                                               QCoreApplication::translate("main", "Defined the HTTP/2 header table size"),
                                               QCoreApplication::translate("main", "size"));
    parser.addOption(http2HeaderTableSizeOpt);

    QCommandLineOption upgradeH2cOpt(QStringLiteral("upgrade-h2c"),
                                               QCoreApplication::translate("main", "Upgrades HTTP/1 to H2c (HTTP/2 Clear Text)"));
    parser.addOption(upgradeH2cOpt);

    QCommandLineOption httpsH2Opt(QStringLiteral("https-h2"),
                                  QCoreApplication::translate("main", "Negotiate HTTP/2 on HTTPS socket"));
    parser.addOption(httpsH2Opt);

    QCommandLineOption httpsSocketOpt({ QStringLiteral("https-socket"), QStringLiteral("hs1") },
                                      QCoreApplication::translate("main", "bind to the specified TCP socket using HTTPS protocol"),
                                      QCoreApplication::translate("main", "address"));
    parser.addOption(httpsSocketOpt);

    QCommandLineOption fastcgiSocketOpt(QStringLiteral("fastcgi-socket"),
                                        QCoreApplication::translate("main", "bind to the specified UNIX/TCP socket using FastCGI protocol"),
                                        QCoreApplication::translate("main", "address"));
    parser.addOption(fastcgiSocketOpt);

    QCommandLineOption socketAccess(QStringLiteral("socket-access"),
                                    QCoreApplication::translate("main", "set the LOCAL socket access, such as 'ugo' standing for User, Group, Other access"),
                                    QCoreApplication::translate("main", "options"));
    parser.addOption(socketAccess);

    QCommandLineOption socketTimeout({ QStringLiteral("socket-timeout"), QStringLiteral("z") },
                                     QCoreApplication::translate("main", "set internal sockets timeout"),
                                     QCoreApplication::translate("main", "seconds"));
    parser.addOption(socketTimeout);

    QCommandLineOption staticMapOpt(QStringLiteral("static-map"),
                                    QCoreApplication::translate("main", "map mountpoint to static directory (or file)"),
                                    QCoreApplication::translate("main", "mountpoint=path"));
    parser.addOption(staticMapOpt);

    QCommandLineOption staticMap2Opt(QStringLiteral("static-map2"),
                                     QCoreApplication::translate("main", "like static-map but completely appending the requested resource to the docroot"),
                                     QCoreApplication::translate("main", "mountpoint=path"));
    parser.addOption(staticMap2Opt);

    QCommandLineOption autoReload({ QStringLiteral("auto-restart"), QStringLiteral("r") },
                                  QCoreApplication::translate("main", "auto restarts when the application file changes"));
    parser.addOption(autoReload);

    QCommandLineOption touchReloadOpt(QStringLiteral("touch-reload"),
                                      QCoreApplication::translate("main", "reload application if the specified file is modified/touched"),
                                      QCoreApplication::translate("main", "file"));
    parser.addOption(touchReloadOpt);

    QCommandLineOption tcpNoDelay(QStringLiteral("tcp-nodelay"),
                                  QCoreApplication::translate("main", "enable TCP NODELAY on each request"));
    parser.addOption(tcpNoDelay);

    QCommandLineOption soKeepAlive(QStringLiteral("so-keepalive"),
                                   QCoreApplication::translate("main", "enable TCP KEEPALIVEs"));
    parser.addOption(soKeepAlive);

    QCommandLineOption socketSndbuf(QStringLiteral("socket-sndbuf"),
                                    QCoreApplication::translate("main", "set SO_SNDBUF"),
                                    QCoreApplication::translate("main", "bytes"));
    parser.addOption(socketSndbuf);

    QCommandLineOption socketRcvbuf(QStringLiteral("socket-rcvbuf"),
                                    QCoreApplication::translate("main", "set SO_RCVBUF"),
                                    QCoreApplication::translate("main", "bytes"));
    parser.addOption(socketRcvbuf);

    QCommandLineOption wsMaxSize(QStringLiteral("websocket-max-size"),
                                 QCoreApplication::translate("main", "sets the socket receive buffer size in bytes at the OS level. This maps to the SO_RCVBUF socket option"),
                                 QCoreApplication::translate("main", "Kbytes"));
    parser.addOption(wsMaxSize);

    QCommandLineOption pidfileOpt(QStringLiteral("pidfile"),
                                  QCoreApplication::translate("main", "create pidfile (before privileges drop)"),
                                  QCoreApplication::translate("main", "file"));
    parser.addOption(pidfileOpt);

    QCommandLineOption pidfile2Opt(QStringLiteral("pidfile2"),
                                   QCoreApplication::translate("main", "create pidfile (after privileges drop)"),
                                   QCoreApplication::translate("main", "file"));
    parser.addOption(pidfile2Opt);

#ifdef Q_OS_UNIX
    QCommandLineOption stopOption(QStringLiteral("stop"),
                                  QCoreApplication::translate("main", "stop an instance"),
                                  QCoreApplication::translate("main", "pidfile"));
    parser.addOption(stopOption);

    QCommandLineOption uidOption(QStringLiteral("uid"),
                                 QCoreApplication::translate("main", "setuid to the specified user/uid"),
                                 QCoreApplication::translate("main", "user/uid"));
    parser.addOption(uidOption);

    QCommandLineOption gidOption(QStringLiteral("gid"),
                                 QCoreApplication::translate("main", "setgid to the specified group/gid"),
                                 QCoreApplication::translate("main", "group/gid"));
    parser.addOption(gidOption);

    QCommandLineOption noInitgroupsOption(QStringLiteral("no-initgroups"),
                                          QCoreApplication::translate("main", "disable additional groups set via initgroups()"));
    parser.addOption(noInitgroupsOption);

    QCommandLineOption chownSocketOption(QStringLiteral("chown-socket"),
                                         QCoreApplication::translate("main", "chown unix sockets"),
                                         QCoreApplication::translate("main", "uid:gid"));
    parser.addOption(chownSocketOption);

    QCommandLineOption umaskOption(QStringLiteral("umask"),
                                   QCoreApplication::translate("main", "set file mode creation mask"),
                                   QCoreApplication::translate("main", "mode"));
    parser.addOption(umaskOption);

    QCommandLineOption cpuAffinityOption(QStringLiteral("cpu-affinity"),
                                         QCoreApplication::translate("main", "set CPU affinity with the number of CPUs available for each worker core"),
                                         QCoreApplication::translate("main", "core count"));
    parser.addOption(cpuAffinityOption);
#endif // Q_OS_UNIX

#ifdef Q_OS_LINUX
    QCommandLineOption reusePortOption(QStringLiteral("reuse-port"),
                                       QCoreApplication::translate("main", "enable SO_REUSEPORT flag on socket (Linux 3.9+)"));
    parser.addOption(reusePortOption);
#endif

    QCommandLineOption threadBalancerOpt(QStringLiteral("experimental-thread-balancer"),
                                         QCoreApplication::translate("main", "balances new connections to threads using round-robin"));
    parser.addOption(threadBalancerOpt);

    // Process the actual command line arguments given by the user
    parser.process(arguments);

    const auto inis = parser.values(iniOpt);
    for (const QString &ini : inis) {
        d->loadConfig(ini, false);
    }
    setIni(inis);

    const auto jsons = parser.values(jsonOpt);
    for (const QString &json : jsons) {
        d->loadConfig(json, true);
    }
    setJson(jsons);

    d->applyConfig(d->opt);

    if (parser.isSet(chdir)) {
        setChdir(parser.value(chdir));
    }

    if (parser.isSet(chdir2)) {
        setChdir2(parser.value(chdir2));
    }

    if (parser.isSet(threads)) {
        setThreads(parser.value(threads));
    }

    if (parser.isSet(socketAccess)) {
        setSocketAccess(parser.value(socketAccess));
    }

    if (parser.isSet(socketTimeout)) {
        bool ok;
        auto size = parser.value(socketTimeout).toInt(&ok);
        setSocketTimeout(size);
        if (!ok || size < 0) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(pidfileOpt)) {
        setPidfile(parser.value(pidfileOpt));
    }

    if (parser.isSet(pidfile2Opt)) {
        setPidfile2(parser.value(pidfile2Opt));
    }

#ifdef Q_OS_UNIX
    if (parser.isSet(stopOption)) {
        UnixFork::stopWSGI(parser.value(stopOption));
    }

    if (parser.isSet(processes)) {
        setProcesses(parser.value(processes));
    }

    if (parser.isSet(uidOption)) {
        setUid(parser.value(uidOption));
    }

    if (parser.isSet(gidOption)) {
        setGid(parser.value(gidOption));
    }

    if (parser.isSet(noInitgroupsOption)) {
        setNoInitgroups(true);
    }

    if (parser.isSet(chownSocketOption)) {
        setChownSocket(parser.value(chownSocketOption));
    }

    if (parser.isSet(umaskOption)) {
        setUmask(parser.value(umaskOption));
    }

    if (parser.isSet(cpuAffinityOption)) {
        bool ok;
        auto value = parser.value(cpuAffinityOption).toInt(&ok);
        setCpuAffinity(value);
        if (!ok || value < 0) {
            parser.showHelp(1);
        }
    }
#endif // Q_OS_UNIX

#ifdef Q_OS_LINUX
    if (parser.isSet(reusePortOption)) {
        setReusePort(true);
    }
#endif

    if (parser.isSet(lazyOption)) {
        setLazy(true);
    }

    if (parser.isSet(bufferSize)) {
        bool ok;
        auto size = parser.value(bufferSize).toInt(&ok);
        setBufferSize(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(postBuffering)) {
        bool ok;
        auto size = parser.value(postBuffering).toLongLong(&ok);
        setPostBuffering(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(postBufferingBufsize)) {
        bool ok;
        auto size = parser.value(postBufferingBufsize).toLongLong(&ok);
        setPostBufferingBufsize(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(application)) {
        setApplication(parser.value(application));
    }

    if (parser.isSet(master)) {
        setMaster(true);
    }

    if (parser.isSet(autoReload)) {
        setAutoReload(true);
    }

    if (parser.isSet(tcpNoDelay)) {
        setTcpNodelay(true);
    }

    if (parser.isSet(soKeepAlive)) {
        setSoKeepalive(true);
    }

    if (parser.isSet(upgradeH2cOpt)) {
        setUpgradeH2c(true);
    }

    if (parser.isSet(httpsH2Opt)) {
        setHttpsH2(true);
    }

    if (parser.isSet(socketSndbuf)) {
        bool ok;
        auto size = parser.value(socketSndbuf).toInt(&ok);
        setSocketSndbuf(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(socketRcvbuf)) {
        bool ok;
        auto size = parser.value(socketRcvbuf).toInt(&ok);
        setSocketRcvbuf(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(wsMaxSize)) {
        bool ok;
        auto size = parser.value(wsMaxSize).toInt(&ok);
        setWebsocketMaxSize(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(http2HeaderTableSizeOpt)) {
        bool ok;
        auto size = parser.value(http2HeaderTableSizeOpt).toUInt(&ok);
        setHttp2HeaderTableSize(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    setHttpSocket(httpSocket() + parser.values(httpSocketOpt));

    setHttp2Socket(http2Socket() + parser.values(http2SocketOpt));

    setHttpsSocket(httpsSocket() + parser.values(httpsSocketOpt));

    setFastcgiSocket(fastcgiSocket() + parser.values(fastcgiSocketOpt));

    setStaticMap(staticMap() + parser.values(staticMapOpt));

    setStaticMap2(staticMap2() + parser.values(staticMap2Opt));

    setTouchReload(touchReload() + parser.values(touchReloadOpt));

    d->threadBalancer = parser.isSet(threadBalancerOpt);
}

int WSGI::exec(Cutelyst::Application *app)<commit_msg>add command line switch for frontend proxy<commit_after>/*
 * Copyright (C) 2016-2018 <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
#include "wsgi_p.h"

#include "protocol.h"
#include "protocolhttp.h"
#include "protocolhttp2.h"
#include "protocolfastcgi.h"
#include "cwsgiengine.h"
#include "socket.h"
#include "tcpserverbalancer.h"
#include "localserver.h"

#ifdef Q_OS_UNIX
#include "unixfork.h"
#else
#include "windowsfork.h"
#endif

#ifdef Q_OS_LINUX
#include "../EventLoopEPoll/eventdispatcher_epoll.h"
#include "systemdnotify.h"
#endif

#include <QCoreApplication>
#include <QCommandLineParser>
#include <QUrl>
#include <QPluginLoader>
#include <QThread>
#include <QLoggingCategory>
#include <QSettings>
#include <QSocketNotifier>
#include <QMetaProperty>
#include <QTimer>
#include <QDir>
#include <QMutex>

#include <iostream>

Q_LOGGING_CATEGORY(CUTELYST_WSGI, "wsgi", QtWarningMsg)

using namespace CWSGI;
using namespace Cutelyst;

WSGI::WSGI(QObject *parent) : QObject(parent),
    d_ptr(new WSGIPrivate(this))
{
    if (qEnvironmentVariableIsEmpty("QT_MESSAGE_PATTERN")) {
        qSetMessagePattern(QLatin1String("%{pid}:%{threadid} %{category}[%{type}] %{message}"));
    }

#ifdef Q_OS_LINUX
    if (!qEnvironmentVariableIsSet("CUTELYST_QT_EVENT_LOOP")) {
        std::cout << "Installing EPoll event loop" << std::endl;
        QCoreApplication::setEventDispatcher(new EventDispatcherEPoll);
    }
#endif
}

WSGI::~WSGI()
{
    Q_D(WSGI);

    delete d->protoHTTP;
    delete d->protoHTTP2;
    delete d->protoFCGI;

    std::cout << "Cutelyst-WSGI terminated" << std::endl;
}

void WSGI::parseCommandLine(const QStringList &arguments)
{
    Q_D(WSGI);

    QCommandLineParser parser;
    parser.setApplicationDescription(QCoreApplication::translate("main", "Fast, developer-friendly WSGI server"));
    parser.addHelpOption();
    parser.addVersionOption();

    QCommandLineOption iniOpt(QStringLiteral("ini"),
                              QCoreApplication::translate("main", "load config from ini file"),
                              QCoreApplication::translate("main", "file"));
    parser.addOption(iniOpt);

    QCommandLineOption jsonOpt({ QStringLiteral("j"), QStringLiteral("json") },
                           QCoreApplication::translate("main", "load config from JSON file"),
                           QCoreApplication::translate("main", "file"));
    parser.addOption(jsonOpt);

    QCommandLineOption chdir(QStringLiteral("chdir"),
                             QCoreApplication::translate("main", "chdir to specified directory before apps loading"),
                             QCoreApplication::translate("main", "directory"));
    parser.addOption(chdir);

    QCommandLineOption chdir2(QStringLiteral("chdir2"),
                              QCoreApplication::translate("main", "chdir to specified directory afterapps loading"),
                              QCoreApplication::translate("main", "directory"));
    parser.addOption(chdir2);

    QCommandLineOption lazyOption(QStringLiteral("lazy"),
                                  QCoreApplication::translate("main", "set lazy mode (load app in workers instead of master)"));
    parser.addOption(lazyOption);

    QCommandLineOption application({ QStringLiteral("application"), QStringLiteral("a") },
                                   QCoreApplication::translate("main", "Application to load"),
                                   QCoreApplication::translate("main", "file"));
    parser.addOption(application);

    QCommandLineOption threads({ QStringLiteral("threads"), QStringLiteral("t") },
                               QCoreApplication::translate("main", "Number of thread to use"),
                               QCoreApplication::translate("main", "threads"));
    parser.addOption(threads);

#ifdef Q_OS_UNIX
    QCommandLineOption processes({ QStringLiteral("processes"), QStringLiteral("p") },
                                 QCoreApplication::translate("main", "spawn the specified number of processes"),
                                 QCoreApplication::translate("main", "processes"));
    parser.addOption(processes);
#endif

    QCommandLineOption master({ QStringLiteral("master"), QStringLiteral("M") },
                              QCoreApplication::translate("main", "Enable master process"));
    parser.addOption(master);

    QCommandLineOption bufferSize({ QStringLiteral("buffer-size"), QStringLiteral("b") },
                                  QCoreApplication::translate("main", "set internal buffer size"),
                                  QCoreApplication::translate("main", "bytes"));
    parser.addOption(bufferSize);

    QCommandLineOption postBuffering(QStringLiteral("post-buffering"),
                                     QCoreApplication::translate("main", "set size after which will buffer to disk instead of memory"),
                                     QCoreApplication::translate("main", "bytes"));
    parser.addOption(postBuffering);

    QCommandLineOption postBufferingBufsize(QStringLiteral("post-buffering-bufsize"),
                                            QCoreApplication::translate("main", "set buffer size for read() in post buffering mode"),
                                            QCoreApplication::translate("main", "bytes"));
    parser.addOption(postBufferingBufsize);

    QCommandLineOption httpSocketOpt({ QStringLiteral("http-socket"), QStringLiteral("h1") },
                                     QCoreApplication::translate("main", "bind to the specified TCP socket using HTTP protocol"),
                                     QCoreApplication::translate("main", "address"));
    parser.addOption(httpSocketOpt);

    QCommandLineOption http2SocketOpt({ QStringLiteral("http2-socket"), QStringLiteral("h2") },
                                     QCoreApplication::translate("main", "bind to the specified TCP socket using HTTP/2 protocol"),
                                     QCoreApplication::translate("main", "address"));
    parser.addOption(http2SocketOpt);

    QCommandLineOption http2HeaderTableSizeOpt(QStringLiteral("http2-header-table-size"),
                                               QCoreApplication::translate("main", "Defined the HTTP/2 header table size"),
                                               QCoreApplication::translate("main", "size"));
    parser.addOption(http2HeaderTableSizeOpt);

    QCommandLineOption upgradeH2cOpt(QStringLiteral("upgrade-h2c"),
                                               QCoreApplication::translate("main", "Upgrades HTTP/1 to H2c (HTTP/2 Clear Text)"));
    parser.addOption(upgradeH2cOpt);

    QCommandLineOption httpsH2Opt(QStringLiteral("https-h2"),
                                  QCoreApplication::translate("main", "Negotiate HTTP/2 on HTTPS socket"));
    parser.addOption(httpsH2Opt);

    QCommandLineOption httpsSocketOpt({ QStringLiteral("https-socket"), QStringLiteral("hs1") },
                                      QCoreApplication::translate("main", "bind to the specified TCP socket using HTTPS protocol"),
                                      QCoreApplication::translate("main", "address"));
    parser.addOption(httpsSocketOpt);

    QCommandLineOption fastcgiSocketOpt(QStringLiteral("fastcgi-socket"),
                                        QCoreApplication::translate("main", "bind to the specified UNIX/TCP socket using FastCGI protocol"),
                                        QCoreApplication::translate("main", "address"));
    parser.addOption(fastcgiSocketOpt);

    QCommandLineOption socketAccess(QStringLiteral("socket-access"),
                                    QCoreApplication::translate("main", "set the LOCAL socket access, such as 'ugo' standing for User, Group, Other access"),
                                    QCoreApplication::translate("main", "options"));
    parser.addOption(socketAccess);

    QCommandLineOption socketTimeout({ QStringLiteral("socket-timeout"), QStringLiteral("z") },
                                     QCoreApplication::translate("main", "set internal sockets timeout"),
                                     QCoreApplication::translate("main", "seconds"));
    parser.addOption(socketTimeout);

    QCommandLineOption staticMapOpt(QStringLiteral("static-map"),
                                    QCoreApplication::translate("main", "map mountpoint to static directory (or file)"),
                                    QCoreApplication::translate("main", "mountpoint=path"));
    parser.addOption(staticMapOpt);

    QCommandLineOption staticMap2Opt(QStringLiteral("static-map2"),
                                     QCoreApplication::translate("main", "like static-map but completely appending the requested resource to the docroot"),
                                     QCoreApplication::translate("main", "mountpoint=path"));
    parser.addOption(staticMap2Opt);

    QCommandLineOption autoReload({ QStringLiteral("auto-restart"), QStringLiteral("r") },
                                  QCoreApplication::translate("main", "auto restarts when the application file changes"));
    parser.addOption(autoReload);

    QCommandLineOption touchReloadOpt(QStringLiteral("touch-reload"),
                                      QCoreApplication::translate("main", "reload application if the specified file is modified/touched"),
                                      QCoreApplication::translate("main", "file"));
    parser.addOption(touchReloadOpt);

    QCommandLineOption tcpNoDelay(QStringLiteral("tcp-nodelay"),
                                  QCoreApplication::translate("main", "enable TCP NODELAY on each request"));
    parser.addOption(tcpNoDelay);

    QCommandLineOption soKeepAlive(QStringLiteral("so-keepalive"),
                                   QCoreApplication::translate("main", "enable TCP KEEPALIVEs"));
    parser.addOption(soKeepAlive);

    QCommandLineOption socketSndbuf(QStringLiteral("socket-sndbuf"),
                                    QCoreApplication::translate("main", "set SO_SNDBUF"),
                                    QCoreApplication::translate("main", "bytes"));
    parser.addOption(socketSndbuf);

    QCommandLineOption socketRcvbuf(QStringLiteral("socket-rcvbuf"),
                                    QCoreApplication::translate("main", "set SO_RCVBUF"),
                                    QCoreApplication::translate("main", "bytes"));
    parser.addOption(socketRcvbuf);

    QCommandLineOption wsMaxSize(QStringLiteral("websocket-max-size"),
                                 QCoreApplication::translate("main", "sets the socket receive buffer size in bytes at the OS level. This maps to the SO_RCVBUF socket option"),
                                 QCoreApplication::translate("main", "Kbytes"));
    parser.addOption(wsMaxSize);

    QCommandLineOption pidfileOpt(QStringLiteral("pidfile"),
                                  QCoreApplication::translate("main", "create pidfile (before privileges drop)"),
                                  QCoreApplication::translate("main", "file"));
    parser.addOption(pidfileOpt);

    QCommandLineOption pidfile2Opt(QStringLiteral("pidfile2"),
                                   QCoreApplication::translate("main", "create pidfile (after privileges drop)"),
                                   QCoreApplication::translate("main", "file"));
    parser.addOption(pidfile2Opt);

#ifdef Q_OS_UNIX
    QCommandLineOption stopOption(QStringLiteral("stop"),
                                  QCoreApplication::translate("main", "stop an instance"),
                                  QCoreApplication::translate("main", "pidfile"));
    parser.addOption(stopOption);

    QCommandLineOption uidOption(QStringLiteral("uid"),
                                 QCoreApplication::translate("main", "setuid to the specified user/uid"),
                                 QCoreApplication::translate("main", "user/uid"));
    parser.addOption(uidOption);

    QCommandLineOption gidOption(QStringLiteral("gid"),
                                 QCoreApplication::translate("main", "setgid to the specified group/gid"),
                                 QCoreApplication::translate("main", "group/gid"));
    parser.addOption(gidOption);

    QCommandLineOption noInitgroupsOption(QStringLiteral("no-initgroups"),
                                          QCoreApplication::translate("main", "disable additional groups set via initgroups()"));
    parser.addOption(noInitgroupsOption);

    QCommandLineOption chownSocketOption(QStringLiteral("chown-socket"),
                                         QCoreApplication::translate("main", "chown unix sockets"),
                                         QCoreApplication::translate("main", "uid:gid"));
    parser.addOption(chownSocketOption);

    QCommandLineOption umaskOption(QStringLiteral("umask"),
                                   QCoreApplication::translate("main", "set file mode creation mask"),
                                   QCoreApplication::translate("main", "mode"));
    parser.addOption(umaskOption);

    QCommandLineOption cpuAffinityOption(QStringLiteral("cpu-affinity"),
                                         QCoreApplication::translate("main", "set CPU affinity with the number of CPUs available for each worker core"),
                                         QCoreApplication::translate("main", "core count"));
    parser.addOption(cpuAffinityOption);
#endif // Q_OS_UNIX

#ifdef Q_OS_LINUX
    QCommandLineOption reusePortOption(QStringLiteral("reuse-port"),
                                       QCoreApplication::translate("main", "enable SO_REUSEPORT flag on socket (Linux 3.9+)"));
    parser.addOption(reusePortOption);
#endif

    QCommandLineOption threadBalancerOpt(QStringLiteral("experimental-thread-balancer"),
                                         QCoreApplication::translate("main", "balances new connections to threads using round-robin"));
    parser.addOption(threadBalancerOpt);

    QCommandLineOption frontendProxy(QStringLiteral("using-frontend-proxy"),
                                     QCoreApplication::translate("main", "Enable frontend (reverse-)proxy support"));
    parser.addOption(frontendProxy);


    // Process the actual command line arguments given by the user
    parser.process(arguments);

    const auto inis = parser.values(iniOpt);
    for (const QString &ini : inis) {
        d->loadConfig(ini, false);
    }
    setIni(inis);

    const auto jsons = parser.values(jsonOpt);
    for (const QString &json : jsons) {
        d->loadConfig(json, true);
    }
    setJson(jsons);

    d->applyConfig(d->opt);

    if (parser.isSet(chdir)) {
        setChdir(parser.value(chdir));
    }

    if (parser.isSet(chdir2)) {
        setChdir2(parser.value(chdir2));
    }

    if (parser.isSet(threads)) {
        setThreads(parser.value(threads));
    }

    if (parser.isSet(socketAccess)) {
        setSocketAccess(parser.value(socketAccess));
    }

    if (parser.isSet(socketTimeout)) {
        bool ok;
        auto size = parser.value(socketTimeout).toInt(&ok);
        setSocketTimeout(size);
        if (!ok || size < 0) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(pidfileOpt)) {
        setPidfile(parser.value(pidfileOpt));
    }

    if (parser.isSet(pidfile2Opt)) {
        setPidfile2(parser.value(pidfile2Opt));
    }

#ifdef Q_OS_UNIX
    if (parser.isSet(stopOption)) {
        UnixFork::stopWSGI(parser.value(stopOption));
    }

    if (parser.isSet(processes)) {
        setProcesses(parser.value(processes));
    }

    if (parser.isSet(uidOption)) {
        setUid(parser.value(uidOption));
    }

    if (parser.isSet(gidOption)) {
        setGid(parser.value(gidOption));
    }

    if (parser.isSet(noInitgroupsOption)) {
        setNoInitgroups(true);
    }

    if (parser.isSet(chownSocketOption)) {
        setChownSocket(parser.value(chownSocketOption));
    }

    if (parser.isSet(umaskOption)) {
        setUmask(parser.value(umaskOption));
    }

    if (parser.isSet(cpuAffinityOption)) {
        bool ok;
        auto value = parser.value(cpuAffinityOption).toInt(&ok);
        setCpuAffinity(value);
        if (!ok || value < 0) {
            parser.showHelp(1);
        }
    }
#endif // Q_OS_UNIX

#ifdef Q_OS_LINUX
    if (parser.isSet(reusePortOption)) {
        setReusePort(true);
    }
#endif

    if (parser.isSet(lazyOption)) {
        setLazy(true);
    }

    if (parser.isSet(bufferSize)) {
        bool ok;
        auto size = parser.value(bufferSize).toInt(&ok);
        setBufferSize(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(postBuffering)) {
        bool ok;
        auto size = parser.value(postBuffering).toLongLong(&ok);
        setPostBuffering(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(postBufferingBufsize)) {
        bool ok;
        auto size = parser.value(postBufferingBufsize).toLongLong(&ok);
        setPostBufferingBufsize(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(application)) {
        setApplication(parser.value(application));
    }

    if (parser.isSet(master)) {
        setMaster(true);
    }

    if (parser.isSet(autoReload)) {
        setAutoReload(true);
    }

    if (parser.isSet(tcpNoDelay)) {
        setTcpNodelay(true);
    }

    if (parser.isSet(soKeepAlive)) {
        setSoKeepalive(true);
    }

    if (parser.isSet(upgradeH2cOpt)) {
        setUpgradeH2c(true);
    }

    if (parser.isSet(httpsH2Opt)) {
        setHttpsH2(true);
    }

    if (parser.isSet(socketSndbuf)) {
        bool ok;
        auto size = parser.value(socketSndbuf).toInt(&ok);
        setSocketSndbuf(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(socketRcvbuf)) {
        bool ok;
        auto size = parser.value(socketRcvbuf).toInt(&ok);
        setSocketRcvbuf(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(wsMaxSize)) {
        bool ok;
        auto size = parser.value(wsMaxSize).toInt(&ok);
        setWebsocketMaxSize(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(http2HeaderTableSizeOpt)) {
        bool ok;
        auto size = parser.value(http2HeaderTableSizeOpt).toUInt(&ok);
        setHttp2HeaderTableSize(size);
        if (!ok || size < 1) {
            parser.showHelp(1);
        }
    }

    if (parser.isSet(frontendProxy)) {
        setUsingFrontendProxy(true);
    }

    setHttpSocket(httpSocket() + parser.values(httpSocketOpt));

    setHttp2Socket(http2Socket() + parser.values(http2SocketOpt));

    setHttpsSocket(httpsSocket() + parser.values(httpsSocketOpt));

    setFastcgiSocket(fastcgiSocket() + parser.values(fastcgiSocketOpt));

    setStaticMap(staticMap() + parser.values(staticMapOpt));

    setStaticMap2(staticMap2() + parser.values(staticMap2Opt));

    setTouchReload(touchReload() + parser.values(touchReloadOpt));

    d->threadBalancer = parser.isSet(threadBalancerOpt);
}

int WSGI::exec(Cutelyst::Application *app)
{'fraction_non_alphanumeric': 0.11774217999898934, 'fraction_numerical': 0.004699580575066957, 'mean_word_length': 2.574370089406665, 'pattern_counts': {'":': 0, '<': 69, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 145, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4841805', 'n_tokens_mistral': 10702, 'n_tokens_neox': 10304, 'n_words': 2052}