Cutelyst  2.13.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 
104 }
105 
107 {
108  Response *res = context->response();
109  Headers &headers = res->headers();
110  const auto cookies = res->cookies();
111  for (const QNetworkCookie &cookie : cookies) {
112  headers.pushHeader(QStringLiteral("SET_COOKIE"), QString::fromLatin1(cookie.toRawForm()));
113  }
114 }
115 
117 {
118  Response *response = context->response();
119  Headers &headers = response->headers();
120 
121  // Fix missing content length
122  if (headers.contentLength() < 0) {
123  qint64 size = response->size();
124  if (size >= 0) {
126  }
127  }
128 
129  finalizeCookies();
130 
131  // Done
132  status |= EngineRequest::FinalizedHeaders;
133  return writeHeaders(response->status(), headers);
134 }
135 
136 qint64 EngineRequest::write(const char *data, qint64 len)
137 {
138  if (!(status & EngineRequest::Chunked)) {
139  return doWrite(data, len);
140  } else if (!(status & EngineRequest::ChunkedDone)) {
141  const QByteArray chunkSize = QByteArray::number(len, 16).toUpper();
142  QByteArray chunk;
143  chunk.reserve(int(len + chunkSize.size() + 4));
144  chunk.append(chunkSize).append("\r\n", 2)
145  .append(data, int(len)).append("\r\n", 2);
146 
147  qint64 retWrite = doWrite(chunk.data(), chunk.size());
148 
149  // Flag if we wrote an empty chunk
150  if (!len) {
151  status |= EngineRequest::ChunkedDone;
152  }
153 
154  return retWrite == chunk.size() ? len : -1;
155  }
156  return -1;
157 }
158 
159 bool EngineRequest::webSocketHandshake(const QString &key, const QString &origin, const QString &protocol)
160 {
161  if (status & EngineRequest::FinalizedHeaders) {
162  return false;
163  }
164 
165  if (webSocketHandshakeDo(key, origin, protocol)) {
166  status |= EngineRequest::FinalizedHeaders | EngineRequest::Async | EngineRequest::IOWrite;
167  return true;
168  }
169 
170  return false;
171 }
172 
173 bool EngineRequest::webSocketSendTextMessage(const QString &message)
174 {
175  Q_UNUSED(message)
176  return false;
177 }
178 
179 bool EngineRequest::webSocketSendBinaryMessage(const QByteArray &message)
180 {
181  Q_UNUSED(message)
182  return false;
183 }
184 
185 bool EngineRequest::webSocketSendPing(const QByteArray &payload)
186 {
187  Q_UNUSED(payload)
188  return false;
189 }
190 
191 bool EngineRequest::webSocketClose(quint16 code, const QString &reason)
192 {
193  Q_UNUSED(code)
194  Q_UNUSED(reason)
195  return false;
196 }
197 
199 {
200 }
201 
202 bool EngineRequest::webSocketHandshakeDo(const QString &key, const QString &origin, const QString &protocol)
203 {
204  Q_UNUSED(key)
205  Q_UNUSED(origin)
206  Q_UNUSED(protocol)
207  return false;
208 }
209 
210 void EngineRequest::setPath(char *rawPath, const int len)
211 {
212  if (len == 0) {
213  path = QString();
214  return;
215  }
216 
217  char *data = rawPath;
218  const char *inputPtr = data;
219 
220  bool skipUtf8 = true;
221  int outlen = 0;
222  for (int i = 0; i < len; ++i, ++outlen) {
223  const char c = inputPtr[i];
224  if (c == '%' && i + 2 < len) {
225  int a = inputPtr[++i];
226  int b = inputPtr[++i];
227 
228  if (a >= '0' && a <= '9') a -= '0';
229  else if (a >= 'a' && a <= 'f') a = a - 'a' + 10;
230  else if (a >= 'A' && a <= 'F') a = a - 'A' + 10;
231 
232  if (b >= '0' && b <= '9') b -= '0';
233  else if (b >= 'a' && b <= 'f') b = b - 'a' + 10;
234  else if (b >= 'A' && b <= 'F') b = b - 'A' + 10;
235 
236  *data++ = char((a << 4) | b);
237  skipUtf8 = false;
238  } else if (c == '+') {
239  *data++ = ' ';
240  } else {
241  *data++ = c;
242  }
243  }
244 
245  if (skipUtf8) {
246  path = QString::fromLatin1(rawPath, outlen);
247  } else {
248  path = QString::fromUtf8(rawPath, outlen);
249  }
250 }
251 
252 #include "moc_enginerequest.cpp"
Cutelyst::EngineRequest::status
Status status
Connection status.
Definition: enginerequest.h:167
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:161
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:116
Cutelyst::Response::setBody
void setBody(QIODevice *body)
Definition: response.cpp:114
Cutelyst::EngineRequest::path
QString path
Call setPath() instead.
Definition: enginerequest.h:142
Cutelyst::EngineRequest::protocol
QString protocol
The protocol requested by the user agent 'HTTP1/1'.
Definition: enginerequest.h:148
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:106
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:198
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
Cutelyst::EngineRequest::context
Context * context
The Cutelyst::Context of this request.
Definition: enginerequest.h:175
Cutelyst::EngineRequest::body
QIODevice * body
The QIODevice containing the body (if any) of the request.
Definition: enginerequest.h:171
Cutelyst::EngineRequest::write
qint64 write(const char *data, qint64 len)
Called by Response to manually write data.
Definition: enginerequest.cpp:136
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:210
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