cutelyst  5.0.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 using namespace Qt::StringLiterals;
39 
40 QByteArray dateHeader();
41 
42 ServerEngine::ServerEngine(Application *localApp,
43  int workerCore,
44  const QVariantMap &opts,
45  Server *server)
46  : Engine(localApp, workerCore, opts)
47  , m_lastDate{dateHeader()}
48  , m_server(server)
49 {
50  m_lastDateTimer.start();
51 
52  if (m_server->socketTimeout()) {
53  m_socketTimeout = new QTimer(this);
54  m_socketTimeout->setObjectName(u"Cutelyst::socketTimeout"_s);
55  m_socketTimeout->setInterval(std::chrono::seconds{m_server->socketTimeout()});
56  }
57 
58  connect(this, &ServerEngine::shutdown, app(), [this] { Q_EMIT app()->shuttingDown(app()); });
59 
60  const QStringList staticMap = m_server->staticMap();
61  const QStringList staticMap2 = m_server->staticMap2();
62  if (!staticMap.isEmpty() || !staticMap2.isEmpty()) {
63  // NOLINTNEXTLINE
64  auto staticMapPlugin = new StaticMap(app());
65 
66  for (const QString &part : staticMap) {
67  staticMapPlugin->addStaticMap(
68  part.section(u'=', 0, 0), part.section(u'=', 1, 1), false);
69  }
70 
71  for (const QString &part : staticMap2) {
72  staticMapPlugin->addStaticMap(part.section(u'=', 0, 0), part.section(u'=', 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 *cloneServer = balancer->createServer(this);
95  if (cloneServer) {
96  ++m_runningServers;
97  if (m_socketTimeout) {
98  connect(m_socketTimeout,
100  cloneServer,
101  &TcpServer::timeoutConnections);
102  }
103 
104  if (cloneServer->protocol()->type() == Protocol::Type::Http11) {
105  cloneServer->setProtocol(getProtoHttp());
106  } else if (cloneServer->protocol()->type() == Protocol::Type::Http2) {
107  cloneServer->setProtocol(getProtoHttp2());
108  } else if (cloneServer->protocol()->type() == Protocol::Type::FastCGI1) {
109  cloneServer->setProtocol(getProtoFastCgi());
110  }
111 
112 #ifndef QT_NO_SSL
113  if (m_server->httpsH2()) {
114  auto sslServer = qobject_cast<TcpSslServer *>(cloneServer);
115  if (sslServer) {
116  sslServer->setHttp2Protocol(getProtoHttp2());
117  }
118  }
119 #endif // QT_NO_SSL
120  }
121  }
122 
123  const auto localServer = qobject_cast<LocalServer *>(server);
124  if (localServer) {
125  LocalServer *cloneServer = localServer->createServer(this);
126  if (cloneServer) {
127  ++m_runningServers;
128  if (m_socketTimeout) {
129  connect(m_socketTimeout,
131  cloneServer,
132  &LocalServer::timeoutConnections);
133  }
134 
135  if (cloneServer->protocol()->type() == Protocol::Type::Http11) {
136  cloneServer->setProtocol(getProtoHttp());
137  } else if (cloneServer->protocol()->type() == Protocol::Type::Http2) {
138  cloneServer->setProtocol(getProtoHttp2());
139  } else if (cloneServer->protocol()->type() == Protocol::Type::FastCGI1) {
140  cloneServer->setProtocol(getProtoFastCgi());
141  }
142  }
143  }
144  }
145 }
146 
147 void ServerEngine::postFork(int workerId)
148 {
149  m_workerId = workerId;
150 
151 #ifdef Q_OS_UNIX
152  UnixFork::setSched(m_server, workerId, workerCore());
153 #endif
154 
155  if (Q_LIKELY(postForkApplication())) {
156  Q_EMIT started();
157  } else {
158  std::cerr << "Application failed to post fork, cheaping worker: " << workerId
159  << ", core: " << workerCore() << '\n';
160  Q_EMIT shutdown();
161  }
162 }
163 
164 QByteArray ServerEngine::dateHeader()
165 {
166  QString ret;
167  ret = u"\r\nDate: " + QLocale::c().toString(QDateTime::currentDateTimeUtc(),
168  u"ddd, dd MMM yyyy hh:mm:ss 'GMT"_s);
169  return ret.toLatin1();
170 }
171 
172 Protocol *ServerEngine::getProtoHttp()
173 {
174  if (!m_protoHttp) {
175  if (m_server->upgradeH2c()) {
176  m_protoHttp = new ProtocolHttp(m_server, getProtoHttp2());
177  } else {
178  m_protoHttp = new ProtocolHttp(m_server);
179  }
180  }
181  return m_protoHttp;
182 }
183 
184 ProtocolHttp2 *ServerEngine::getProtoHttp2()
185 {
186  if (!m_protoHttp2) {
187  m_protoHttp2 = new ProtocolHttp2(m_server);
188  }
189  return m_protoHttp2;
190 }
191 
192 Protocol *ServerEngine::getProtoFastCgi()
193 {
194  if (!m_protoFcgi) {
195  m_protoFcgi = new ProtocolFastCGI(m_server);
196  }
197  return m_protoFcgi;
198 }
199 
201 {
202  if (Q_LIKELY(initApplication())) {
203  return true;
204  }
205 
206  return false;
207 }
208 
209 void ServerEngine::handleSocketShutdown(Socket *socket)
210 {
211  if (socket->processing == 0) {
212  socket->connectionClose();
213  } else if (socket->proto->type() == Protocol::Type::Http11Websocket) {
214  auto req = static_cast<ProtoRequestHttp *>(socket->protoData);
215  req->webSocketClose(Response::CloseCode::CloseCodeGoingAway, {});
216  } else {
217  socket->protoData->headerConnection = ProtocolData::HeaderConnection::Close;
218  }
219 }
220 
221 #include "moc_serverengine.cpp"
QFuture< ArgsType< Signal >> connect(Sender *sender, Signal signal)
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