cutelyst  4.9.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
protocolhttp.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2016-2018 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "protocolhttp.h"
6 
7 #include "protocolhttp2.h"
8 #include "protocolwebsocket.h"
9 #include "server.h"
10 #include "socket.h"
11 
12 #include <Cutelyst/Context>
13 #include <Cutelyst/Headers>
14 #include <Cutelyst/Response>
15 #include <typeinfo>
16 
17 #include <QBuffer>
18 #include <QCoreApplication>
19 #include <QCryptographicHash>
20 #include <QEventLoop>
21 #include <QIODevice>
22 #include <QLoggingCategory>
23 #include <QVariant>
24 
25 using namespace Cutelyst;
26 using namespace Qt::Literals::StringLiterals;
27 
28 QByteArray http11StatusMessage(quint16 status);
29 
30 Q_LOGGING_CATEGORY(C_SERVER_HTTP, "cutelyst.server.http", QtWarningMsg)
31 Q_DECLARE_LOGGING_CATEGORY(C_SERVER_SOCK)
32 
33 ProtocolHttp::ProtocolHttp(Server *wsgi, ProtocolHttp2 *upgradeH2c)
34  : Protocol(wsgi)
35  , m_websocketProto(new ProtocolWebSocket(wsgi))
36  , m_upgradeH2c(upgradeH2c)
37 {
38  usingFrontendProxy = wsgi->usingFrontendProxy();
39 }
40 
41 ProtocolHttp::~ProtocolHttp()
42 {
43  delete m_websocketProto;
44 }
45 
46 Protocol::Type ProtocolHttp::type() const
47 {
48  return Protocol::Type::Http11;
49 }
50 
51 inline int CrLfIndexIn(const char *str, int len, int from)
52 {
53  do {
54  const char *pch = static_cast<const char *>(memchr(str + from, '\r', size_t(len - from)));
55  if (pch != nullptr) {
56  int pos = int(pch - str);
57  if ((pos + 1) < len) {
58  if (*++pch == '\n') {
59  return pos;
60  } else {
61  from = ++pos;
62  continue;
63  }
64  }
65  }
66  break;
67  } while (true);
68 
69  return -1;
70 }
71 
72 void ProtocolHttp::parse(Socket *sock, QIODevice *io) const
73 {
74  // Post buffering
75  auto protoRequest = static_cast<ProtoRequestHttp *>(sock->protoData);
76  if (protoRequest->status & Cutelyst::EngineRequest::Async) {
77  return;
78  }
79 
80  if (protoRequest->connState == ProtoRequestHttp::ContentBody) {
81  qint64 bytesAvailable = io->bytesAvailable();
82  qint64 len;
83  qint64 remaining;
84 
85  QIODevice *body = protoRequest->body;
86  do {
87  remaining = protoRequest->contentLength - body->size();
88  len = io->read(m_postBuffer, qMin(m_postBufferSize, remaining));
89  if (len == -1) {
90  qCWarning(C_SERVER_HTTP)
91  << "error while reading body" << len << protoRequest->headers;
92  sock->connectionClose();
93  return;
94  }
95  bytesAvailable -= len;
96  // qCDebug(C_SERVER_HTTP) << "WRITE body" << protoRequest->contentLength <<
97  // remaining << len << (remaining == len) << io->bytesAvailable();
98  body->write(m_postBuffer, len);
99  } while (bytesAvailable && remaining);
100 
101  if (remaining == len) {
102  processRequest(sock, io);
103  }
104 
105  return;
106  }
107 
108  qint64 len = io->read(protoRequest->buffer + protoRequest->buf_size,
109  m_bufferSize - protoRequest->buf_size);
110  if (len == -1) {
111  qCWarning(C_SERVER_HTTP) << "Failed to read from socket" << io->errorString();
112  return;
113  }
114  protoRequest->buf_size += len;
115 
116  while (protoRequest->last < protoRequest->buf_size) {
117  // qCDebug(C_SERVER_HTTP) << Q_FUNC_INFO << QByteArray(protoRequest->buffer,
118  // protoRequest->buf_size);
119  int ix = CrLfIndexIn(protoRequest->buffer, protoRequest->buf_size, protoRequest->last);
120  if (ix != -1) {
121  qint64 len = ix - protoRequest->beginLine;
122  char *ptr = protoRequest->buffer + protoRequest->beginLine;
123  protoRequest->beginLine = ix + 2;
124  protoRequest->last = protoRequest->beginLine;
125 
126  if (protoRequest->connState == ProtoRequestHttp::MethodLine) {
127  if (useStats && protoRequest->startOfRequest == TimePointSteady{}) {
128  protoRequest->startOfRequest = std::chrono::steady_clock::now();
129  }
130 
131  parseMethod(ptr, ptr + len, sock);
132  protoRequest->connState = ProtoRequestHttp::HeaderLine;
133  protoRequest->contentLength = -1;
134  protoRequest->headers = Cutelyst::Headers();
135  // qCDebug(C_SERVER_HTTP) << "--------" << protoRequest->method <<
136  // protoRequest->path << protoRequest->query <<
137  // protoRequest->protocol;
138 
139  } else if (protoRequest->connState == ProtoRequestHttp::HeaderLine) {
140  if (len) {
141  parseHeader(ptr, ptr + len, sock);
142  } else {
143  if (protoRequest->contentLength > 0) {
144  protoRequest->connState = ProtoRequestHttp::ContentBody;
145  protoRequest->body = createBody(protoRequest->contentLength);
146  if (!protoRequest->body) {
147  qCWarning(C_SERVER_HTTP) << "error while creating body, closing socket";
148  sock->connectionClose();
149  return;
150  }
151 
152  ptr += 2;
153  len =
154  qMin(protoRequest->contentLength,
155  static_cast<qint64>(protoRequest->buf_size - protoRequest->last));
156  // qCDebug(C_SERVER_HTTP) << "WRITE" <<
157  // protoRequest->contentLength << len;
158  if (len) {
159  protoRequest->body->write(ptr, len);
160  }
161  protoRequest->last += len;
162 
163  if (protoRequest->contentLength > len) {
164  // qCDebug(C_SERVER_HTTP) << "WRITE more..."
165  // << protoRequest->contentLength << len;
166  // body is not completed yet
167  if (io->bytesAvailable()) {
168  // since we still have bytes available call this function
169  // so that the body parser reads the rest of available data
170  parse(sock, io);
171  }
172  return;
173  }
174  }
175 
176  if (!processRequest(sock, io)) {
177  break;
178  }
179  }
180  }
181  } else {
182  if (protoRequest->startOfRequest == TimePointSteady{}) {
183  protoRequest->startOfRequest = std::chrono::steady_clock::now();
184  }
185  protoRequest->last = protoRequest->buf_size;
186  }
187  }
188 
189  if (protoRequest->buf_size == m_bufferSize) {
190  // 414 Request-URI Too Long
191  }
192 }
193 
194 ProtocolData *ProtocolHttp::createData(Socket *sock) const
195 {
196  return new ProtoRequestHttp(sock, m_bufferSize);
197 }
198 
199 bool ProtocolHttp::processRequest(Socket *sock, QIODevice *io) const
200 {
201  auto request = static_cast<ProtoRequestHttp *>(sock->protoData);
202  // qCDebug(C_SERVER_HTTP) << "processRequest" << sock->protoData->contentLength;
203  if (request->body) {
204  request->body->seek(0);
205  }
206 
207  // When enabled try to upgrade to H2C
208  if (m_upgradeH2c && m_upgradeH2c->upgradeH2C(sock, io, *request)) {
209  return false;
210  }
211 
212  ++sock->processing;
213  sock->engine->processRequest(request);
214 
215  if (request->websocketUpgraded) {
216  return false; // Must read remaining data
217  }
218 
219  if (request->status & Cutelyst::EngineRequest::Async) {
220  return false; // Need to break now
221  }
222 
223  return true;
224 }
225 
226 void ProtocolHttp::parseMethod(const char *ptr, const char *end, Socket *sock) const
227 {
228  auto protoRequest = static_cast<ProtoRequestHttp *>(sock->protoData);
229  const char *word_boundary = ptr;
230  while (*word_boundary != ' ' && word_boundary < end) {
231  ++word_boundary;
232  }
233  protoRequest->method = QByteArray(ptr, int(word_boundary - ptr));
234 
235  // skip spaces
236  while (*word_boundary == ' ' && word_boundary < end) {
237  ++word_boundary;
238  }
239  ptr = word_boundary;
240 
241  // find path end
242  while (*word_boundary != ' ' && *word_boundary != '?' && word_boundary < end) {
243  ++word_boundary;
244  }
245 
246  // This will change the ptr but will only change less than size
247  protoRequest->setPath(const_cast<char *>(ptr), int(word_boundary - ptr));
248 
249  if (*word_boundary == '?') {
250  ptr = word_boundary + 1;
251  while (*word_boundary != ' ' && word_boundary < end) {
252  ++word_boundary;
253  }
254  protoRequest->query = QByteArray(ptr, int(word_boundary - ptr));
255  } else {
256  protoRequest->query = QByteArray();
257  }
258 
259  // skip spaces
260  while (*word_boundary == ' ' && word_boundary < end) {
261  ++word_boundary;
262  }
263  ptr = word_boundary;
264 
265  while (*word_boundary != ' ' && word_boundary < end) {
266  ++word_boundary;
267  }
268  protoRequest->protocol = QByteArray(ptr, int(word_boundary - ptr));
269 }
270 
271 void ProtocolHttp::parseHeader(const char *ptr, const char *end, Socket *sock) const
272 {
273  auto protoRequest = static_cast<ProtoRequestHttp *>(sock->protoData);
274  const char *word_boundary = ptr;
275  while (*word_boundary != ':' && word_boundary < end) {
276  ++word_boundary;
277  }
278  const auto key = QByteArray(ptr, int(word_boundary - ptr));
279 
280  while ((*word_boundary == ':' || *word_boundary == ' ') && word_boundary < end) {
281  ++word_boundary;
282  }
283  const auto value = QByteArray(word_boundary, int(end - word_boundary));
284 
285  if (protoRequest->headerConnection == ProtoRequestHttp::HeaderConnection::NotSet &&
286  key.compare("Connection", Qt::CaseInsensitive) == 0) {
287  if (value.compare("close", Qt::CaseInsensitive) == 0) {
288  protoRequest->headerConnection = ProtoRequestHttp::HeaderConnection::Close;
289  } else {
290  protoRequest->headerConnection = ProtoRequestHttp::HeaderConnection::Keep;
291  }
292  } else if (protoRequest->contentLength < 0 &&
293  key.compare("Content-Length", Qt::CaseInsensitive) == 0) {
294  bool ok;
295  qint64 cl = value.toLongLong(&ok);
296  if (ok && cl >= 0) {
297  protoRequest->contentLength = cl;
298  }
299  } else if (!protoRequest->headerHost && key.compare("Host", Qt::CaseInsensitive) == 0) {
300  protoRequest->serverAddress = value;
301  protoRequest->headerHost = true;
302  } else if (usingFrontendProxy) {
303  if (!protoRequest->X_Forwarded_For &&
304  (key.compare("X-Forwarded-For", Qt::CaseInsensitive) == 0 ||
305  key.compare("X-Real-Ip", Qt::CaseInsensitive) == 0)) {
306  // configure your reverse-proxy to list only one IP address
307  protoRequest->remoteAddress.setAddress(QString::fromLatin1(value));
308  protoRequest->remotePort = 0; // unknown
309  protoRequest->X_Forwarded_For = true;
310  } else if (!protoRequest->X_Forwarded_Host &&
311  key.compare("X-Forwarded-Host", Qt::CaseInsensitive) == 0) {
312  protoRequest->serverAddress = value;
313  protoRequest->X_Forwarded_Host = true;
314  protoRequest->headerHost = true; // ignore a following Host: header (if any)
315  } else if (!protoRequest->X_Forwarded_Proto &&
316  key.compare("X-Forwarded-Proto", Qt::CaseInsensitive) == 0) {
317  protoRequest->isSecure = (value.compare("https") == 0);
318  protoRequest->X_Forwarded_Proto = true;
319  }
320  }
321  protoRequest->headers.pushHeader(key, value);
322 }
323 
324 ProtoRequestHttp::ProtoRequestHttp(Socket *sock, int bufferSize)
325  : ProtocolData(sock, bufferSize)
326 {
327  isSecure = sock->isSecure;
328 }
329 
330 ProtoRequestHttp::~ProtoRequestHttp()
331 {
332 }
333 
334 void ProtoRequestHttp::setupNewConnection(Socket *sock)
335 {
336  serverAddress = sock->serverAddress;
337  remoteAddress = sock->remoteAddress;
338  remotePort = sock->remotePort;
339 }
340 
341 bool ProtoRequestHttp::writeHeaders(quint16 status, const Cutelyst::Headers &headers)
342 {
343  if (websocketUpgraded && status != Cutelyst::Response::SwitchingProtocols) {
344  qCWarning(C_SERVER_SOCK) << "Trying to write header while on an Websocket context";
345  return false;
346  }
347 
348  QByteArray data = http11StatusMessage(status);
349 
350  const auto headersData = headers.data();
351  ProtoRequestHttp::HeaderConnection fallbackConnection = headerConnection;
352  headerConnection = ProtoRequestHttp::HeaderConnection::NotSet;
353 
354  bool hasDate = false;
355  auto it = headersData.begin();
356  while (it != headersData.end()) {
357  if (headerConnection == ProtoRequestHttp::HeaderConnection::NotSet &&
358  it->key.compare("Connection", Qt::CaseInsensitive) == 0) {
359  if (it->value.compare("close") == 0) {
360  headerConnection = ProtoRequestHttp::HeaderConnection::Close;
361  } else if (it->value.compare("Upgrade") == 0) {
362  headerConnection = ProtoRequestHttp::HeaderConnection::Upgrade;
363  } else {
364  headerConnection = ProtoRequestHttp::HeaderConnection::Keep;
365  }
366  } else if (!hasDate && it->key.compare("Date", Qt::CaseInsensitive) == 0) {
367  hasDate = true;
368  }
369 
370  data.append("\r\n");
371  data.append(it->key);
372  data.append(": ");
373  data.append(it->value);
374 
375  ++it;
376  }
377 
378  if (headerConnection == ProtoRequestHttp::HeaderConnection::NotSet) {
379  if (fallbackConnection == ProtoRequestHttp::HeaderConnection::Keep ||
380  (fallbackConnection != ProtoRequestHttp::HeaderConnection::Close &&
381  protocol.compare("HTTP/1.1") == 0)) {
382  headerConnection = ProtoRequestHttp::HeaderConnection::Keep;
383  data.append("\r\nConnection: keep-alive", 24);
384  } else {
385  headerConnection = ProtoRequestHttp::HeaderConnection::Close;
386  data.append("\r\nConnection: close", 19);
387  }
388  }
389 
390  if (!hasDate) {
391  data.append(static_cast<ServerEngine *>(sock->engine)->lastDate());
392  }
393  data.append("\r\n\r\n", 4);
394 
395  return io->write(data) == data.size();
396 }
397 
398 qint64 ProtoRequestHttp::doWrite(const char *data, qint64 len)
399 {
400  return io->write(data, len);
401 }
402 
404 {
405  if (websocketUpgraded) {
406  // need 2 byte header
407  websocket_need = 2;
408  websocket_phase = ProtoRequestHttp::WebSocketPhaseHeaders;
409  buf_size = 0;
410  return;
411  }
412 
413  if (!sock->requestFinished()) {
414  // disconnected
415  return;
416  }
417 
418  if (headerConnection == ProtoRequestHttp::HeaderConnection::Close) {
419  sock->connectionClose();
420  return;
421  }
422 
423  if (last < buf_size) {
424  // move pipelined request to 0
425  int remaining = buf_size - last;
426  memmove(buffer, buffer + last, size_t(remaining));
427  resetData();
428  buf_size = remaining;
429 
430  if (status & EngineRequest::Async) {
431  sock->proto->parse(sock, io);
432  }
433  } else {
434  resetData();
435  }
436 }
437 
438 bool ProtoRequestHttp::webSocketSendTextMessage(const QString &message)
439 {
440  if (headerConnection != ProtoRequestHttp::HeaderConnection::Upgrade) {
441  qCWarning(C_SERVER_HTTP)
442  << "Not sending websocket text message due connection header not upgraded"
443  << headerConnection << message.size();
444  return false;
445  }
446 
447  const QByteArray rawMessage = message.toUtf8();
448  const QByteArray headers = ProtocolWebSocket::createWebsocketHeader(
449  ProtoRequestHttp::OpCodeText, quint64(rawMessage.size()));
450  return doWrite(headers) == headers.size() && doWrite(rawMessage) == rawMessage.size();
451 }
452 
453 bool ProtoRequestHttp::webSocketSendBinaryMessage(const QByteArray &message)
454 {
455  if (headerConnection != ProtoRequestHttp::HeaderConnection::Upgrade) {
456  qCWarning(C_SERVER_HTTP)
457  << "Not sending websocket binary messagedue connection header not upgraded"
458  << headerConnection << message.size();
459  return false;
460  }
461 
462  const QByteArray headers = ProtocolWebSocket::createWebsocketHeader(
463  ProtoRequestHttp::OpCodeBinary, quint64(message.size()));
464  return doWrite(headers) == headers.size() && doWrite(message) == message.size();
465 }
466 
467 bool ProtoRequestHttp::webSocketSendPing(const QByteArray &payload)
468 {
469  if (headerConnection != ProtoRequestHttp::HeaderConnection::Upgrade) {
470  qCWarning(C_SERVER_HTTP) << "Not sending websocket ping due connection header not upgraded"
471  << headerConnection << payload.size();
472  return false;
473  }
474 
475  const QByteArray rawMessage = payload.left(125);
476  const QByteArray headers = ProtocolWebSocket::createWebsocketHeader(
477  ProtoRequestHttp::OpCodePing, quint64(rawMessage.size()));
478  return doWrite(headers) == headers.size() && doWrite(rawMessage) == rawMessage.size();
479 }
480 
481 bool ProtoRequestHttp::webSocketClose(quint16 code, const QString &reason)
482 {
483  if (headerConnection != ProtoRequestHttp::HeaderConnection::Upgrade) {
484  qCWarning(C_SERVER_HTTP) << "Not sending websocket close due connection header not upgraded"
485  << headerConnection << code << reason;
486  return false;
487  }
488 
489  const QByteArray reply = ProtocolWebSocket::createWebsocketCloseReply(reason, code);
490  bool ret = doWrite(reply) == reply.size();
491  sock->requestFinished();
492  sock->connectionClose();
493  return ret;
494 }
495 
496 void ProtoRequestHttp::socketDisconnected()
497 {
498  if (websocketUpgraded) {
499  if (websocket_finn_opcode != 0x88) {
500  Q_EMIT context->request()->webSocketClosed(1005, QString{});
501  }
502  sock->requestFinished();
503  }
504 }
505 
506 bool ProtoRequestHttp::webSocketHandshakeDo(const QByteArray &key,
507  const QByteArray &origin,
508  const QByteArray &protocol)
509 {
510  if (headerConnection == ProtoRequestHttp::HeaderConnection::Upgrade) {
511  return true;
512  }
513 
514  if (sock->proto->type() != Protocol::Type::Http11) {
515  qCWarning(C_SERVER_SOCK)
516  << "Upgrading a connection to websocket is only supported with the HTTP/1.1 protocol"
517  << typeid(sock->proto).name();
518  return false;
519  }
520 
521  const Cutelyst::Headers requestHeaders = context->request()->headers();
522  Cutelyst::Response *response = context->response();
523  Cutelyst::Headers &headers = response->headers();
524 
525  response->setStatus(Cutelyst::Response::SwitchingProtocols);
526  headers.setHeader("Upgrade"_ba, "WebSocket"_ba);
527  headers.setHeader("Connection"_ba, "Upgrade"_ba);
528  const auto localOrigin = origin.isEmpty() ? requestHeaders.header("Origin") : origin;
529  headers.setHeader("Sec-Websocket-Origin"_ba, localOrigin.isEmpty() ? "*"_ba : localOrigin);
530 
531  if (!protocol.isEmpty()) {
532  headers.setHeader("Sec-Websocket-Protocol"_ba, protocol);
533  } else if (const auto wsProtocol = requestHeaders.header("Sec-Websocket-Protocol");
534  !wsProtocol.isEmpty()) {
535  headers.setHeader("Sec-Websocket-Protocol"_ba, wsProtocol);
536  }
537 
538  const QByteArray localKey = key.isEmpty() ? requestHeaders.header("Sec-Websocket-Key") : key;
539  const QByteArray wsKey = localKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
540  if (wsKey.length() == 36) {
541  qCWarning(C_SERVER_SOCK) << "Missing websocket key";
542  return false;
543  }
544 
545  const QByteArray wsAccept =
547  headers.setHeader("Sec-Websocket-Accept"_ba, wsAccept);
548 
549  headerConnection = ProtoRequestHttp::HeaderConnection::Upgrade;
550  websocketUpgraded = true;
551  auto httpProto = static_cast<ProtocolHttp *>(sock->proto);
552  sock->proto = httpProto->m_websocketProto;
553 
554  return writeHeaders(Cutelyst::Response::SwitchingProtocols, headers);
555 }
556 
557 QByteArray http11StatusMessage(quint16 status)
558 {
559  QByteArray ret;
560  switch (status) {
561  case Response::OK:
562  ret = QByteArrayLiteral("HTTP/1.1 200 OK");
563  break;
564  case Response::Found:
565  ret = QByteArrayLiteral("HTTP/1.1 302 Found");
566  break;
567  case Response::NotFound:
568  ret = QByteArrayLiteral("HTTP/1.1 404 Not Found");
569  break;
570  case Response::InternalServerError:
571  ret = QByteArrayLiteral("HTTP/1.1 500 Internal Server Error");
572  break;
573  case Response::MovedPermanently:
574  ret = QByteArrayLiteral("HTTP/1.1 301 Moved Permanently");
575  break;
576  case Response::NotModified:
577  ret = QByteArrayLiteral("HTTP/1.1 304 Not Modified");
578  break;
579  case Response::SeeOther:
580  ret = QByteArrayLiteral("HTTP/1.1 303 See Other");
581  break;
582  case Response::Forbidden:
583  ret = QByteArrayLiteral("HTTP/1.1 403 Forbidden");
584  break;
585  case Response::TemporaryRedirect:
586  ret = QByteArrayLiteral("HTTP/1.1 307 Temporary Redirect");
587  break;
588  case Response::Unauthorized:
589  ret = QByteArrayLiteral("HTTP/1.1 401 Unauthorized");
590  break;
591  case Response::BadRequest:
592  ret = QByteArrayLiteral("HTTP/1.1 400 Bad Request");
593  break;
594  case Response::MethodNotAllowed:
595  ret = QByteArrayLiteral("HTTP/1.1 405 Method Not Allowed");
596  break;
597  case Response::RequestTimeout:
598  ret = QByteArrayLiteral("HTTP/1.1 408 Request Timeout");
599  break;
600  case Response::Continue:
601  ret = QByteArrayLiteral("HTTP/1.1 100 Continue");
602  break;
603  case Response::SwitchingProtocols:
604  ret = QByteArrayLiteral("HTTP/1.1 101 Switching Protocols");
605  break;
606  case Response::Created:
607  ret = QByteArrayLiteral("HTTP/1.1 201 Created");
608  break;
609  case Response::Accepted:
610  ret = QByteArrayLiteral("HTTP/1.1 202 Accepted");
611  break;
612  case Response::NonAuthoritativeInformation:
613  ret = QByteArrayLiteral("HTTP/1.1 203 Non-Authoritative Information");
614  break;
615  case Response::NoContent:
616  ret = QByteArrayLiteral("HTTP/1.1 204 No Content");
617  break;
618  case Response::ResetContent:
619  ret = QByteArrayLiteral("HTTP/1.1 205 Reset Content");
620  break;
621  case Response::PartialContent:
622  ret = QByteArrayLiteral("HTTP/1.1 206 Partial Content");
623  break;
624  case Response::MultipleChoices:
625  ret = QByteArrayLiteral("HTTP/1.1 300 Multiple Choices");
626  break;
627  case Response::UseProxy:
628  ret = QByteArrayLiteral("HTTP/1.1 305 Use Proxy");
629  break;
630  case Response::PaymentRequired:
631  ret = QByteArrayLiteral("HTTP/1.1 402 Payment Required");
632  break;
633  case Response::NotAcceptable:
634  ret = QByteArrayLiteral("HTTP/1.1 406 Not Acceptable");
635  break;
636  case Response::ProxyAuthenticationRequired:
637  ret = QByteArrayLiteral("HTTP/1.1 407 Proxy Authentication Required");
638  break;
639  case Response::Conflict:
640  ret = QByteArrayLiteral("HTTP/1.1 409 Conflict");
641  break;
642  case Response::Gone:
643  ret = QByteArrayLiteral("HTTP/1.1 410 Gone");
644  break;
645  case Response::LengthRequired:
646  ret = QByteArrayLiteral("HTTP/1.1 411 Length Required");
647  break;
648  case Response::PreconditionFailed:
649  ret = QByteArrayLiteral("HTTP/1.1 412 Precondition Failed");
650  break;
651  case Response::RequestEntityTooLarge:
652  ret = QByteArrayLiteral("HTTP/1.1 413 Request Entity Too Large");
653  break;
654  case Response::RequestURITooLong:
655  ret = QByteArrayLiteral("HTTP/1.1 414 Request-URI Too Long");
656  break;
657  case Response::UnsupportedMediaType:
658  ret = QByteArrayLiteral("HTTP/1.1 415 Unsupported Media Type");
659  break;
660  case Response::RequestedRangeNotSatisfiable:
661  ret = QByteArrayLiteral("HTTP/1.1 416 Requested Range Not Satisfiable");
662  break;
663  case Response::ExpectationFailed:
664  ret = QByteArrayLiteral("HTTP/1.1 417 Expectation Failed");
665  break;
666  case Response::NotImplemented:
667  ret = QByteArrayLiteral("HTTP/1.1 501 Not Implemented");
668  break;
669  case Response::BadGateway:
670  ret = QByteArrayLiteral("HTTP/1.1 502 Bad Gateway");
671  break;
672  case Response::ServiceUnavailable:
673  ret = QByteArrayLiteral("HTTP/1.1 503 Service Unavailable");
674  break;
675  case Response::MultiStatus:
676  ret = QByteArrayLiteral("HTTP/1.1 207 Multi-Status");
677  break;
678  case Response::GatewayTimeout:
679  ret = QByteArrayLiteral("HTTP/1.1 504 Gateway Timeout");
680  break;
681  case Response::HTTPVersionNotSupported:
682  ret = QByteArrayLiteral("HTTP/1.1 505 HTTP Version Not Supported");
683  break;
684  case Response::BandwidthLimitExceeded:
685  ret = QByteArrayLiteral("HTTP/1.1 509 Bandwidth Limit Exceeded");
686  break;
687  default:
688  ret = QByteArrayLiteral("HTTP/1.1 ").append(QByteArray::number(status));
689  break;
690  }
691 
692  return ret;
693 }
694 
695 #include "moc_protocolhttp.cpp"
Request request
Definition: context.h:72
virtual bool seek(qint64 pos)
Headers & headers() noexcept
QString errorString() const const
qsizetype size() const const
bool isEmpty() const const
Container for HTTP headers.
Definition: headers.h:23
Implements a web server.
Definition: server.h:59
void processRequest(EngineRequest *request)
Definition: engine.cpp:251
qsizetype length() const const
void processingFinished() override final
QByteArray read(qint64 maxSize)
int compare(QByteArrayView bv, Qt::CaseSensitivity cs) const const
A Cutelyst response.
Definition: response.h:28
QByteArray number(double n, char format, int precision)
virtual qint64 size() const const
void webSocketClosed(quint16 closeCode, const QString &reason)
Emitted when the websocket receives a close frame, including a close code and a reason, it&#39;s also emitted when the connection closes without the client sending the close frame.
Headers headers() const noexcept
Definition: request.cpp:313
qint64 doWrite(const char *data, qint64 len) override final
CaseInsensitive
qint64 write(const QByteArray &data)
QHostAddress remoteAddress
The Cutelyst namespace holds all public Cutelyst API.
QVector< HeaderKeyValue > data() const
Definition: headers.h:419
virtual qint64 bytesAvailable() const const
bool writeHeaders(quint16 status, const Cutelyst::Headers &headers) override final
QByteArray & append(QByteArrayView data)
QByteArray header(QByteArrayView key) const noexcept
Definition: headers.cpp:393
QString fromLatin1(QByteArrayView str)
QByteArray left(qsizetype len) const const
QByteArray hash(QByteArrayView data, Algorithm method)
QByteArray toBase64(Base64Options options) const const
qsizetype size() const const
Response * response() const noexcept
Definition: context.cpp:98
void setStatus(quint16 status) noexcept
Definition: response.cpp:73
void setHeader(const QByteArray &key, const QByteArray &value)
Definition: headers.cpp:437
QByteArray toUtf8() const const