cutelyst  4.8.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  // NOLINTNEXTLINE
63  auto staticMapPlugin = new StaticMap(app());
64 
65  for (const QString &part : staticMap) {
66  staticMapPlugin->addStaticMap(
67  part.section(QLatin1Char('='), 0, 0), part.section(QLatin1Char('='), 1, 1), false);
68  }
69 
70  for (const QString &part : staticMap2) {
71  staticMapPlugin->addStaticMap(
72  part.section(QLatin1Char('='), 0, 0), part.section(QLatin1Char('='), 1, 1), true);
73  }
74  }
75 }
76 
77 ServerEngine::~ServerEngine()
78 {
79  delete m_protoFcgi;
80  delete m_protoHttp;
81  delete m_protoHttp2;
82 }
83 
85 {
86  return m_workerId;
87 }
88 
89 void ServerEngine::setServers(const std::vector<QObject *> &servers)
90 {
91  for (QObject *server : servers) {
92  auto balancer = qobject_cast<TcpServerBalancer *>(server);
93  if (balancer) {
94  TcpServer *server = balancer->createServer(this);
95  if (server) {
96  ++m_runningServers;
97  if (m_socketTimeout) {
98  connect(
99  m_socketTimeout, &QTimer::timeout, server, &TcpServer::timeoutConnections);
100  }
101 
102  if (server->protocol()->type() == Protocol::Type::Http11) {
103  server->setProtocol(getProtoHttp());
104  } else if (server->protocol()->type() == Protocol::Type::Http2) {
105  server->setProtocol(getProtoHttp2());
106  } else if (server->protocol()->type() == Protocol::Type::FastCGI1) {
107  server->setProtocol(getProtoFastCgi());
108  }
109 
110 #ifndef QT_NO_SSL
111  if (m_wsgi->httpsH2()) {
112  auto sslServer = qobject_cast<TcpSslServer *>(server);
113  if (sslServer) {
114  sslServer->setHttp2Protocol(getProtoHttp2());
115  }
116  }
117 #endif // QT_NO_SSL
118  }
119  }
120 
121  auto localServer = qobject_cast<LocalServer *>(server);
122  if (localServer) {
123  LocalServer *server = localServer->createServer(this);
124  if (server) {
125  ++m_runningServers;
126  if (m_socketTimeout) {
127  connect(m_socketTimeout,
129  server,
130  &LocalServer::timeoutConnections);
131  }
132 
133  if (server->protocol()->type() == Protocol::Type::Http11) {
134  server->setProtocol(getProtoHttp());
135  } else if (server->protocol()->type() == Protocol::Type::Http2) {
136  server->setProtocol(getProtoHttp2());
137  } else if (server->protocol()->type() == Protocol::Type::FastCGI1) {
138  server->setProtocol(getProtoFastCgi());
139  }
140  }
141  }
142  }
143 }
144 
145 void ServerEngine::postFork(int workerId)
146 {
147  m_workerId = workerId;
148 
149 #ifdef Q_OS_UNIX
150  UnixFork::setSched(m_wsgi, workerId, workerCore());
151 #endif
152 
153  if (Q_LIKELY(postForkApplication())) {
154  Q_EMIT started();
155  } else {
156  std::cerr << "Application failed to post fork, cheaping worker: " << workerId
157  << ", core: " << workerCore() << std::endl;
158  Q_EMIT shutdown();
159  }
160 }
161 
162 QByteArray ServerEngine::dateHeader()
163 {
164  QString ret;
165  ret = QLatin1String("\r\nDate: ") +
167  QStringLiteral("ddd, dd MMM yyyy hh:mm:ss 'GMT"));
168  return ret.toLatin1();
169 }
170 
171 Protocol *ServerEngine::getProtoHttp()
172 {
173  if (!m_protoHttp) {
174  if (m_wsgi->upgradeH2c()) {
175  m_protoHttp = new ProtocolHttp(m_wsgi, getProtoHttp2());
176  } else {
177  m_protoHttp = new ProtocolHttp(m_wsgi);
178  }
179  }
180  return m_protoHttp;
181 }
182 
183 ProtocolHttp2 *ServerEngine::getProtoHttp2()
184 {
185  if (!m_protoHttp2) {
186  m_protoHttp2 = new ProtocolHttp2(m_wsgi);
187  }
188  return m_protoHttp2;
189 }
190 
191 Protocol *ServerEngine::getProtoFastCgi()
192 {
193  if (!m_protoFcgi) {
194  m_protoFcgi = new ProtocolFastCGI(m_wsgi);
195  }
196  return m_protoFcgi;
197 }
198 
200 {
201  if (Q_LIKELY(initApplication())) {
202  return true;
203  }
204 
205  return false;
206 }
207 
208 void ServerEngine::handleSocketShutdown(Socket *socket)
209 {
210  if (socket->processing == 0) {
211  socket->connectionClose();
212  } else if (socket->proto->type() == Protocol::Type::Http11Websocket) {
213  auto req = static_cast<ProtoRequestHttp *>(socket->protoData);
214  req->webSocketClose(Response::CloseCode::CloseCodeGoingAway, {});
215  } else {
216  socket->protoData->headerConnection = ProtocolData::HeaderConnection::Close;
217  }
218 }
219 
220 #include "moc_serverengine.cpp"
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
Implements a web server.
Definition: server.h:59
virtual bool init() override
bool initApplication()
Definition: engine.cpp:73
bool postForkApplication()
Definition: engine.cpp:90
QString toString(QDate date, FormatType format) const const
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
T qobject_cast(QObject *object)
The Cutelyst Engine.
Definition: engine.h:19
int workerCore() const
Definition: engine.cpp:67
QDateTime currentDateTimeUtc()
Q_EMITQ_EMIT