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) {
112 headers.setContentLength(size);
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"
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
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
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)