cutelyst  4.3.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
serverengine.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2016-2022 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "serverengine.h"
6 
7 #include "config.h"
8 #include "localserver.h"
9 #include "protocol.h"
10 #include "protocolfastcgi.h"
11 #include "protocolhttp.h"
12 #include "protocolhttp2.h"
13 #include "protocolwebsocket.h"
14 #include "server.h"
15 #include "socket.h"
16 #include "staticmap.h"
17 #include "tcpserver.h"
18 #include "tcpserverbalancer.h"
19 #include "tcpsslserver.h"
20 
21 #ifdef Q_OS_UNIX
22 # include "unixfork.h"
23 #endif
24 
25 #include <Cutelyst/Application>
26 #include <Cutelyst/Context>
27 #include <Cutelyst/Request>
28 #include <Cutelyst/Response>
29 #include <iostream>
30 #include <typeinfo>
31 
32 #include <QCoreApplication>
33 #include <QLoggingCategory>
34 
35 Q_LOGGING_CATEGORY(C_SERVER_ENGINE, "cutelyst.server.engine", QtWarningMsg)
36 
37 using namespace Cutelyst;
38 
39 QByteArray dateHeader();
40 
41 ServerEngine::ServerEngine(Application *localApp,
42  int workerCore,
43  const QVariantMap &opts,
44  Server *wsgi)
45  : Engine(localApp, workerCore, opts)
46  , m_wsgi(wsgi)
47 {
48  m_lastDate = dateHeader();
49  m_lastDateTimer.start();
50 
51  if (m_wsgi->socketTimeout()) {
52  m_socketTimeout = new QTimer(this);
53  m_socketTimeout->setObjectName(QStringLiteral("Cutelyst::socketTimeout"));
54  m_socketTimeout->setInterval(std::chrono::seconds{m_wsgi->socketTimeout()});
55  }
56 
57  connect(this, &ServerEngine::shutdown, app(), [this] { Q_EMIT app() -> shuttingDown(app()); });
58 
59  const QStringList staticMap = m_wsgi->staticMap();
60  const QStringList staticMap2 = m_wsgi->staticMap2();
61  if (!staticMap.isEmpty() || !staticMap2.isEmpty()) {
62  auto staticMapPlugin = new StaticMap(app());
63 
64  for (const QString &part : staticMap) {
65  staticMapPlugin->addStaticMap(
66  part.section(QLatin1Char('='), 0, 0), part.section(QLatin1Char('='), 1, 1), false);
67  }
68 
69  for (const QString &part : staticMap2) {
70  staticMapPlugin->addStaticMap(
71  part.section(QLatin1Char('='), 0, 0), part.section(QLatin1Char('='), 1, 1), true);
72  }
73  }
74 }
75 
76 ServerEngine::~ServerEngine()
77 {
78  delete m_protoFcgi;
79  delete m_protoHttp;
80  delete m_protoHttp2;
81 }
82 
84 {
85  return m_workerId;
86 }
87 
88 void ServerEngine::setServers(const std::vector<QObject *> &servers)
89 {
90  for (QObject *server : servers) {
91  auto balancer = qobject_cast<TcpServerBalancer *>(server);
92  if (balancer) {
93  TcpServer *server = balancer->createServer(this);
94  if (server) {
95  ++m_runningServers;
96  if (m_socketTimeout) {
97  connect(
98  m_socketTimeout, &QTimer::timeout, server, &TcpServer::timeoutConnections);
99  }
100 
101  if (server->protocol()->type() == Protocol::Type::Http11) {
102  server->setProtocol(getProtoHttp());
103  } else if (server->protocol()->type() == Protocol::Type::Http2) {
104  server->setProtocol(getProtoHttp2());
105  } else if (server->protocol()->type() == Protocol::Type::FastCGI1) {
106  server->setProtocol(getProtoFastCgi());
107  }
108 
109 #ifndef QT_NO_SSL
110  if (m_wsgi->httpsH2()) {
111  auto sslServer = qobject_cast<TcpSslServer *>(server);
112  if (sslServer) {
113  sslServer->setHttp2Protocol(getProtoHttp2());
114  }
115  }
116 #endif // QT_NO_SSL
117  }
118  }
119 
120  auto localServer = qobject_cast<LocalServer *>(server);
121  if (localServer) {
122  LocalServer *server = localServer->createServer(this);
123  if (server) {
124  ++m_runningServers;
125  if (m_socketTimeout) {
126  connect(m_socketTimeout,
128  server,
129  &LocalServer::timeoutConnections);
130  }
131 
132  if (server->protocol()->type() == Protocol::Type::Http11) {
133  server->setProtocol(getProtoHttp());
134  } else if (server->protocol()->type() == Protocol::Type::Http2) {
135  server->setProtocol(getProtoHttp2());
136  } else if (server->protocol()->type() == Protocol::Type::FastCGI1) {
137  server->setProtocol(getProtoFastCgi());
138  }
139  }
140  }
141  }
142 }
143 
144 void ServerEngine::postFork(int workerId)
145 {
146  m_workerId = workerId;
147 
148 #ifdef Q_OS_UNIX
149  UnixFork::setSched(m_wsgi, workerId, workerCore());
150 #endif
151 
152  if (Q_LIKELY(postForkApplication())) {
153  Q_EMIT started();
154  } else {
155  std::cerr << "Application failed to post fork, cheaping worker: " << workerId
156  << ", core: " << workerCore() << std::endl;
157  Q_EMIT shutdown();
158  }
159 }
160 
161 QByteArray ServerEngine::dateHeader()
162 {
163  QString ret;
164  ret = QLatin1String("\r\nDate: ") +
166  QStringLiteral("ddd, dd MMM yyyy hh:mm:ss 'GMT"));
167  return ret.toLatin1();
168 }
169 
170 Protocol *ServerEngine::getProtoHttp()
171 {
172  if (!m_protoHttp) {
173  if (m_wsgi->upgradeH2c()) {
174  m_protoHttp = new ProtocolHttp(m_wsgi, getProtoHttp2());
175  } else {
176  m_protoHttp = new ProtocolHttp(m_wsgi);
177  }
178  }
179  return m_protoHttp;
180 }
181 
182 ProtocolHttp2 *ServerEngine::getProtoHttp2()
183 {
184  if (!m_protoHttp2) {
185  m_protoHttp2 = new ProtocolHttp2(m_wsgi);
186  }
187  return m_protoHttp2;
188 }
189 
190 Protocol *ServerEngine::getProtoFastCgi()
191 {
192  if (!m_protoFcgi) {
193  m_protoFcgi = new ProtocolFastCGI(m_wsgi);
194  }
195  return m_protoFcgi;
196 }
197 
199 {
200  if (Q_LIKELY(initApplication())) {
201  return true;
202  }
203 
204  return false;
205 }
206 
207 void ServerEngine::handleSocketShutdown(Socket *socket)
208 {
209  if (socket->processing == 0) {
210  socket->connectionClose();
211  } else if (socket->proto->type() == Protocol::Type::Http11Websocket) {
212  auto req = static_cast<ProtoRequestHttp *>(socket->protoData);
213  req->webSocketClose(Response::CloseCode::CloseCodeGoingAway, {});
214  } else {
215  socket->protoData->headerConnection = ProtocolData::HeaderConnection::Close;
216  }
217 }
218 
219 #include "moc_serverengine.cpp"
QString toString(qlonglong i) const const
Implements a web server.
Definition: server.h:59
virtual bool init() override
bool initApplication()
Definition: engine.cpp:73
bool postForkApplication()
Definition: engine.cpp:90
void timeout()
QLocale c()
bool isEmpty() const const
The Cutelyst namespace holds all public Cutelyst API.
QByteArray toLatin1() const const
virtual int workerId() const override
The Cutelyst application.
Definition: application.h:72
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
The Cutelyst Engine.
Definition: engine.h:19
int workerCore() const
Definition: engine.cpp:67
QDateTime currentDateTimeUtc()
Q_EMITQ_EMIT