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