cutelyst 4.0.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>
13Q_LOGGING_CATEGORY(CUTELYST_ENGINEREQUEST, "cutelyst.engine_request", QtWarningMsg)
14
15using namespace Cutelyst;
16
17EngineRequest::EngineRequest()
18{
19}
20
21EngineRequest::~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("text/html; charset=utf-8"_qba);
64
65 QByteArray body;
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()) {
83 }
84
85 if ((status & EngineRequest::FinalizedHeaders) || finalizeHeaders()) {
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("Set-Cookie"_qba, cookie.toRawForm());
100 }
101}
102
104{
105 Response *response = context->response();
106 Headers &headers = response->headers();
107
108 // Fix missing content length
109 if (headers.contentLength() < 0) {
110 qint64 size = response->size();
111 if (size >= 0) {
113 }
114 }
115
117
118 // Done
119 status |= EngineRequest::FinalizedHeaders;
120 return writeHeaders(response->status(), headers);
121}
122
123qint64 EngineRequest::write(const char *data, qint64 len)
124{
125 if (!(status & EngineRequest::Chunked)) {
126 return doWrite(data, len);
127 } else if (!(status & EngineRequest::ChunkedDone)) {
128 const QByteArray chunkSize = QByteArray::number(len, 16).toUpper();
129 QByteArray chunk;
130 chunk.reserve(int(len + chunkSize.size() + 4));
131 chunk.append(chunkSize).append("\r\n", 2).append(data, int(len)).append("\r\n", 2);
132
133 qint64 retWrite = doWrite(chunk.data(), chunk.size());
134
135 // Flag if we wrote an empty chunk
136 if (!len) {
137 status |= EngineRequest::ChunkedDone;
138 }
139
140 return retWrite == chunk.size() ? len : -1;
141 }
142 return -1;
143}
144
145bool EngineRequest::webSocketHandshake(const QByteArray &key,
146 const QByteArray &origin,
147 const QByteArray &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
164bool EngineRequest::webSocketSendTextMessage(const QString &message)
165{
166 Q_UNUSED(message)
167 return false;
168}
169
170bool EngineRequest::webSocketSendBinaryMessage(const QByteArray &message)
171{
172 Q_UNUSED(message)
173 return false;
174}
175
176bool EngineRequest::webSocketSendPing(const QByteArray &payload)
177{
178 Q_UNUSED(payload)
179 return false;
180}
181
182bool EngineRequest::webSocketClose(quint16 code, const QString &reason)
183{
184 Q_UNUSED(code)
185 Q_UNUSED(reason)
186 return false;
187}
188
190{
191}
192
193bool EngineRequest::webSocketHandshakeDo(const QByteArray &key,
194 const QByteArray &origin,
195 const QByteArray &protocol)
196{
197 Q_UNUSED(key)
198 Q_UNUSED(origin)
199 Q_UNUSED(protocol)
200 return false;
201}
202
203void EngineRequest::setPath(char *rawPath, const int len)
204{
205 if (len == 0) {
206 path = u"/"_qs;
207 return;
208 }
209
210 char *data = rawPath;
211 const char *inputPtr = data;
212
213 bool lastSlash = false;
214 bool skipUtf8 = true;
215 int outlen = 0;
216 for (int i = 0; i < len; ++i, ++outlen) {
217 const char c = inputPtr[i];
218 if (c == '%' && i + 2 < len) {
219 int a = inputPtr[++i];
220 int b = inputPtr[++i];
221
222 if (a >= '0' && a <= '9')
223 a -= '0';
224 else if (a >= 'a' && a <= 'f')
225 a = a - 'a' + 10;
226 else if (a >= 'A' && a <= 'F')
227 a = a - 'A' + 10;
228
229 if (b >= '0' && b <= '9')
230 b -= '0';
231 else if (b >= 'a' && b <= 'f')
232 b = b - 'a' + 10;
233 else if (b >= 'A' && b <= 'F')
234 b = b - 'A' + 10;
235
236 *data++ = char((a << 4) | b);
237 skipUtf8 = false;
238 } else if (c == '+') {
239 *data++ = ' ';
240 } else if (c == '/') {
241 // Remove duplicated slashes
242 if (!lastSlash) {
243 *data++ = '/';
244 } else {
245 --outlen;
246 }
247 lastSlash = true;
248 continue;
249 } else {
250 *data++ = c;
251 }
252 lastSlash = false;
253 }
254
255 if (skipUtf8) {
256 path = QString::fromLatin1(rawPath, outlen);
257 } else {
258 path = QString::fromUtf8(rawPath, outlen);
259 }
260
261 if (!path.startsWith(u'/')) {
262 path.prepend(u'/');
263 }
264}
265
266#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:486
bool error() const noexcept
Returns true if an error was set.
Definition: context.cpp:49
Response * response() const noexcept
Definition: context.cpp:96
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.
QByteArray protocol
The protocol requested by the user agent 'HTTP1/1'.
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.
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:172
qint64 contentLength() const
Definition: headers.cpp:163
void pushHeader(const QByteArray &key, const QByteArray &value)
Definition: headers.cpp:458
qint64 size() const noexcept override
void setContentType(const QByteArray &type)
Definition: response.h:203
void setStatus(quint16 status) noexcept
Definition: response.cpp:72
void setBody(QIODevice *body)
Definition: response.cpp:102
Headers & headers() noexcept
QList< QNetworkCookie > cookies() const
Definition: response.cpp:191
QIODevice * bodyDevice() const noexcept
Definition: response.cpp:96
QByteArray & body()
Definition: response.cpp:85
quint16 status() const noexcept
Definition: response.cpp:66
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8