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