cutelyst  4.5.1
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"
The Cutelyst application.
Definition: application.h:66
The Cutelyst Engine.
Definition: engine.h:20
bool initApplication()
Definition: engine.cpp:73
int workerCore() const
Definition: engine.cpp:67
bool postForkApplication()
Definition: engine.cpp:90
virtual int workerId() const override
virtual bool init() override
Implements a web server.
Definition: server.h:60
The Cutelyst namespace holds all public Cutelyst API.
QDateTime currentDateTimeUtc()
bool isEmpty() const const
QLocale c()
QString toString(QDate date, QLocale::FormatType format) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QByteArray toLatin1() const const
QFuture< ArgsType< Signal >> connect(Sender *sender, Signal signal)
void timeout()