Cutelyst  2.5.0
engine.cpp
1 /*
2  * Copyright (C) 2013-2018 Daniel Nicoletti <dantti12@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 #include "engine_p.h"
19 
20 #include "context_p.h"
21 
22 #include "common.h"
23 #include "request_p.h"
24 #include "application.h"
25 #include "response_p.h"
26 #include "context_p.h"
27 
28 #include <QUrl>
29 #include <QSettings>
30 #include <QDir>
31 #include <QThread>
32 #include <QByteArray>
33 #include <QJsonDocument>
34 
35 using namespace Cutelyst;
36 
57 Engine::Engine(Cutelyst::Application *app, int workerCore, const QVariantMap &opts)
58  : d_ptr(new EnginePrivate)
59 {
60  Q_D(Engine);
61 
62  connect(this, &Engine::processRequestAsync, this, &Engine::processRequest, Qt::QueuedConnection);
63 
64  d->opts = opts;
65  d->workerCore = workerCore;
66 
67  // If workerCore is greater than 0 we need a new application instance
68  if (workerCore) {
69  auto newApp = qobject_cast<Application *>(app->metaObject()->newInstance());
70  if (!newApp) {
71  qFatal("*** FATAL *** Could not create a NEW instance of your Cutelyst::Application, "
72  "make sure your constructor has Q_INVOKABLE macro or disable threaded mode.");
73  }
74  d->app = newApp;
75  } else {
76  d->app = app;
77  }
78 
79  // To make easier for engines to clean up
80  // the app must be a child of it
81  d->app->setParent(this);
82 }
83 
84 Engine::~Engine()
85 {
86  delete d_ptr;
87 }
88 
94 {
95  Q_D(const Engine);
96  Q_ASSERT(d->app);
97  return d->app;
98 }
99 
120 {
121  Q_D(const Engine);
122  return d->workerCore;
123 }
124 
126 {
127  Q_D(Engine);
128 
129  if (thread() != QThread::currentThread()) {
130  qCCritical(CUTELYST_ENGINE) << "Cannot init application on a different thread";
131  return false;
132  }
133 
134  if (!d->app->setup(this)) {
135  qCCritical(CUTELYST_ENGINE) << "Failed to setup application";
136  return false;
137  }
138 
139  return true;
140 }
141 
143 {
144  Q_D(Engine);
145 
146  if (!d->app) {
147  qCCritical(CUTELYST_ENGINE) << "Failed to postForkApplication on a null application";
148  return false;
149  }
150 
151  QThread::currentThread()->setObjectName(QString::number(d->workerCore));
152 
153  return d->app->enginePostFork();
154 }
155 
156 quint64 Engine::time()
157 {
158  return QDateTime::currentMSecsSinceEpoch() * 1000;
159 }
160 
161 const char *Engine::httpStatusMessage(quint16 status, int *len)
162 {
163  const char *ret;
164  switch (status) {
165  case Response::OK:
166  ret = "HTTP/1.1 200 OK";
167  break;
168  case Response::Found:
169  ret = "HTTP/1.1 302 Found";
170  break;
171  case Response::NotFound:
172  ret = "HTTP/1.1 404 Not Found";
173  break;
174  case Response::InternalServerError:
175  ret = "HTTP/1.1 500 Internal Server Error";
176  break;
177  case Response::MovedPermanently:
178  ret = "HTTP/1.1 301 Moved Permanently";
179  break;
180  case Response::NotModified:
181  ret = "HTTP/1.1 304 Not Modified";
182  break;
183  case Response::SeeOther:
184  ret = "HTTP/1.1 303 See Other";
185  break;
186  case Response::Forbidden:
187  ret = "HTTP/1.1 403 Forbidden";
188  break;
189  case Response::TemporaryRedirect:
190  ret = "HTTP/1.1 307 Temporary Redirect";
191  break;
192  case Response::Unauthorized:
193  ret = "HTTP/1.1 401 Unauthorized";
194  break;
195  case Response::BadRequest:
196  ret = "HTTP/1.1 400 Bad Request";
197  break;
198  case Response::MethodNotAllowed:
199  ret = "HTTP/1.1 405 Method Not Allowed";
200  break;
201  case Response::RequestTimeout:
202  ret = "HTTP/1.1 408 Request Timeout";
203  break;
204  case Response::Continue:
205  ret = "HTTP/1.1 100 Continue";
206  break;
207  case Response::SwitchingProtocols:
208  ret = "HTTP/1.1 101 Switching Protocols";
209  break;
210  case Response::Created:
211  ret = "HTTP/1.1 201 Created";
212  break;
213  case Response::Accepted:
214  ret = "HTTP/1.1 202 Accepted";
215  break;
216  case Response::NonAuthoritativeInformation:
217  ret = "HTTP/1.1 203 Non-Authoritative Information";
218  break;
219  case Response::NoContent:
220  ret = "HTTP/1.1 204 No Content";
221  break;
222  case Response::ResetContent:
223  ret = "HTTP/1.1 205 Reset Content";
224  break;
225  case Response::PartialContent:
226  ret = "HTTP/1.1 206 Partial Content";
227  break;
228  case Response::MultipleChoices:
229  ret = "HTTP/1.1 300 Multiple Choices";
230  break;
231  case Response::UseProxy:
232  ret = "HTTP/1.1 305 Use Proxy";
233  break;
234  case Response::PaymentRequired:
235  ret = "HTTP/1.1 402 Payment Required";
236  break;
237  case Response::NotAcceptable:
238  ret = "HTTP/1.1 406 Not Acceptable";
239  break;
240  case Response::ProxyAuthenticationRequired:
241  ret = "HTTP/1.1 407 Proxy Authentication Required";
242  break;
243  case Response::Conflict:
244  ret = "HTTP/1.1 409 Conflict";
245  break;
246  case Response::Gone:
247  ret = "HTTP/1.1 410 Gone";
248  break;
249  case Response::LengthRequired:
250  ret = "HTTP/1.1 411 Length Required";
251  break;
252  case Response::PreconditionFailed:
253  ret = "HTTP/1.1 412 Precondition Failed";
254  break;
255  case Response::RequestEntityTooLarge:
256  ret = "HTTP/1.1 413 Request Entity Too Large";
257  break;
258  case Response::RequestURITooLong:
259  ret = "HTTP/1.1 414 Request-URI Too Long";
260  break;
261  case Response::UnsupportedMediaType:
262  ret = "HTTP/1.1 415 Unsupported Media Type";
263  break;
264  case Response::RequestedRangeNotSatisfiable:
265  ret = "HTTP/1.1 416 Requested Range Not Satisfiable";
266  break;
267  case Response::ExpectationFailed:
268  ret = "HTTP/1.1 417 Expectation Failed";
269  break;
270  case Response::NotImplemented:
271  ret = "HTTP/1.1 501 Not Implemented";
272  break;
273  case Response::BadGateway:
274  ret = "HTTP/1.1 502 Bad Gateway";
275  break;
276  case Response::ServiceUnavailable:
277  ret = "HTTP/1.1 503 Service Unavailable";
278  break;
279  case Response::MultiStatus:
280  ret = "HTTP/1.1 207 Multi-Status";
281  break;
282  case Response::GatewayTimeout:
283  ret = "HTTP/1.1 504 Gateway Timeout";
284  break;
285  case Response::HTTPVersionNotSupported:
286  ret = "HTTP/1.1 505 HTTP Version Not Supported";
287  break;
288  case Response::BandwidthLimitExceeded:
289  ret = "HTTP/1.1 509 Bandwidth Limit Exceeded";
290  break;
291  default:
292  ret = QByteArrayLiteral("HTTP/1.1 ").append(QByteArray::number(status)).constData();
293  break;
294  }
295 
296  if (len) {
297  *len = strlen(ret);
298  }
299  return ret;
300 }
301 
303 {
304  Q_D(Engine);
305  return d->app->defaultHeaders();
306 }
307 
309 {
310  Q_D(Engine);
311 
312  d->app->handleRequest(request);
313 
314  request->processingFinished();
315 }
316 
317 QVariantMap Engine::opts() const
318 {
319  Q_D(const Engine);
320  return d->opts;
321 }
322 
323 QVariantMap Engine::config(const QString &entity) const
324 {
325  Q_D(const Engine);
326  return d->config.value(entity).toMap();
327 }
328 
329 void Engine::setConfig(const QVariantMap &config)
330 {
331  Q_D(Engine);
332  d->config = config;
333 }
334 
335 QVariantMap Engine::loadIniConfig(const QString &filename)
336 {
337  QVariantMap ret;
338  QSettings settings(filename, QSettings::IniFormat);
339  if (settings.status() != QSettings::NoError) {
340  qCWarning(CUTELYST_ENGINE) << "Failed to load INI file:" << settings.status();
341  return ret;
342  }
343 
344  const auto groups = settings.childGroups();
345  for (const QString &group : groups) {
346  QVariantMap configGroup;
347  settings.beginGroup(group);
348  const auto child = settings.childKeys();
349  for (const QString &key : child) {
350  configGroup.insert(key, settings.value(key));
351  }
352  settings.endGroup();
353  ret.insert(group, configGroup);
354  }
355 
356  return ret;
357 }
358 
359 QVariantMap Engine::loadJsonConfig(const QString &filename)
360 {
361  QVariantMap ret;
362  QFile file(filename);
363  if (!file.open(QIODevice::ReadOnly)) {
364  return ret;
365  }
366  QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
367 
368  ret = doc.toVariant().toMap();
369 
370  return ret;
371 }
372 
373 #include "moc_engine.cpp"
void setConfig(const QVariantMap &config)
Definition: engine.cpp:329
static const char * httpStatusMessage(quint16 status, int *len=nullptr)
Definition: engine.cpp:161
void processRequest(EngineRequest *request)
Definition: engine.cpp:308
bool initApplication()
initApplication
Definition: engine.cpp:125
bool postForkApplication()
postForkApplication
Definition: engine.cpp:142
static QVariantMap loadIniConfig(const QString &filename)
Definition: engine.cpp:335
Engine(Application *app, int workerCore, const QVariantMap &opts)
Definition: engine.cpp:57
QVariantMap opts() const
Definition: engine.cpp:317
QVariantMap config(const QString &entity) const
user configuration for the application
Definition: engine.cpp:323
static QVariantMap loadJsonConfig(const QString &filename)
Definition: engine.cpp:359
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
Application * app() const
application
Definition: engine.cpp:93
virtual quint64 time()
Definition: engine.cpp:156
The Cutelyst Application.
Definition: application.h:55
Headers & defaultHeaders()
Definition: engine.cpp:302
The Cutelyst Engine.
Definition: engine.h:33
int workerCore() const
Each worker process migth have a number of worker cores (threads), a single process with two worker t...
Definition: engine.cpp:119
virtual void processingFinished()
This is called when the Application chain is finished processing this request, here the request can s...
void processRequestAsync(EngineRequest *request)