Cutelyst  1.11.0
enginerequest.cpp
1 /*
2  * Copyright (C) 2017 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")
27 
28 using namespace Cutelyst;
29 
30 EngineRequest::EngineRequest(Engine *_engine) : engine(_engine)
31 {
32 
33 }
34 
35 EngineRequest::~EngineRequest()
36 {
37  delete body;
38 }
39 
41 {
42  Response *response = c->response();
43 
44  if (!(status & EngineRequest::Chunked)) {
45  QIODevice *body = response->bodyDevice();
46 
47  if (body) {
48  body->seek(0);
49  char block[64 * 1024];
50  while (!body->atEnd()) {
51  qint64 in = body->read(block, sizeof(block));
52  if (in <= 0) {
53  break;
54  }
55 
56  if (write(block, in) != in) {
57  qCWarning(CUTELYST_ENGINEREQUEST) << "Failed to write body";
58  break;
59  }
60  }
61  } else {
62  const QByteArray bodyByteArray = response->body();
63  write(bodyByteArray.constData(), bodyByteArray.size());
64  }
65  } else if (!(status & EngineRequest::ChunkedDone)) {
66  // Write the final '0' chunk
67  doWrite("0\r\n\r\n", 5);
68  }
69 }
70 
72 {
73  Response *res = c->response();
74 
75  res->setContentType(QStringLiteral("text/html; charset=utf-8"));
76 
77  QByteArray body;
78 
79  // Trick IE. Old versions of IE would display their own error page instead
80  // of ours if we'd give it less than 512 bytes.
81  body.reserve(512);
82 
83  body.append(c->errors().join(QLatin1Char('\n')).toUtf8());
84 
85  res->setBody(body);
86 
87  // Return 500
88  res->setStatus(Response::InternalServerError);
89 }
90 
92 {
93  if (c->error()) {
94  finalizeError(c);
95  }
96 
97  if (!(status & EngineRequest::FinalizedHeaders) && !finalizeHeaders(c)) {
98  return;
99  }
100 
101  finalizeBody(c);
102 }
103 
105 {
106  Response *res = c->response();
107  Headers &headers = res->headers();
108  const auto cookies = res->cookies();
109  for (const QNetworkCookie &cookie : cookies) {
110  headers.pushHeader(QStringLiteral("set_cookie"), QString::fromLatin1(cookie.toRawForm()));
111  }
112 }
113 
115 {
116  Response *response = c->response();
117  Headers &headers = response->headers();
118 
119  // Fix missing content length
120  if (headers.contentLength() < 0) {
121  qint64 size = response->size();
122  if (size >= 0) {
123  headers.setContentLength(size);
124  }
125  }
126 
127  finalizeCookies(c);
128 
129  // Done
130  status |= EngineRequest::FinalizedHeaders;
131  return writeHeaders(response->status(), headers);
132 }
133 
134 qint64 EngineRequest::write(const char *data, qint64 len)
135 {
136  if (!(status & EngineRequest::Chunked)) {
137  return doWrite(data, len);
138  } else if (!(status & EngineRequest::ChunkedDone)) {
139  const QByteArray chunkSize = QByteArray::number(len, 16).toUpper();
140  QByteArray chunk;
141  chunk.reserve(len + chunkSize.size() + 4);
142  chunk.append(chunkSize).append("\r\n", 2)
143  .append(data, len).append("\r\n", 2);
144 
145  qint64 retWrite = doWrite(chunk.data(), chunk.size());
146 
147  // Flag if we wrote an empty chunk
148  if (!len) {
149  status |= EngineRequest::ChunkedDone;
150  }
151 
152  return retWrite == chunk.size() ? len : -1;
153  }
154  return -1;
155 }
156 
157 bool EngineRequest::webSocketHandshake(Context *c, const QString &key, const QString &origin, const QString &protocol)
158 {
159  if (status & EngineRequest::FinalizedHeaders) {
160  return false;
161  }
162 
163  if (webSocketHandshakeDo(c, key, origin, protocol)) {
164  status |= EngineRequest::FinalizedHeaders;
165  return true;
166  }
167 
168  return false;
169 }
170 
171 bool EngineRequest::webSocketSendTextMessage(const QString &message)
172 {
173  Q_UNUSED(message)
174  return false;
175 }
176 
177 bool EngineRequest::webSocketSendBinaryMessage(const QByteArray &message)
178 {
179  Q_UNUSED(message)
180  return false;
181 }
182 
183 bool EngineRequest::webSocketSendPing(const QByteArray &payload)
184 {
185  Q_UNUSED(payload)
186  return false;
187 }
188 
189 bool EngineRequest::webSocketClose(quint16 code, const QString &reason)
190 {
191  Q_UNUSED(code)
192  Q_UNUSED(reason)
193  return false;
194 }
195 
196 bool EngineRequest::webSocketHandshakeDo(Context *c, const QString &key, const QString &origin, const QString &protocol)
197 {
198  Q_UNUSED(c)
199  Q_UNUSED(key)
200  Q_UNUSED(origin)
201  Q_UNUSED(protocol)
202  return false;
203 }
quint16 status() const
Definition: response.cpp:84
void pushHeader(const QString &field, const QString &value)
Definition: headers.cpp:368
void setContentType(const QString &type)
Definition: response.h:205
QString protocol
The protocol requested by the user agent &#39;HTTP1/1&#39;.
qint64 contentLength() const
Definition: headers.cpp:157
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:308
void setStatus(quint16 status)
Definition: response.cpp:90
bool error() const
Returns true if an error was set.
Definition: context.cpp:64
QList< QNetworkCookie > cookies() const
Definition: response.cpp:212
virtual void finalizeBody(Context *c)
Engines must reimplement this to write the response body back to the caller.
The Cutelyst Context.
Definition: context.h:50
virtual qint64 doWrite(const char *data, qint64 len)=0
Reimplement this to do the RAW writing to the client.
Headers & headers()
Definition: response.cpp:297
Headers headers
The request headers.
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
virtual bool finalizeHeaders(Context *c)
Finalize the headers, and call doWriteHeader(), reimplemententions must call this first...
void finalize(Context *c)
Called by Application to deal with finalizing cookies, headers and body.
QStringList errors() const
Returns a list of errors that were defined.
Definition: context.cpp:81
Response * response() const
Definition: context.cpp:111
virtual void finalizeError(Context *c)
Engines should overwrite this if they want to to make custom error messages.
Q_REQUIRED_RESULT QByteArray & body()
Definition: response.cpp:102
Status status
Connection status.
QIODevice * bodyDevice() const
Definition: response.cpp:113
virtual void finalizeCookies(Context *c)
Reimplement if you need a custom way to Set-Cookie, the default implementation writes them to c->res(...
void setBody(QIODevice *body)
Definition: response.cpp:119
void setContentLength(qint64 value)
Definition: headers.cpp:166
virtual bool writeHeaders(quint16 status, const Headers &headers)=0
Reimplement this to write the headers back to the client.
The Cutelyst Engine.
Definition: engine.h:33