cutelyst 5.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;
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 = static_cast<unsigned char>(inputPtr[++i]);
219 int b = static_cast<unsigned char>(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
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
237 *data++ = char((a << 4) | b);
238 skipUtf8 = false;
239 } else if (c == '+') {
240 *data++ = ' ';
241 } else if (c == '/') {
242 // Remove duplicated slashes
243 if (!lastSlash) {
244 *data++ = '/';
245 } else {
246 --outlen;
247 }
248 lastSlash = true;
249 continue;
250 } else {
251 *data++ = c;
252 }
253 lastSlash = false;
254 }
255
256 if (skipUtf8) {
257 path = QString::fromLatin1(rawPath, outlen);
258 } else {
259 path = QString::fromUtf8(rawPath, outlen);
260 }
261
262 if (!path.startsWith(u'/')) {
263 path.prepend(u'/');
264 }
265}
266
267#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:464
A Cutelyst response.
Definition response.h:29
qint64 size() const noexcept override
void setContentType(const QByteArray &type)
Definition response.h:230
void setStatus(quint16 status) noexcept
Definition response.cpp:74
void setBody(QIODevice *body)
Definition response.cpp:105
Headers & headers() noexcept
QList< QNetworkCookie > cookies() const
Definition response.cpp:196
QIODevice * bodyDevice() const noexcept
Definition response.cpp:99
QByteArray & body()
Definition response.cpp:87
quint16 status() const noexcept
Definition response.cpp:68
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