cutelyst  3.9.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
enginerequest.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2017-2022 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "enginerequest.h"
6 
7 #include "common.h"
8 
9 #include <Cutelyst/Context>
10 #include <Cutelyst/response_p.h>
11 
12 #include <QLoggingCategory>
13 Q_LOGGING_CATEGORY(CUTELYST_ENGINEREQUEST, "cutelyst.engine_request", QtWarningMsg)
14 
15 using namespace Cutelyst;
16 
17 EngineRequest::EngineRequest()
18 {
19 }
20 
21 EngineRequest::~EngineRequest()
22 {
23  delete context;
24 }
25 
27 {
28  if (!(status & EngineRequest::Chunked)) {
29  Response *response = context->response();
30  QIODevice *body = response->bodyDevice();
31 
32  if (body) {
33  if (!body->isSequential()) {
34  body->seek(0);
35  }
36 
37  char block[64 * 1024];
38  while (!body->atEnd()) {
39  qint64 in = body->read(block, sizeof(block));
40  if (in <= 0) {
41  break;
42  }
43 
44  if (write(block, in) != in) {
45  qCWarning(CUTELYST_ENGINEREQUEST) << "Failed to write body";
46  break;
47  }
48  }
49  } else {
50  const QByteArray bodyByteArray = response->body();
51  write(bodyByteArray.constData(), bodyByteArray.size());
52  }
53  } else if (!(status & EngineRequest::ChunkedDone)) {
54  // Write the final '0' chunk
55  doWrite("0\r\n\r\n", 5);
56  }
57 }
58 
60 {
61  Response *res = context->response();
62 
63  res->setContentType(QStringLiteral("text/html; charset=utf-8"));
64 
66 
67  // Trick IE. Old versions of IE would display their own error page instead
68  // of ours if we'd give it less than 512 bytes.
69  body.reserve(512);
70 
71  body.append(context->errors().join(QLatin1Char('\n')).toUtf8());
72 
73  res->setBody(body);
74 
75  // Return 500
76  res->setStatus(Response::InternalServerError);
77 }
78 
80 {
81  if (context->error()) {
82  finalizeError();
83  }
84 
85  if ((status & EngineRequest::FinalizedHeaders) || finalizeHeaders()) {
86  finalizeBody();
87  }
88 
89  status |= EngineRequest::Finalized;
91 }
92 
94 {
95  Response *res = context->response();
96  Headers &headers = res->headers();
97  const auto cookies = res->cookies();
98  for (const QNetworkCookie &cookie : cookies) {
99  headers.pushHeader(QStringLiteral("SET_COOKIE"), QString::fromLatin1(cookie.toRawForm()));
100  }
101 #if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
102  const auto cuteCookies = res->cuteCookies();
103  for (const Cookie &cookie : cuteCookies) {
104  headers.pushHeader(QStringLiteral("SET_COOKIE"), QString::fromLatin1(cookie.toRawForm()));
105  }
106 #endif
107 }
108 
110 {
111  Response *response = context->response();
112  Headers &headers = response->headers();
113 
114  // Fix missing content length
115  if (headers.contentLength() < 0) {
116  qint64 size = response->size();
117  if (size >= 0) {
119  }
120  }
121 
122  finalizeCookies();
123 
124  // Done
125  status |= EngineRequest::FinalizedHeaders;
126  return writeHeaders(response->status(), headers);
127 }
128 
129 qint64 EngineRequest::write(const char *data, qint64 len)
130 {
131  if (!(status & EngineRequest::Chunked)) {
132  return doWrite(data, len);
133  } else if (!(status & EngineRequest::ChunkedDone)) {
134  const QByteArray chunkSize = QByteArray::number(len, 16).toUpper();
135  QByteArray chunk;
136  chunk.reserve(int(len + chunkSize.size() + 4));
137  chunk.append(chunkSize).append("\r\n", 2).append(data, int(len)).append("\r\n", 2);
138 
139  qint64 retWrite = doWrite(chunk.data(), chunk.size());
140 
141  // Flag if we wrote an empty chunk
142  if (!len) {
143  status |= EngineRequest::ChunkedDone;
144  }
145 
146  return retWrite == chunk.size() ? len : -1;
147  }
148  return -1;
149 }
150 
151 bool EngineRequest::webSocketHandshake(const QString &key,
152  const QString &origin,
153  const QString &protocol)
154 {
155  if (status & EngineRequest::FinalizedHeaders) {
156  return false;
157  }
158 
159  if (webSocketHandshakeDo(key, origin, protocol)) {
160  status |= EngineRequest::FinalizedHeaders | EngineRequest::Async | EngineRequest::IOWrite;
161 
162  context->finalize();
163 
164  return true;
165  }
166 
167  return false;
168 }
169 
170 bool EngineRequest::webSocketSendTextMessage(const QString &message)
171 {
172  Q_UNUSED(message)
173  return false;
174 }
175 
176 bool EngineRequest::webSocketSendBinaryMessage(const QByteArray &message)
177 {
178  Q_UNUSED(message)
179  return false;
180 }
181 
182 bool EngineRequest::webSocketSendPing(const QByteArray &payload)
183 {
184  Q_UNUSED(payload)
185  return false;
186 }
187 
188 bool EngineRequest::webSocketClose(quint16 code, const QString &reason)
189 {
190  Q_UNUSED(code)
191  Q_UNUSED(reason)
192  return false;
193 }
194 
196 {
197 }
198 
199 bool EngineRequest::webSocketHandshakeDo(const QString &key,
200  const QString &origin,
201  const QString &protocol)
202 {
203  Q_UNUSED(key)
204  Q_UNUSED(origin)
205  Q_UNUSED(protocol)
206  return false;
207 }
208 
209 void EngineRequest::setPath(char *rawPath, const int len)
210 {
211  if (len == 0) {
212  path = QString();
213  return;
214  }
215 
216  char *data = rawPath;
217  const char *inputPtr = data;
218 
219  bool skipUtf8 = true;
220  int outlen = 0;
221  for (int i = 0; i < len; ++i, ++outlen) {
222  const char c = inputPtr[i];
223  if (c == '%' && i + 2 < len) {
224  int a = inputPtr[++i];
225  int b = inputPtr[++i];
226 
227  if (a >= '0' && a <= '9')
228  a -= '0';
229  else if (a >= 'a' && a <= 'f')
230  a = a - 'a' + 10;
231  else if (a >= 'A' && a <= 'F')
232  a = a - 'A' + 10;
233 
234  if (b >= '0' && b <= '9')
235  b -= '0';
236  else if (b >= 'a' && b <= 'f')
237  b = b - 'a' + 10;
238  else if (b >= 'A' && b <= 'F')
239  b = b - 'A' + 10;
240 
241  *data++ = char((a << 4) | b);
242  skipUtf8 = false;
243  } else if (c == '+') {
244  *data++ = ' ';
245  } else {
246  *data++ = c;
247  }
248  }
249 
250  if (skipUtf8) {
251  path = QString::fromLatin1(rawPath, outlen);
252  } else {
253  path = QString::fromUtf8(rawPath, outlen);
254  }
255 }
256 
257 #include "moc_enginerequest.cpp"
QStringList errors() const noexcept
Returns a list of errors that were defined.
Definition: context.cpp:66
void finalize()
finalize the request right away this is automatically called at the end of the actions chain
Definition: context.cpp:493
bool error() const noexcept
Returns true if an error was set.
Definition: context.cpp:49
Response * response() const noexcept
Definition: context.cpp:96
The Cutelyst Cookie.
Definition: cookie.h:29
virtual qint64 doWrite(const char *data, qint64 len)=0
Reimplement this to do the RAW writing to the client.
Context * context
The Cutelyst::Context of this request.
virtual void finalizeBody()
Engines must reimplement this to write the response body back to the caller.
virtual void finalizeError()
Engines should overwrite this if they want to to make custom error messages.
QString path
Call setPath() instead.
Status status
Connection status.
void finalize()
Called by Application to deal with finalizing cookies, headers and body.
qint64 write(const char *data, qint64 len)
Called by Response to manually write data.
void setPath(char *rawPath, const int len)
This method sets the path and already does the decoding so that it is done a single time.
Headers headers
The request headers.
virtual bool writeHeaders(quint16 status, const Headers &headers)=0
Reimplement this to write the headers back to the client.
virtual bool finalizeHeaders()
Finalize the headers, and call doWriteHeader(), reimplemententions must call this first.
virtual void finalizeCookies()
Reimplement if you need a custom way to Set-Cookie, the default implementation writes them to c->res(...
QIODevice * body
The QIODevice containing the body (if any) of the request.
QString protocol
The protocol requested by the user agent 'HTTP1/1'.
virtual void processingFinished()
This is called when the Application chain is finished processing this request, here the request can s...
void setContentLength(qint64 value)
Definition: headers.cpp:167
void pushHeader(const QString &field, const QString &value)
Definition: headers.cpp:406
qint64 contentLength() const
Definition: headers.cpp:158
void setStatus(quint16 status) noexcept
Definition: response.cpp:70
Headers & headers() noexcept
void setBody(QIODevice *body)
Definition: response.cpp:100
QIODevice * bodyDevice() const
Definition: response.cpp:94
QList< QNetworkCookie > cookies() const
Definition: response.cpp:218
virtual qint64 size() const noexcept override
Q_REQUIRED_RESULT QByteArray & body()
Definition: response.cpp:83
quint16 status() const noexcept
Definition: response.cpp:64
void setContentType(const QString &type)
Definition: response.h:220
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8
QByteArray & append(char ch)
const char * constData() const const
char * data()
QByteArray number(int n, int base)
void reserve(int size)
int size() const const
QByteArray toUpper() const const
virtual bool atEnd() const const
virtual bool isSequential() const const
qint64 read(char *data, qint64 maxSize)
virtual bool seek(qint64 pos)
QString fromLatin1(const char *str, int size)
QString fromUtf8(const char *str, int size)
QString join(const QString &separator) const const