cutelyst 4.4.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 // Set content length if we have a valid one
109 const qint64 size = response->size();
110 if (size >= 0) {
112 }
113
115
116 // Done
117 status |= EngineRequest::FinalizedHeaders;
118 return writeHeaders(response->status(), headers);
119}
120
121qint64 EngineRequest::write(const char *data, qint64 len)
122{
123 if (!(status & EngineRequest::Chunked)) {
124 return doWrite(data, len);
125 } else if (!(status & EngineRequest::ChunkedDone)) {
126 const QByteArray chunkSize = QByteArray::number(len, 16).toUpper();
127 QByteArray chunk;
128 chunk.reserve(int(len + chunkSize.size() + 4));
129 chunk.append(chunkSize).append("\r\n", 2).append(data, int(len)).append("\r\n", 2);
130
131 qint64 retWrite = doWrite(chunk.data(), chunk.size());
132
133 // Flag if we wrote an empty chunk
134 if (!len) {
135 status |= EngineRequest::ChunkedDone;
136 }
137
138 return retWrite == chunk.size() ? len : -1;
139 }
140 return -1;
141}
142
143bool EngineRequest::webSocketHandshake(const QByteArray &key,
144 const QByteArray &origin,
145 const QByteArray &protocol)
146{
147 if (status & EngineRequest::FinalizedHeaders) {
148 return false;
149 }
150
151 if (webSocketHandshakeDo(key, origin, protocol)) {
152 status |= EngineRequest::FinalizedHeaders | EngineRequest::Async | EngineRequest::IOWrite;
153
154 context->finalize();
155
156 return true;
157 }
158
159 return false;
160}
161
162bool EngineRequest::webSocketSendTextMessage(const QString &message)
163{
164 Q_UNUSED(message)
165 return false;
166}
167
168bool EngineRequest::webSocketSendBinaryMessage(const QByteArray &message)
169{
170 Q_UNUSED(message)
171 return false;
172}
173
174bool EngineRequest::webSocketSendPing(const QByteArray &payload)
175{
176 Q_UNUSED(payload)
177 return false;
178}
179
180bool EngineRequest::webSocketClose(quint16 code, const QString &reason)
181{
182 Q_UNUSED(code)
183 Q_UNUSED(reason)
184 return false;
185}
186
188{
189}
190
191bool EngineRequest::webSocketHandshakeDo(const QByteArray &key,
192 const QByteArray &origin,
193 const QByteArray &protocol)
194{
195 Q_UNUSED(key)
196 Q_UNUSED(origin)
197 Q_UNUSED(protocol)
198 return false;
199}
200
201void EngineRequest::setPath(char *rawPath, const int len)
202{
203 if (len == 0) {
204 path = u"/"_qs;
205 return;
206 }
207
208 char *data = rawPath;
209 const char *inputPtr = data;
210
211 bool lastSlash = false;
212 bool skipUtf8 = true;
213 int outlen = 0;
214 for (int i = 0; i < len; ++i, ++outlen) {
215 const char c = inputPtr[i];
216 if (c == '%' && i + 2 < len) {
217 int a = inputPtr[++i];
218 int b = inputPtr[++i];
219
220 if (a >= '0' && a <= '9')
221 a -= '0';
222 else if (a >= 'a' && a <= 'f')
223 a = a - 'a' + 10;
224 else if (a >= 'A' && a <= 'F')
225 a = a - 'A' + 10;
226
227 if (b >= '0' && b <= '9')
228 b -= '0';
229 else if (b >= 'a' && b <= 'f')
230 b = b - 'a' + 10;
231 else if (b >= 'A' && b <= 'F')
232 b = b - 'A' + 10;
233
234 *data++ = char((a << 4) | b);
235 skipUtf8 = false;
236 } else if (c == '+') {
237 *data++ = ' ';
238 } else if (c == '/') {
239 // Remove duplicated slashes
240 if (!lastSlash) {
241 *data++ = '/';
242 } else {
243 --outlen;
244 }
245 lastSlash = true;
246 continue;
247 } else {
248 *data++ = c;
249 }
250 lastSlash = false;
251 }
252
253 if (skipUtf8) {
254 path = QString::fromLatin1(rawPath, outlen);
255 } else {
256 path = QString::fromUtf8(rawPath, outlen);
257 }
258
259 if (!path.startsWith(u'/')) {
260 path.prepend(u'/');
261 }
262}
263
264#include "moc_enginerequest.cpp"
QStringList errors() const noexcept
Definition: context.cpp:67
bool error() const noexcept
Definition: context.cpp:50
Response * response() const noexcept
Definition: context.cpp:97
virtual qint64 doWrite(const char *data, qint64 len)=0
virtual void finalizeBody()
virtual void finalizeError()
qint64 write(const char *data, qint64 len)
void setPath(char *rawPath, const int len)
virtual bool writeHeaders(quint16 status, const Headers &headers)=0
virtual bool finalizeHeaders()
virtual void finalizeCookies()
virtual void processingFinished()
Container for HTTP headers.
Definition: headers.h:24
void setContentLength(qint64 value)
Definition: headers.cpp:172
void pushHeader(const QByteArray &key, const QByteArray &value)
Definition: headers.cpp:460
A Cutelyst response.
Definition: response.h:29
qint64 size() const noexcept override
void setContentType(const QByteArray &type)
Definition: response.h:238
void setStatus(quint16 status) noexcept
Definition: response.cpp:72
void setBody(QIODevice *body)
Definition: response.cpp:103
Headers & headers() noexcept
QList< QNetworkCookie > cookies() const
Definition: response.cpp:206
QIODevice * bodyDevice() const noexcept
Definition: response.cpp:97
QByteArray & body()
Definition: response.cpp:85
quint16 status() const noexcept
Definition: response.cpp:66
The Cutelyst namespace holds all public Cutelyst API.