Cutelyst  2.3.0
engine.cpp
1 /*
2  * Copyright (C) 2013-2017 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  // Debug messages should be disabled by default
65  QLoggingCategory::setFilterRules(QLatin1String("cutelyst.*.debug=false"));
66 
67  d->opts = opts;
68  d->workerCore = workerCore;
69 
70  // If workerCore is greater than 0 we need a new application instance
71  if (workerCore) {
72  auto newApp = qobject_cast<Application *>(app->metaObject()->newInstance());
73  if (!newApp) {
74  qFatal("*** FATAL *** Could not create a NEW instance of your Cutelyst::Application, "
75  "make sure your constructor has Q_INVOKABLE macro or disable threaded mode.");
76  }
77  d->app = newApp;
78  } else {
79  d->app = app;
80  }
81 
82  // To make easier for engines to clean up
83  // the app must be a child of it
84  d->app->setParent(this);
85 }
86 
87 Engine::~Engine()
88 {
89  delete d_ptr;
90 }
91 
97 {
98  Q_D(const Engine);
99  Q_ASSERT(d->app);
100  return d->app;
101 }
102 
123 {
124  Q_D(const Engine);
125  return d->workerCore;
126 }
127 
129 {
130  Q_D(Engine);
131 
132  if (thread() != QThread::currentThread()) {
133  qCCritical(CUTELYST_ENGINE) << "Cannot init application on a different thread";
134  return false;
135  }
136 
137  if (!d->app->setup(this)) {
138  qCCritical(CUTELYST_ENGINE) << "Failed to setup application";
139  return false;
140  }
141 
142  return true;
143 }
144 
146 {
147  Q_D(Engine);
148 
149  if (!d->app) {
150  qCCritical(CUTELYST_ENGINE) << "Failed to postForkApplication on a null application";
151  return false;
152  }
153 
154  QThread::currentThread()->setObjectName(QString::number(d->workerCore));
155 
156  return d->app->enginePostFork();
157 }
158 
159 quint64 Engine::time()
160 {
161  return QDateTime::currentMSecsSinceEpoch() * 1000;
162 }
163 
164 const char *Engine::httpStatusMessage(quint16 status, int *len)
165 {
166  const char *ret;
167  switch (status) {
168  case Response::OK:
169  ret = "HTTP/1.1 200 OK";
170  break;
171  case Response::Found:
172  ret = "HTTP/1.1 302 Found";
173  break;
174  case Response::NotFound:
175  ret = "HTTP/1.1 404 Not Found";
176  break;
177  case Response::InternalServerError:
178  ret = "HTTP/1.1 500 Internal Server Error";
179  break;
180  case Response::MovedPermanently:
181  ret = "HTTP/1.1 301 Moved Permanently";
182  break;
183  case Response::NotModified:
184  ret = "HTTP/1.1 304 Not Modified";
185  break;
186  case Response::SeeOther:
187  ret = "HTTP/1.1 303 See Other";
188  break;
189  case Response::Forbidden:
190  ret = "HTTP/1.1 403 Forbidden";
191  break;
192  case Response::TemporaryRedirect:
193  ret = "HTTP/1.1 307 Temporary Redirect";
194  break;
195  case Response::Unauthorized:
196  ret = "HTTP/1.1 401 Unauthorized";
197  break;
198  case Response::BadRequest:
199  ret = "HTTP/1.1 400 Bad Request";
200  break;
201  case Response::MethodNotAllowed:
202  ret = "HTTP/1.1 405 Method Not Allowed";
203  break;
204  case Response::RequestTimeout:
205  ret = "HTTP/1.1 408 Request Timeout";
206  break;
207  case Response::Continue:
208  ret = "HTTP/1.1 100 Continue";
209  break;
210  case Response::SwitchingProtocols:
211  ret = "HTTP/1.1 101 Switching Protocols";
212  break;
213  case Response::Created:
214  ret = "HTTP/1.1 201 Created";
215  break;
216  case Response::Accepted:
217  ret = "HTTP/1.1 202 Accepted";
218  break;
219  case Response::NonAuthoritativeInformation:
220  ret = "HTTP/1.1 203 Non-Authoritative Information";
221  break;
222  case Response::NoContent:
223  ret = "HTTP/1.1 204 No Content";
224  break;
225  case Response::ResetContent:
226  ret = "HTTP/1.1 205 Reset Content";
227  break;
228  case Response::PartialContent:
229  ret = "HTTP/1.1 206 Partial Content";
230  break;
231  case Response::MultipleChoices:
232  ret = "HTTP/1.1 300 Multiple Choices";
233  break;
234  case Response::UseProxy:
235  ret = "HTTP/1.1 305 Use Proxy";
236  break;
237  case Response::PaymentRequired:
238  ret = "HTTP/1.1 402 Payment Required";
239  break;
240  case Response::NotAcceptable:
241  ret = "HTTP/1.1 406 Not Acceptable";
242  break;
243  case Response::ProxyAuthenticationRequired:
244  ret = "HTTP/1.1 407 Proxy Authentication Required";
245  break;
246  case Response::Conflict:
247  ret = "HTTP/1.1 409 Conflict";
248  break;
249  case Response::Gone:
250  ret = "HTTP/1.1 410 Gone";
251  break;
252  case Response::LengthRequired:
253  ret = "HTTP/1.1 411 Length Required";
254  break;
255  case Response::PreconditionFailed:
256  ret = "HTTP/1.1 412 Precondition Failed";
257  break;
258  case Response::RequestEntityTooLarge:
259  ret = "HTTP/1.1 413 Request Entity Too Large";
260  break;
261  case Response::RequestURITooLong:
262  ret = "HTTP/1.1 414 Request-URI Too Long";
263  break;
264  case Response::UnsupportedMediaType:
265  ret = "HTTP/1.1 415 Unsupported Media Type";
266  break;
267  case Response::RequestedRangeNotSatisfiable:
268  ret = "HTTP/1.1 416 Requested Range Not Satisfiable";
269  break;
270  case Response::ExpectationFailed:
271  ret = "HTTP/1.1 417 Expectation Failed";
272  break;
273  case Response::NotImplemented:
274  ret = "HTTP/1.1 501 Not Implemented";
275  break;
276  case Response::BadGateway:
277  ret = "HTTP/1.1 502 Bad Gateway";
278  break;
279  case Response::ServiceUnavailable:
280  ret = "HTTP/1.1 503 Service Unavailable";
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:164
void processRequest(EngineRequest *request)
Definition: engine.cpp:308
bool initApplication()
initApplication
Definition: engine.cpp:128
bool postForkApplication()
postForkApplication
Definition: engine.cpp:145
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:96
virtual quint64 time()
Definition: engine.cpp:159
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:122
virtual void processingFinished()
This is called when the Application chain is finished processing this request, here the request can s...
void processRequestAsync(EngineRequest *request)