Cutelyst  2.12.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"
void pushHeader(const QString &field, const QString &value)
Definition: headers.cpp:413
QString path
Call setPath() instead.
void setContentType(const QString &type)
Definition: response.h:218
QString protocol
The protocol requested by the user agent 'HTTP1/1'.
quint16 status() const
Definition: response.cpp:79
QIODevice * body
The QIODevice containing the body (if any) of the request.
qint64 write(const char *data, qint64 len)
Called by Response to manually write data.
virtual qint64 size() const override
Definition: response.cpp:333
QString join(const QString &sep) const const
void setStatus(quint16 status)
Definition: response.cpp:85
virtual void finalizeError()
Engines should overwrite this if they want to to make custom error messages.
virtual qint64 doWrite(const char *data, qint64 len)=0
Reimplement this to do the RAW writing to the client.
QList< QNetworkCookie > cookies() const
Definition: response.cpp:222
QStringList errors() const
Returns a list of errors that were defined.
Definition: context.cpp:80
Context * context
The Cutelyst::Context of this request.
Headers & headers()
Definition: response.cpp:316
Response * response() const
Definition: context.cpp:110
Headers headers
The request headers.
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
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.
virtual bool finalizeHeaders()
Finalize the headers, and call doWriteHeader(), reimplemententions must call this first.
qint64 contentLength() const
Definition: headers.cpp:172
Q_REQUIRED_RESULT QByteArray & body()
Definition: response.cpp:97
Status status
Connection status.
void setBody(QIODevice *body)
Definition: response.cpp:114
virtual void finalizeBody()
Engines must reimplement this to write the response body back to the caller.
void setContentLength(qint64 value)
Definition: headers.cpp:181
virtual bool writeHeaders(quint16 status, const Headers &headers)=0
Reimplement this to write the headers back to the client.
virtual void finalizeCookies()
Reimplement if you need a custom way to Set-Cookie, the default implementation writes them to c->res(...
void finalize()
Called by Application to deal with finalizing cookies, headers and body.
virtual void processingFinished()
This is called when the Application chain is finished processing this request, here the request can s...
bool error() const
Returns true if an error was set.
Definition: context.cpp:63
QIODevice * bodyDevice() const
Definition: response.cpp:108