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 
66  QByteArray body;
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 }
103 
105 {
106  Response *response = context->response();
107  Headers &headers = response->headers();
108 
109  // Fix missing content length
110  if (headers.contentLength() < 0) {
111  qint64 size = response->size();
112  if (size >= 0) {
114  }
115  }
116 
117  finalizeCookies();
118 
119  // Done
120  status |= EngineRequest::FinalizedHeaders;
121  return writeHeaders(response->status(), headers);
122 }
123 
124 qint64 EngineRequest::write(const char *data, qint64 len)
125 {
126  if (!(status & EngineRequest::Chunked)) {
127  return doWrite(data, len);
128  } else if (!(status & EngineRequest::ChunkedDone)) {
129  const QByteArray chunkSize = QByteArray::number(len, 16).toUpper();
130  QByteArray chunk;
131  chunk.reserve(int(len + chunkSize.size() + 4));
132  chunk.append(chunkSize).append("\r\n", 2)
133  .append(data, int(len)).append("\r\n", 2);
134 
135  qint64 retWrite = doWrite(chunk.data(), chunk.size());
136 
137  // Flag if we wrote an empty chunk
138  if (!len) {
139  status |= EngineRequest::ChunkedDone;
140  }
141 
142  return retWrite == chunk.size() ? len : -1;
143  }
144  return -1;
145 }
146 
147 bool EngineRequest::webSocketHandshake(const QString &key, const QString &origin, const QString &protocol)
148 {
149  if (status & EngineRequest::FinalizedHeaders) {
150  return false;
151  }
152 
153  if (webSocketHandshakeDo(key, origin, protocol)) {
154  status |= EngineRequest::FinalizedHeaders | EngineRequest::Async | EngineRequest::IOWrite;
155 
156  context->finalize();
157 
158  return true;
159  }
160 
161  return false;
162 }
163 
164 bool EngineRequest::webSocketSendTextMessage(const QString &message)
165 {
166  Q_UNUSED(message)
167  return false;
168 }
169 
170 bool EngineRequest::webSocketSendBinaryMessage(const QByteArray &message)
171 {
172  Q_UNUSED(message)
173  return false;
174 }
175 
176 bool EngineRequest::webSocketSendPing(const QByteArray &payload)
177 {
178  Q_UNUSED(payload)
179  return false;
180 }
181 
182 bool EngineRequest::webSocketClose(quint16 code, const QString &reason)
183 {
184  Q_UNUSED(code)
185  Q_UNUSED(reason)
186  return false;
187 }
188 
190 {
191 }
192 
193 bool EngineRequest::webSocketHandshakeDo(const QString &key, const QString &origin, const QString &protocol)
194 {
195  Q_UNUSED(key)
196  Q_UNUSED(origin)
197  Q_UNUSED(protocol)
198  return false;
199 }
200 
201 void EngineRequest::setPath(char *rawPath, const int len)
202 {
203  if (len == 0) {
204  path = QString();
205  return;
206  }
207 
208  char *data = rawPath;
209  const char *inputPtr = data;
210 
211  bool skipUtf8 = true;
212  int outlen = 0;
213  for (int i = 0; i < len; ++i, ++outlen) {
214  const char c = inputPtr[i];
215  if (c == '%' && i + 2 < len) {
216  int a = inputPtr[++i];
217  int b = inputPtr[++i];
218 
219  if (a >= '0' && a <= '9') a -= '0';
220  else if (a >= 'a' && a <= 'f') a = a - 'a' + 10;
221  else if (a >= 'A' && a <= 'F') a = a - 'A' + 10;
222 
223  if (b >= '0' && b <= '9') b -= '0';
224  else if (b >= 'a' && b <= 'f') b = b - 'a' + 10;
225  else if (b >= 'A' && b <= 'F') b = b - 'A' + 10;
226 
227  *data++ = char((a << 4) | b);
228  skipUtf8 = false;
229  } else if (c == '+') {
230  *data++ = ' ';
231  } else {
232  *data++ = c;
233  }
234  }
235 
236  if (skipUtf8) {
237  path = QString::fromLatin1(rawPath, outlen);
238  } else {
239  path = QString::fromUtf8(rawPath, outlen);
240  }
241 }
242 
243 #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
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:209
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:217
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8