cutelyst 4.3.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
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
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:458
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.
QByteArray & append(QByteArrayView data)
const char * constData() const const
char * data()
QByteArray number(double n, char format, int precision)
void reserve(qsizetype size)
qsizetype size() const const
QByteArray toUpper() const const
virtual bool atEnd() const const
virtual bool isSequential() const const
QByteArray read(qint64 maxSize)
virtual bool seek(qint64 pos)
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
QString & prepend(QChar ch)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const