cutelyst 3.9.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
response.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#include "common.h"
6#include "context_p.h"
7#include "engine.h"
8#include "enginerequest.h"
9#include "response_p.h"
10
11#include <QCryptographicHash>
12#include <QEventLoop>
13#include <QtCore/QJsonDocument>
14
15using namespace Cutelyst;
16
17Response::Response(const Headers &defaultHeaders, EngineRequest *engineRequest)
18 : d_ptr(new ResponsePrivate(defaultHeaders, engineRequest))
19{
21}
22
23qint64 Response::readData(char *data, qint64 maxlen)
24{
25 Q_UNUSED(data)
26 Q_UNUSED(maxlen)
27 return -1;
28}
29
30qint64 Response::writeData(const char *data, qint64 len)
31{
32 Q_D(Response);
33
34 if (len <= 0) {
35 return len;
36 }
37
38 // Finalize headers if someone manually writes output
39 if (!(d->engineRequest->status & EngineRequest::FinalizedHeaders)) {
40 if (d->headers.header(QStringLiteral("TRANSFER_ENCODING")).compare(u"chunked") == 0) {
41 d->engineRequest->status |= EngineRequest::IOWrite | EngineRequest::Chunked;
42 } else {
43 // When chunked encoding is not set the client can only know
44 // that data is finished if we close the connection
45 d->headers.setHeader(QStringLiteral("CONNECTION"), QStringLiteral("close"));
46 d->engineRequest->status |= EngineRequest::IOWrite;
47 }
48 delete d->bodyIODevice;
49 d->bodyIODevice = nullptr;
50 d->bodyData = QByteArray();
51
52 d->engineRequest->finalizeHeaders();
53 }
54
55 return d->engineRequest->write(data, len);
56}
57
58Response::~Response()
59{
60 delete d_ptr->bodyIODevice;
61 delete d_ptr;
62}
63
64quint16 Response::status() const noexcept
65{
66 Q_D(const Response);
67 return d->status;
68}
69
70void Response::setStatus(quint16 status) noexcept
71{
72 Q_D(Response);
73 d->status = status;
74}
75
76bool Response::hasBody() const noexcept
77{
78 Q_D(const Response);
79 return !d->bodyData.isEmpty() || d->bodyIODevice ||
80 d->engineRequest->status & EngineRequest::IOWrite;
81}
82
84{
85 Q_D(Response);
86 if (d->bodyIODevice) {
87 delete d->bodyIODevice;
88 d->bodyIODevice = nullptr;
89 }
90
91 return d->bodyData;
92}
93
95{
96 Q_D(const Response);
97 return d->bodyIODevice;
98}
99
101{
102 Q_D(Response);
103 Q_ASSERT(body && body->isOpen() && body->isReadable());
104
105 if (!(d->engineRequest->status & EngineRequest::IOWrite)) {
106 d->bodyData = QByteArray();
107 if (d->bodyIODevice) {
108 delete d->bodyIODevice;
109 }
110 d->bodyIODevice = body;
111 }
112}
113
115{
116 Q_D(Response);
117 d->setBodyData(body);
118}
119
121{
122 Q_D(Response);
123 const QByteArray body = documment.toJson(QJsonDocument::Compact);
124 d->setBodyData(body);
125 d->headers.setContentType(QStringLiteral("application/json"));
126}
127
129{
130 Q_D(Response);
131 d->setBodyData(json.toUtf8());
132 d->headers.setContentType(QStringLiteral("application/json"));
133}
134
136{
137 Q_D(Response);
138 d->setBodyData(json);
139 d->headers.setContentType(QStringLiteral("application/json"));
140}
141
143{
144 Q_D(Response);
146 d->setBodyData(body);
147 d->headers.setContentType(QStringLiteral("application/json"));
148}
149
151{
152 Q_D(Response);
154 d->setBodyData(body);
155 d->headers.setContentType(QStringLiteral("application/json"));
156}
157
159{
160 Q_D(const Response);
161 return d->headers.contentEncoding();
162}
163
165{
166 Q_D(Response);
167 Q_ASSERT_X(!(d->engineRequest->status & EngineRequest::FinalizedHeaders),
168 "setContentEncoding",
169 "setting a header value after finalize_headers and the response callback has been "
170 "called. Not what you want.");
171
172 d->headers.setContentEncoding(encoding);
173}
174
176{
177 Q_D(const Response);
178 return d->headers.contentLength();
179}
180
181void Response::setContentLength(qint64 length)
182{
183 Q_D(Response);
184 Q_ASSERT_X(!(d->engineRequest->status & EngineRequest::FinalizedHeaders),
185 "setContentLength",
186 "setting a header value after finalize_headers and the response callback has been "
187 "called. Not what you want.");
188
189 d->headers.setContentLength(length);
190}
191
193{
194 Q_D(const Response);
195 return d->headers.contentType();
196}
197
199{
200 Q_D(const Response);
201 return d->headers.contentTypeCharset();
202}
203
205{
206 Q_D(const Response);
207 return QVariant::fromValue(d->cookies.value(name));
208}
209
210#if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
211QVariant Response::cuteCookie(const QByteArray &name) const
212{
213 Q_D(const Response);
214 return QVariant::fromValue(d->cuteCookies.value(name));
215}
216#endif
217
219{
220 Q_D(const Response);
221 return d->cookies.values();
222}
223
224#if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
225QList<Cookie> Response::cuteCookies() const
226{
227 Q_D(const Response);
228 return d->cuteCookies.values();
229}
230#endif
231
233{
234 Q_D(Response);
235 d->cookies.insert(cookie.name(), cookie);
236}
237
238#if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
239void Response::setCuteCookie(const Cookie &cookie)
240{
241 Q_D(Response);
242 d->cuteCookies.insert(cookie.name(), cookie);
243}
244#endif
245
247{
248 Q_D(Response);
249 for (const QNetworkCookie &cookie : cookies) {
250 d->cookies.insert(cookie.name(), cookie);
251 }
252}
253
254#if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
255void Response::setCuteCookies(const QList<Cookie> &cookies)
256{
257 Q_D(Response);
258 for (const Cookie &cookie : cookies) {
259 d->cuteCookies.insert(cookie.name(), cookie);
260 }
261}
262#endif
263
265{
266 Q_D(Response);
267 return d->cookies.remove(name);
268}
269
270#if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
271int Response::removeCuteCookies(const QByteArray &name)
272{
273 Q_D(Response);
274 return d->cuteCookies.remove(name);
275}
276#endif
277
278void Response::redirect(const QUrl &url, quint16 status)
279{
280 Q_D(Response);
281 d->location = url;
282 d->status = status;
283
284 if (url.isValid()) {
286 qCDebug(CUTELYST_RESPONSE) << "Redirecting to" << location << status;
287
288 d->headers.setHeader(QStringLiteral("LOCATION"), location);
289 d->headers.setContentType(QStringLiteral("text/html; charset=utf-8"));
290
291 const QString buf = QLatin1String(R"V0G0N(<!DOCTYPE html>
292<html xmlns="http://www.w3.org/1999/xhtml">
293 <head>
294 <title>Moved</title>
295 </head>
296 <body>
297 <p>This item has moved <a href=")V0G0N") +
298 location + QLatin1String(R"V0G0N(">here</a>.</p>
299 </body>
300</html>
301)V0G0N");
302 setBody(buf.toLatin1());
303 } else {
304 d->headers.removeHeader(QStringLiteral("LOCATION"));
305 qCDebug(CUTELYST_ENGINE) << "Invalid redirect removing header" << url << status;
306 }
307}
308
309void Response::redirect(const QString &url, quint16 status)
310{
311 redirect(QUrl(url), status);
312}
313
314void Response::redirectSafe(const QUrl &url, const QUrl &fallback)
315{
316 Q_D(const Response);
317 if (url.matches(d->engineRequest->context->req()->uri(),
319 redirect(url);
320 } else {
321 redirect(fallback);
323}
324
325QUrl Response::location() const noexcept
326{
327 Q_D(const Response);
328 return d->location;
329}
330
331QString Response::header(const QString &field) const
332{
333 Q_D(const Response);
334 return d->headers.header(field);
335}
336
337void Response::setHeader(const QString &field, const QString &value)
338{
340 Q_ASSERT_X(!(d->engineRequest->status & EngineRequest::FinalizedHeaders),
341 "setHeader",
342 "setting a header value after finalize_headers and the response callback has been "
343 "called. Not what you want.");
344
345 d->headers.setHeader(field, value);
346}
347
348Headers &Response::headers() noexcept
349{
350 Q_D(Response);
351 return d->headers;
352}
353
354bool Response::isFinalizedHeaders() const noexcept
355{
356 Q_D(const Response);
357 return d->engineRequest->status & EngineRequest::FinalizedHeaders;
358}
359
360bool Response::isSequential() const noexcept
361{
362 return true;
363}
364
365qint64 Response::size() const noexcept
366{
367 Q_D(const Response);
368 if (d->engineRequest->status & EngineRequest::IOWrite) {
369 return -1;
370 } else if (d->bodyIODevice) {
371 return d->bodyIODevice->size();
372 } else {
373 return d->bodyData.size();
374 }
375}
378 const QString &origin,
379 const QString &protocol)
380{
381 Q_D(Response);
382 return d->engineRequest->webSocketHandshake(key, origin, protocol);
383}
384
385bool Response::webSocketTextMessage(const QString &message)
386{
387 Q_D(Response);
388 return d->engineRequest->webSocketSendTextMessage(message);
389}
390
392{
393 Q_D(Response);
394 return d->engineRequest->webSocketSendBinaryMessage(message);
395}
396
397bool Response::webSocketPing(const QByteArray &payload)
398{
399 Q_D(Response);
400 return d->engineRequest->webSocketSendPing(payload);
401}
402
403bool Response::webSocketClose(quint16 code, const QString &reason)
404{
405 Q_D(Response);
406 return d->engineRequest->webSocketClose(code, reason);
407}
408
409void ResponsePrivate::setBodyData(const QByteArray &body)
410{
411 if (!(engineRequest->status & EngineRequest::IOWrite)) {
412 if (bodyIODevice) {
413 delete bodyIODevice;
414 bodyIODevice = nullptr;
415 }
416 bodyData = body;
417 headers.setContentLength(body.size());
418 }
419}
420
421#include "moc_response.cpp"
The Cutelyst Cookie.
Definition cookie.h:29
void redirect(const QUrl &url, quint16 status=Found)
Definition response.cpp:278
Headers & headers() noexcept
Definition response.cpp:339
QVariant cookie(const QByteArray &name) const
Definition response.cpp:204
QUrl location() const noexcept
Definition response.cpp:316
qint64 contentLength() const
Definition response.cpp:175
bool hasBody() const noexcept
Definition response.cpp:76
void setStatus(quint16 status) noexcept
Definition response.cpp:70
Response(const Headers &defaultHeaders, EngineRequest *conn=nullptr)
Definition response.cpp:17
void redirectSafe(const QUrl &url, const QUrl &fallback)
Definition response.cpp:305
void setBody(QIODevice *body)
Definition response.cpp:100
QIODevice * bodyDevice() const
Definition response.cpp:94
QString contentEncoding() const
Definition response.cpp:158
void setJsonArrayBody(const QJsonArray &array)
Definition response.cpp:150
void setCookies(const QList< QNetworkCookie > &cookies)
Definition response.cpp:246
virtual qint64 writeData(const char *data, qint64 len) override
Definition response.cpp:30
bool webSocketHandshake(const QString &key={}, const QString &origin={}, const QString &protocol={})
Sends the websocket handshake, if no parameters are defined it will use header data.
Definition response.cpp:368
virtual bool isSequential() const noexcept override
Definition response.cpp:351
bool webSocketClose(quint16 code=Response::CloseCodeNormal, const QString &reason={})
Sends a WebSocket close frame, with both optional close code and a string reason.
Definition response.cpp:394
QList< QNetworkCookie > cookies() const
Definition response.cpp:218
QString header(const QString &field) const
Definition response.cpp:322
QString contentType() const
Definition response.cpp:192
bool webSocketPing(const QByteArray &payload={})
Sends a WebSocket ping with an optional payload limited to 125 bytes, which will be truncated if larg...
Definition response.cpp:388
QString contentTypeCharset() const
Definition response.cpp:198
void setContentLength(qint64 length)
Definition response.cpp:181
int removeCookies(const QByteArray &name)
Definition response.cpp:264
void setContentEncoding(const QString &encoding)
Definition response.cpp:164
void setJsonObjectBody(const QJsonObject &object)
Definition response.cpp:142
bool webSocketBinaryMessage(const QByteArray &message)
Sends a WebSocket binary message.
Definition response.cpp:382
virtual qint64 readData(char *data, qint64 maxlen) override
Definition response.cpp:23
void setJsonBody(const QJsonDocument &documment)
Definition response.cpp:120
Q_REQUIRED_RESULT QByteArray & body()
Definition response.cpp:83
virtual qint64 size() const noexcept override
Definition response.cpp:356
bool webSocketTextMessage(const QString &message)
Sends a WebSocket text message.
Definition response.cpp:376
void setHeader(const QString &field, const QString &value)
Definition response.cpp:328
quint16 status() const noexcept
Definition response.cpp:64
void setCookie(const QNetworkCookie &cookie)
Definition response.cpp:232
bool isFinalizedHeaders() const noexcept
Definition response.cpp:345
The Cutelyst namespace holds all public Cutelyst API.
Definition Mainpage.dox:8
int size() const const
virtual bool open(OpenMode mode)
QByteArray toJson() const const
QString fromLatin1(const char *str, int size)
QByteArray toUtf8() const const
FullyEncoded
bool isValid() const const
bool matches(const QUrl &url, FormattingOptions options) const const
QByteArray toEncoded(FormattingOptions options) const const
QVariant fromValue(const T &value)