cutelyst  3.7.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/response_p.h>
10 #include <Cutelyst/Context>
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 
22 EngineRequest::~EngineRequest()
23 {
24  delete context;
25 }
26 
28 {
29  if (!(status & EngineRequest::Chunked)) {
30  Response *response = context->response();
31  QIODevice *body = response->bodyDevice();
32 
33  if (body) {
34  if (!body->isSequential()) {
35  body->seek(0);
36  }
37 
38  char block[64 * 1024];
39  while (!body->atEnd()) {
40  qint64 in = body->read(block, sizeof(block));
41  if (in <= 0) {
42  break;
43  }
44 
45  if (write(block, in) != in) {
46  qCWarning(CUTELYST_ENGINEREQUEST) << "Failed to write body";
47  break;
48  }
49  }
50  } else {
51  const QByteArray bodyByteArray = response->body();
52  write(bodyByteArray.constData(), bodyByteArray.size());
53  }
54  } else if (!(status & EngineRequest::ChunkedDone)) {
55  // Write the final '0' chunk
56  doWrite("0\r\n\r\n", 5);
57  }
58 }
59 
61 {
62  Response *res = context->response();
63 
64  res->setContentType(QStringLiteral("text/html; charset=utf-8"));
65 
67 
68  // Trick IE. Old versions of IE would display their own error page instead
69  // of ours if we'd give it less than 512 bytes.
70  body.reserve(512);
71 
72  body.append(context->errors().join(QLatin1Char('\n')).toUtf8());
73 
74  res->setBody(body);
75 
76  // Return 500
77  res->setStatus(Response::InternalServerError);
78 }
79 
81 {
82  if (context->error()) {
83  finalizeError();
84  }
85 
86  if ((status & EngineRequest::FinalizedHeaders) || finalizeHeaders()) {
87  finalizeBody();
88  }
89 
90  status |= EngineRequest::Finalized;
92 }
93 
95 {
96  Response *res = context->response();
97  Headers &headers = res->headers();
98  const auto cookies = res->cookies();
99  for (const QNetworkCookie &cookie : cookies) {
100  headers.pushHeader(QStringLiteral("SET_COOKIE"), QString::fromLatin1(cookie.toRawForm()));
101  }
102 #if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
103  const auto cuteCookies = res->cuteCookies();
104  for (const Cookie &cookie : cuteCookies) {
105  headers.pushHeader(QStringLiteral("SET_COOKIE"), QString::fromLatin1(cookie.toRawForm()));
106  }
107 #endif
108 }
109 
111 {
112  Response *response = context->response();
113  Headers &headers = response->headers();
114 
115  // Fix missing content length
116  if (headers.contentLength() < 0) {
117  qint64 size = response->size();
118  if (size >= 0) {
120  }
121  }
122 
123  finalizeCookies();
124 
125  // Done
126  status |= EngineRequest::FinalizedHeaders;
127  return writeHeaders(response->status(), headers);
128 }
129 
130 qint64 EngineRequest::write(const char *data, qint64 len)
131 {
132  if (!(status & EngineRequest::Chunked)) {
133  return doWrite(data, len);
134  } else if (!(status & EngineRequest::ChunkedDone)) {
135  const QByteArray chunkSize = QByteArray::number(len, 16).toUpper();
136  QByteArray chunk;
137  chunk.reserve(int(len + chunkSize.size() + 4));
138  chunk.append(chunkSize).append("\r\n", 2)
139  .append(data, int(len)).append("\r\n", 2);
140 
141  qint64 retWrite = doWrite(chunk.data(), chunk.size());
142 
143  // Flag if we wrote an empty chunk
144  if (!len) {
145  status |= EngineRequest::ChunkedDone;
146  }
147 
148  return retWrite == chunk.size() ? len : -1;
149  }
150  return -1;
151 }
152 
153 bool EngineRequest::webSocketHandshake(const QString &key, const QString &origin, 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, const QString &origin, const QString &protocol)
200 {
201  Q_UNUSED(key)
202  Q_UNUSED(origin)
203  Q_UNUSED(protocol)
204  return false;
205 }
206 
207 void EngineRequest::setPath(char *rawPath, const int len)
208 {
209  if (len == 0) {
210  path = QString();
211  return;
212  }
213 
214  char *data = rawPath;
215  const char *inputPtr = data;
216 
217  bool skipUtf8 = true;
218  int outlen = 0;
219  for (int i = 0; i < len; ++i, ++outlen) {
220  const char c = inputPtr[i];
221  if (c == '%' && i + 2 < len) {
222  int a = inputPtr[++i];
223  int b = inputPtr[++i];
224 
225  if (a >= '0' && a <= '9') a -= '0';
226  else if (a >= 'a' && a <= 'f') a = a - 'a' + 10;
227  else if (a >= 'A' && a <= 'F') a = a - 'A' + 10;
228 
229  if (b >= '0' && b <= '9') b -= '0';
230  else if (b >= 'a' && b <= 'f') b = b - 'a' + 10;
231  else if (b >= 'A' && b <= 'F') b = b - 'A' + 10;
232 
233  *data++ = char((a << 4) | b);
234  skipUtf8 = false;
235  } else if (c == '+') {
236  *data++ = ' ';
237  } else {
238  *data++ = c;
239  }
240  }
241 
242  if (skipUtf8) {
243  path = QString::fromLatin1(rawPath, outlen);
244  } else {
245  path = QString::fromUtf8(rawPath, outlen);
246  }
247 }
248 
249 #include "moc_enginerequest.cpp"
QStringList errors() const noexcept
Returns a list of errors that were defined.
Definition: context.cpp:67
void finalize()
finalize the request right away this is automatically called at the end of the actions chain
Definition: context.cpp:483
bool error() const noexcept
Returns true if an error was set.
Definition: context.cpp:50
Response * response() const noexcept
Definition: context.cpp:97
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:168
void pushHeader(const QString &field, const QString &value)
Definition: headers.cpp:410
qint64 contentLength() const
Definition: headers.cpp:159
void setStatus(quint16 status) noexcept
Definition: response.cpp:72
Headers & headers() noexcept
void setBody(QIODevice *body)
Definition: response.cpp:101
QIODevice * bodyDevice() const
Definition: response.cpp:95
QList< QNetworkCookie > cookies() const
Definition: response.cpp:217
virtual qint64 size() const noexcept override
Q_REQUIRED_RESULT QByteArray & body()
Definition: response.cpp:84
quint16 status() const noexcept
Definition: response.cpp:66
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