cutelyst 3.9.1
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(QStringLiteral("text/html; charset=utf-8"));
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(QStringLiteral("SET_COOKIE"), QString::fromLatin1(cookie.toRawForm()));
100 }
101#if (QT_VERSION < QT_VERSION_CHECK(6, 1, 0))
102 const auto cuteCookies = res->cuteCookies();
103 for (const Cookie &cookie : cuteCookies) {
104 headers.pushHeader(QStringLiteral("SET_COOKIE"), QString::fromLatin1(cookie.toRawForm()));
105 }
106#endif
107}
108
110{
111 Response *response = context->response();
112 Headers &headers = response->headers();
113
114 // Fix missing content length
115 if (headers.contentLength() < 0) {
116 qint64 size = response->size();
117 if (size >= 0) {
118 headers.setContentLength(size);
119 }
120 }
121
123
124 // Done
125 status |= EngineRequest::FinalizedHeaders;
126 return writeHeaders(response->status(), headers);
127}
128
129qint64 EngineRequest::write(const char *data, qint64 len)
130{
131 if (!(status & EngineRequest::Chunked)) {
132 return doWrite(data, len);
133 } else if (!(status & EngineRequest::ChunkedDone)) {
134 const QByteArray chunkSize = QByteArray::number(len, 16).toUpper();
135 QByteArray chunk;
136 chunk.reserve(int(len + chunkSize.size() + 4));
137 chunk.append(chunkSize).append("\r\n", 2).append(data, int(len)).append("\r\n", 2);
138
139 qint64 retWrite = doWrite(chunk.data(), chunk.size());
140
141 // Flag if we wrote an empty chunk
142 if (!len) {
143 status |= EngineRequest::ChunkedDone;
144 }
145
146 return retWrite == chunk.size() ? len : -1;
147 }
148 return -1;
149}
150
151bool EngineRequest::webSocketHandshake(const QString &key,
152 const QString &origin,
153 const QString &protocol)
154{
155 if (status & EngineRequest::FinalizedHeaders) {
156 return false;
157 }
158
159 if (webSocketHandshakeDo(key, origin, protocol)) {
160 status |= EngineRequest::FinalizedHeaders | EngineRequest::Async | EngineRequest::IOWrite;
161
162 context->finalize();
163
164 return true;
165 }
166
167 return false;
168}
169
170bool EngineRequest::webSocketSendTextMessage(const QString &message)
171{
172 Q_UNUSED(message)
173 return false;
174}
175
176bool EngineRequest::webSocketSendBinaryMessage(const QByteArray &message)
177{
178 Q_UNUSED(message)
179 return false;
180}
181
182bool EngineRequest::webSocketSendPing(const QByteArray &payload)
183{
184 Q_UNUSED(payload)
185 return false;
186}
187
188bool EngineRequest::webSocketClose(quint16 code, const QString &reason)
189{
190 Q_UNUSED(code)
191 Q_UNUSED(reason)
192 return false;
193}
194
198
199bool EngineRequest::webSocketHandshakeDo(const QString &key,
200 const QString &origin,
201 const QString &protocol)
202{
203 Q_UNUSED(key)
204 Q_UNUSED(origin)
205 Q_UNUSED(protocol)
206 return false;
207}
208
209void EngineRequest::setPath(char *rawPath, const int len)
210{
211 if (len == 0) {
212 path = QString();
213 return;
214 }
215
216 char *data = rawPath;
217 const char *inputPtr = data;
218
219 bool skipUtf8 = true;
220 int outlen = 0;
221 for (int i = 0; i < len; ++i, ++outlen) {
222 const char c = inputPtr[i];
223 if (c == '%' && i + 2 < len) {
224 int a = inputPtr[++i];
225 int b = inputPtr[++i];
226
227 if (a >= '0' && a <= '9')
228 a -= '0';
229 else if (a >= 'a' && a <= 'f')
230 a = a - 'a' + 10;
231 else if (a >= 'A' && a <= 'F')
232 a = a - 'A' + 10;
233
234 if (b >= '0' && b <= '9')
235 b -= '0';
236 else if (b >= 'a' && b <= 'f')
237 b = b - 'a' + 10;
238 else if (b >= 'A' && b <= 'F')
239 b = b - 'A' + 10;
240
241 *data++ = char((a << 4) | b);
242 skipUtf8 = false;
243 } else if (c == '+') {
244 *data++ = ' ';
245 } else {
246 *data++ = c;
247 }
248 }
249
250 if (skipUtf8) {
251 path = QString::fromLatin1(rawPath, outlen);
252 } else {
253 path = QString::fromUtf8(rawPath, outlen);
254 }
255}
256
257#include "moc_enginerequest.cpp"
void finalize()
finalize the request right away this is automatically called at the end of the actions chain
Definition context.cpp:499
The Cutelyst Cookie.
Definition cookie.h:29
virtual qint64 doWrite(const char *data, qint64 len)=0
Reimplement this to do the RAW writing to the client.
Context * context
The Cutelyst::Context of this request.
virtual void finalizeBody()
Engines must reimplement this to write the response body back to the caller.
virtual void finalizeError()
Engines should overwrite this if they want to to make custom error messages.
QString path
Call setPath() instead.
Status status
Connection status.
void finalize()
Called by Application to deal with finalizing cookies, headers and body.
qint64 write(const char *data, qint64 len)
Called by Response to manually write data.
void setPath(char *rawPath, const int len)
This method sets the path and already does the decoding so that it is done a single time.
Headers headers
The request headers.
virtual bool writeHeaders(quint16 status, const Headers &headers)=0
Reimplement this to write the headers back to the client.
virtual bool finalizeHeaders()
Finalize the headers, and call doWriteHeader(), reimplemententions must call this first.
virtual void finalizeCookies()
Reimplement if you need a custom way to Set-Cookie, the default implementation writes them to c->res(...
QIODevice * body
The QIODevice containing the body (if any) of the request.
QString protocol
The protocol requested by the user agent 'HTTP1/1'.
virtual void processingFinished()
This is called when the Application chain is finished processing this request, here the request can s...
Headers & headers() noexcept
Definition response.cpp:339
void setStatus(quint16 status) noexcept
Definition response.cpp:70
void setBody(QIODevice *body)
Definition response.cpp:100
QIODevice * bodyDevice() const
Definition response.cpp:94
QList< QNetworkCookie > cookies() const
Definition response.cpp:218
Q_REQUIRED_RESULT QByteArray & body()
Definition response.cpp:83
virtual qint64 size() const noexcept override
Definition response.cpp:356
quint16 status() const noexcept
Definition response.cpp:64
void setContentType(const QString &type)
Definition response.h:220
The Cutelyst namespace holds all public Cutelyst API.
Definition Mainpage.dox:8
QByteArray & append(char ch)
const char * constData() const const
char * data()
QByteArray number(int n, int base)
void reserve(int size)
int size() const const
QByteArray toUpper() const const
QString fromLatin1(const char *str, int size)
QString fromUtf8(const char *str, int size)