Cutelyst  2.14.0
enginerequest.cpp
1 /*
2  * Copyright (C) 2017-2018 Daniel Nicoletti <dantti12@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 #include "enginerequest.h"
19 
20 #include "common.h"
21 
22 #include <Cutelyst/response_p.h>
23 #include <Cutelyst/Context>
24 
25 #include <QLoggingCategory>
26 Q_LOGGING_CATEGORY(CUTELYST_ENGINEREQUEST, "cutelyst.engine_request", QtWarningMsg)
27 
28 using namespace Cutelyst;
29 
30 EngineRequest::EngineRequest()
31 {
32 
33 }
34 
35 EngineRequest::~EngineRequest()
36 {
37  delete context;
38 }
39 
41 {
42  if (!(status & EngineRequest::Chunked)) {
43  Response *response = context->response();
44  QIODevice *body = response->bodyDevice();
45 
46  if (body) {
47  if (!body->isSequential()) {
48  body->seek(0);
49  }
50 
51  char block[64 * 1024];
52  while (!body->atEnd()) {
53  qint64 in = body->read(block, sizeof(block));
54  if (in <= 0) {
55  break;
56  }
57 
58  if (write(block, in) != in) {
59  qCWarning(CUTELYST_ENGINEREQUEST) << "Failed to write body";
60  break;
61  }
62  }
63  } else {
64  const QByteArray bodyByteArray = response->body();
65  write(bodyByteArray.constData(), bodyByteArray.size());
66  }
67  } else if (!(status & EngineRequest::ChunkedDone)) {
68  // Write the final '0' chunk
69  doWrite("0\r\n\r\n", 5);
70  }
71 }
72 
74 {
75  Response *res = context->response();
76 
77  res->setContentType(QStringLiteral("text/html; charset=utf-8"));
78 
79  QByteArray body;
80 
81  // Trick IE. Old versions of IE would display their own error page instead
82  // of ours if we'd give it less than 512 bytes.
83  body.reserve(512);
84 
85  body.append(context->errors().join(QLatin1Char('\n')).toUtf8());
86 
87  res->setBody(body);
88 
89  // Return 500
90  res->setStatus(Response::InternalServerError);
91 }
92 
94 {
95  if (context->error()) {
96  finalizeError();
97  }
98 
99  if ((status & EngineRequest::FinalizedHeaders) || finalizeHeaders()) {
100  finalizeBody();
101  }
102 
103  status |= EngineRequest::Finalized;
105 }
106 
108 {
109  Response *res = context->response();
110  Headers &headers = res->headers();
111  const auto cookies = res->cookies();
112  for (const QNetworkCookie &cookie : cookies) {
113  headers.pushHeader(QStringLiteral("SET_COOKIE"), QString::fromLatin1(cookie.toRawForm()));
114  }
115 }
116 
118 {
119  Response *response = context->response();
120  Headers &headers = response->headers();
121 
122  // Fix missing content length
123  if (headers.contentLength() < 0) {
124  qint64 size = response->size();
125  if (size >= 0) {
127  }
128  }
129 
130  finalizeCookies();
131 
132  // Done
133  status |= EngineRequest::FinalizedHeaders;
134  return writeHeaders(response->status(), headers);
135 }
136 
137 qint64 EngineRequest::write(const char *data, qint64 len)
138 {
139  if (!(status & EngineRequest::Chunked)) {
140  return doWrite(data, len);
141  } else if (!(status & EngineRequest::ChunkedDone)) {
142  const QByteArray chunkSize = QByteArray::number(len, 16).toUpper();
143  QByteArray chunk;
144  chunk.reserve(int(len + chunkSize.size() + 4));
145  chunk.append(chunkSize).append("\r\n", 2)
146  .append(data, int(len)).append("\r\n", 2);
147 
148  qint64 retWrite = doWrite(chunk.data(), chunk.size());
149 
150  // Flag if we wrote an empty chunk
151  if (!len) {
152  status |= EngineRequest::ChunkedDone;
153  }
154 
155  return retWrite == chunk.size() ? len : -1;
156  }
157  return -1;
158 }
159 
160 bool EngineRequest::webSocketHandshake(const QString &key, const QString &origin, const QString &protocol)
161 {
162  if (status & EngineRequest::FinalizedHeaders) {
163  return false;
164  }
165 
166  if (webSocketHandshakeDo(key, origin, protocol)) {
167  status |= EngineRequest::FinalizedHeaders | EngineRequest::Async | EngineRequest::IOWrite;
168 
169  context->finalize();
170 
171  return true;
172  }
173 
174  return false;
175 }
176 
177 bool EngineRequest::webSocketSendTextMessage(const QString &message)
178 {
179  Q_UNUSED(message)
180  return false;
181 }
182 
183 bool EngineRequest::webSocketSendBinaryMessage(const QByteArray &message)
184 {
185  Q_UNUSED(message)
186  return false;
187 }
188 
189 bool EngineRequest::webSocketSendPing(const QByteArray &payload)
190 {
191  Q_UNUSED(payload)
192  return false;
193 }
194 
195 bool EngineRequest::webSocketClose(quint16 code, const QString &reason)
196 {
197  Q_UNUSED(code)
198  Q_UNUSED(reason)
199  return false;
200 }
201 
203 {
204 }
205 
206 bool EngineRequest::webSocketHandshakeDo(const QString &key, const QString &origin, const QString &protocol)
207 {
208  Q_UNUSED(key)
209  Q_UNUSED(origin)
210  Q_UNUSED(protocol)
211  return false;
212 }
213 
214 void EngineRequest::setPath(char *rawPath, const int len)
215 {
216  if (len == 0) {
217  path = QString();
218  return;
219  }
220 
221  char *data = rawPath;
222  const char *inputPtr = data;
223 
224  bool skipUtf8 = true;
225  int outlen = 0;
226  for (int i = 0; i < len; ++i, ++outlen) {
227  const char c = inputPtr[i];
228  if (c == '%' && i + 2 < len) {
229  int a = inputPtr[++i];
230  int b = inputPtr[++i];
231 
232  if (a >= '0' && a <= '9') a -= '0';
233  else if (a >= 'a' && a <= 'f') a = a - 'a' + 10;
234  else if (a >= 'A' && a <= 'F') a = a - 'A' + 10;
235 
236  if (b >= '0' && b <= '9') b -= '0';
237  else if (b >= 'a' && b <= 'f') b = b - 'a' + 10;
238  else if (b >= 'A' && b <= 'F') b = b - 'A' + 10;
239 
240  *data++ = char((a << 4) | b);
241  skipUtf8 = false;
242  } else if (c == '+') {
243  *data++ = ' ';
244  } else {
245  *data++ = c;
246  }
247  }
248 
249  if (skipUtf8) {
250  path = QString::fromLatin1(rawPath, outlen);
251  } else {
252  path = QString::fromUtf8(rawPath, outlen);
253  }
254 }
255 
256 #include "moc_enginerequest.cpp"
Cutelyst::EngineRequest::status
Status status
Connection status.
Definition: enginerequest.h:176
Cutelyst::Response::size
virtual qint64 size() const override
Definition: response.cpp:333
Cutelyst::Response::setStatus
void setStatus(quint16 status)
Definition: response.cpp:85
Cutelyst::Headers::setContentLength
void setContentLength(qint64 value)
Definition: headers.cpp:181
Cutelyst::Response::cookies
QList< QNetworkCookie > cookies() const
Definition: response.cpp:222
Cutelyst::Headers::pushHeader
void pushHeader(const QString &field, const QString &value)
Definition: headers.cpp:413
Cutelyst::Context::response
Response * response() const
Definition: context.cpp:110
Cutelyst::EngineRequest::headers
Headers headers
The request headers.
Definition: enginerequest.h:170
Cutelyst::EngineRequest::finalizeBody
virtual void finalizeBody()
Engines must reimplement this to write the response body back to the caller.
Definition: enginerequest.cpp:40
Cutelyst::Response::setContentType
void setContentType(const QString &type)
Definition: response.h:218
Cutelyst::EngineRequest::finalizeHeaders
virtual bool finalizeHeaders()
Finalize the headers, and call doWriteHeader(), reimplemententions must call this first.
Definition: enginerequest.cpp:117
Cutelyst::Response::setBody
void setBody(QIODevice *body)
Definition: response.cpp:114
Cutelyst::EngineRequest::path
QString path
Call setPath() instead.
Definition: enginerequest.h:151
Cutelyst::EngineRequest::protocol
QString protocol
The protocol requested by the user agent 'HTTP1/1'.
Definition: enginerequest.h:157
Cutelyst::Headers
Definition: headers.h:29
Cutelyst::EngineRequest::writeHeaders
virtual bool writeHeaders(quint16 status, const Headers &headers)=0
Reimplement this to write the headers back to the client.
Cutelyst::EngineRequest::finalizeCookies
virtual void finalizeCookies()
Reimplement if you need a custom way to Set-Cookie, the default implementation writes them to c->res(...
Definition: enginerequest.cpp:107
Cutelyst::EngineRequest::processingFinished
virtual void processingFinished()
This is called when the Application chain is finished processing this request, here the request can s...
Definition: enginerequest.cpp:202
Cutelyst::Context::error
bool error() const
Returns true if an error was set.
Definition: context.cpp:63
Cutelyst::Headers::contentLength
qint64 contentLength() const
Definition: headers.cpp:172
QStringList::join
QString join(const QString &sep) const const
Cutelyst::EngineRequest::context
Context * context
The Cutelyst::Context of this request.
Definition: enginerequest.h:184
Cutelyst::EngineRequest::body
QIODevice * body
The QIODevice containing the body (if any) of the request.
Definition: enginerequest.h:180
Cutelyst::EngineRequest::write
qint64 write(const char *data, qint64 len)
Called by Response to manually write data.
Definition: enginerequest.cpp:137
Cutelyst::Response::bodyDevice
QIODevice * bodyDevice() const
Definition: response.cpp:108
Cutelyst::Context::errors
QStringList errors() const
Returns a list of errors that were defined.
Definition: context.cpp:80
Cutelyst
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
Cutelyst::EngineRequest::setPath
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.
Definition: enginerequest.cpp:214
QLatin1Char
Cutelyst::Context::finalize
void finalize()
finalize the request right away this is automatically called at the end of the actions chain
Definition: context.cpp:505
enginerequest.h
Cutelyst::EngineRequest::doWrite
virtual qint64 doWrite(const char *data, qint64 len)=0
Reimplement this to do the RAW writing to the client.
Cutelyst::Response::status
quint16 status() const
Definition: response.cpp:79
Cutelyst::Response::headers
Headers & headers()
Definition: response.cpp:316
Cutelyst::EngineRequest::finalizeError
virtual void finalizeError()
Engines should overwrite this if they want to to make custom error messages.
Definition: enginerequest.cpp:73
Cutelyst::EngineRequest::finalize
void finalize()
Called by Application to deal with finalizing cookies, headers and body.
Definition: enginerequest.cpp:93
Cutelyst::Response::body
Q_REQUIRED_RESULT QByteArray & body()
Definition: response.cpp:97
Cutelyst::Response
Definition: response.h:34