cutelyst  5.0.1
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 *server, ProtocolHttp2 *upgradeH2c)
34  : Protocol(server)
35  , m_websocketProto(new ProtocolWebSocket(server))
36  , m_upgradeH2c(upgradeH2c)
37 {
38  usingFrontendProxy = server->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  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  for (const auto &[key, value] : headersData) {
356  if (headerConnection == ProtoRequestHttp::HeaderConnection::NotSet &&
357  key.compare("Connection", Qt::CaseInsensitive) == 0) {
358  if (value.compare("close") == 0) {
359  headerConnection = ProtoRequestHttp::HeaderConnection::Close;
360  } else if (value.compare("Upgrade") == 0) {
361  headerConnection = ProtoRequestHttp::HeaderConnection::Upgrade;
362  } else {
363  headerConnection = ProtoRequestHttp::HeaderConnection::Keep;
364  }
365  } else if (!hasDate && key.compare("Date", Qt::CaseInsensitive) == 0) {
366  hasDate = true;
367  }
368 
369  data.append("\r\n");
370  data.append(key);
371  data.append(": ");
372  data.append(value);
373  }
374 
375  if (headerConnection == ProtoRequestHttp::HeaderConnection::NotSet) {
376  if (fallbackConnection == ProtoRequestHttp::HeaderConnection::Keep ||
377  (fallbackConnection != ProtoRequestHttp::HeaderConnection::Close &&
378  protocol.compare("HTTP/1.1") == 0)) {
379  headerConnection = ProtoRequestHttp::HeaderConnection::Keep;
380  data.append("\r\nConnection: keep-alive", 24);
381  } else {
382  headerConnection = ProtoRequestHttp::HeaderConnection::Close;
383  data.append("\r\nConnection: close", 19);
384  }
385  }
386 
387  if (!hasDate) {
388  data.append(static_cast<ServerEngine *>(sock->engine)->lastDate());
389  }
390  data.append("\r\n\r\n", 4);
391 
392  return io->write(data) == data.size();
393 }
394 
395 qint64 ProtoRequestHttp::doWrite(const char *data, qint64 len)
396 {
397  return io->write(data, len);
398 }
399 
401 {
402  if (websocketUpgraded) {
403  // need 2 byte header
404  websocket_need = 2;
405  websocket_phase = ProtoRequestHttp::WebSocketPhase::WebSocketPhaseHeaders;
406  buf_size = 0;
407  return;
408  }
409 
410  if (!sock->requestFinished()) {
411  // disconnected
412  return;
413  }
414 
415  if (headerConnection == ProtoRequestHttp::HeaderConnection::Close) {
416  sock->connectionClose();
417  return;
418  }
419 
420  if (last < buf_size) {
421  // move pipelined request to 0
422  int remaining = buf_size - last;
423  memmove(buffer, buffer + last, size_t(remaining));
424  resetData();
425  buf_size = remaining;
426 
427  if (status & EngineRequest::Async) {
428  sock->proto->parse(sock, io);
429  }
430  } else {
431  resetData();
432  }
433 }
434 
435 bool ProtoRequestHttp::webSocketSendTextMessage(const QString &message)
436 {
437  if (headerConnection != ProtoRequestHttp::HeaderConnection::Upgrade) {
438  qCWarning(C_SERVER_HTTP)
439  << "Not sending websocket text message due connection header not upgraded"
440  << headerConnection << message.size();
441  return false;
442  }
443 
444  const QByteArray rawMessage = message.toUtf8();
445  const QByteArray headers = ProtocolWebSocket::createWebsocketHeader(
446  ProtoRequestHttp::OpCodeText, quint64(rawMessage.size()));
447  return doWrite(headers) == headers.size() && doWrite(rawMessage) == rawMessage.size();
448 }
449 
450 bool ProtoRequestHttp::webSocketSendBinaryMessage(const QByteArray &message)
451 {
452  if (headerConnection != ProtoRequestHttp::HeaderConnection::Upgrade) {
453  qCWarning(C_SERVER_HTTP)
454  << "Not sending websocket binary messagedue connection header not upgraded"
455  << headerConnection << message.size();
456  return false;
457  }
458 
459  const QByteArray headers = ProtocolWebSocket::createWebsocketHeader(
460  ProtoRequestHttp::OpCodeBinary, quint64(message.size()));
461  return doWrite(headers) == headers.size() && doWrite(message) == message.size();
462 }
463 
464 bool ProtoRequestHttp::webSocketSendPing(const QByteArray &payload)
465 {
466  if (headerConnection != ProtoRequestHttp::HeaderConnection::Upgrade) {
467  qCWarning(C_SERVER_HTTP) << "Not sending websocket ping due connection header not upgraded"
468  << headerConnection << payload.size();
469  return false;
470  }
471 
472  const QByteArray rawMessage = payload.left(125);
473  const QByteArray headers = ProtocolWebSocket::createWebsocketHeader(
474  ProtoRequestHttp::OpCodePing, quint64(rawMessage.size()));
475  return doWrite(headers) == headers.size() && doWrite(rawMessage) == rawMessage.size();
476 }
477 
478 bool ProtoRequestHttp::webSocketClose(quint16 code, const QString &reason)
479 {
480  if (headerConnection != ProtoRequestHttp::HeaderConnection::Upgrade) {
481  qCWarning(C_SERVER_HTTP) << "Not sending websocket close due connection header not upgraded"
482  << headerConnection << code << reason;
483  return false;
484  }
485 
486  const QByteArray reply = ProtocolWebSocket::createWebsocketCloseReply(reason, code);
487  bool ret = doWrite(reply) == reply.size();
488  sock->requestFinished();
489  sock->connectionClose();
490  return ret;
491 }
492 
493 void ProtoRequestHttp::socketDisconnected()
494 {
495  if (websocketUpgraded) {
496  if (websocket_finn_opcode != 0x88) {
497  Q_EMIT context->request()->webSocketClosed(1005, QString{});
498  }
499  sock->requestFinished();
500  }
501 }
502 
503 bool ProtoRequestHttp::webSocketHandshakeDo(const QByteArray &key,
504  const QByteArray &origin,
505  const QByteArray &protocol)
506 {
507  if (headerConnection == ProtoRequestHttp::HeaderConnection::Upgrade) {
508  return true;
509  }
510 
511  if (sock->proto->type() != Protocol::Type::Http11) {
512  qCWarning(C_SERVER_SOCK)
513  << "Upgrading a connection to websocket is only supported with the HTTP/1.1 protocol"
514  << typeid(sock->proto).name();
515  return false;
516  }
517 
518  const Cutelyst::Headers requestHeaders = context->request()->headers();
519  Cutelyst::Response *response = context->response();
520  Cutelyst::Headers &headers = response->headers();
521 
522  response->setStatus(Cutelyst::Response::SwitchingProtocols);
523  headers.setHeader("Upgrade"_ba, "WebSocket"_ba);
524  headers.setHeader("Connection"_ba, "Upgrade"_ba);
525  const auto localOrigin = origin.isEmpty() ? requestHeaders.header("Origin") : origin;
526  headers.setHeader("Sec-Websocket-Origin"_ba, localOrigin.isEmpty() ? "*"_ba : localOrigin);
527 
528  if (!protocol.isEmpty()) {
529  headers.setHeader("Sec-Websocket-Protocol"_ba, protocol);
530  } else if (const auto wsProtocol = requestHeaders.header("Sec-Websocket-Protocol");
531  !wsProtocol.isEmpty()) {
532  headers.setHeader("Sec-Websocket-Protocol"_ba, wsProtocol);
533  }
534 
535  const QByteArray localKey = key.isEmpty() ? requestHeaders.header("Sec-Websocket-Key") : key;
536  const QByteArray wsKey = localKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
537  if (wsKey.length() == 36) {
538  qCWarning(C_SERVER_SOCK) << "Missing websocket key";
539  return false;
540  }
541 
542  const QByteArray wsAccept =
544  headers.setHeader("Sec-Websocket-Accept"_ba, wsAccept);
545 
546  headerConnection = ProtoRequestHttp::HeaderConnection::Upgrade;
547  websocketUpgraded = true;
548  const auto *httpProto = static_cast<ProtocolHttp *>(sock->proto);
549  sock->proto = httpProto->m_websocketProto;
550 
551  return writeHeaders(Cutelyst::Response::SwitchingProtocols, headers);
552 }
553 
554 QByteArray http11StatusMessage(quint16 status)
555 {
556  QByteArray ret;
557  switch (status) {
558  case Response::OK:
559  ret = QByteArrayLiteral("HTTP/1.1 200 OK");
560  break;
561  case Response::Found:
562  ret = QByteArrayLiteral("HTTP/1.1 302 Found");
563  break;
564  case Response::NotFound:
565  ret = QByteArrayLiteral("HTTP/1.1 404 Not Found");
566  break;
567  case Response::InternalServerError:
568  ret = QByteArrayLiteral("HTTP/1.1 500 Internal Server Error");
569  break;
570  case Response::MovedPermanently:
571  ret = QByteArrayLiteral("HTTP/1.1 301 Moved Permanently");
572  break;
573  case Response::NotModified:
574  ret = QByteArrayLiteral("HTTP/1.1 304 Not Modified");
575  break;
576  case Response::SeeOther:
577  ret = QByteArrayLiteral("HTTP/1.1 303 See Other");
578  break;
579  case Response::Forbidden:
580  ret = QByteArrayLiteral("HTTP/1.1 403 Forbidden");
581  break;
582  case Response::TemporaryRedirect:
583  ret = QByteArrayLiteral("HTTP/1.1 307 Temporary Redirect");
584  break;
585  case Response::Unauthorized:
586  ret = QByteArrayLiteral("HTTP/1.1 401 Unauthorized");
587  break;
588  case Response::BadRequest:
589  ret = QByteArrayLiteral("HTTP/1.1 400 Bad Request");
590  break;
591  case Response::MethodNotAllowed:
592  ret = QByteArrayLiteral("HTTP/1.1 405 Method Not Allowed");
593  break;
594  case Response::RequestTimeout:
595  ret = QByteArrayLiteral("HTTP/1.1 408 Request Timeout");
596  break;
597  case Response::Continue:
598  ret = QByteArrayLiteral("HTTP/1.1 100 Continue");
599  break;
600  case Response::SwitchingProtocols:
601  ret = QByteArrayLiteral("HTTP/1.1 101 Switching Protocols");
602  break;
603  case Response::Created:
604  ret = QByteArrayLiteral("HTTP/1.1 201 Created");
605  break;
606  case Response::Accepted:
607  ret = QByteArrayLiteral("HTTP/1.1 202 Accepted");
608  break;
609  case Response::NonAuthoritativeInformation:
610  ret = QByteArrayLiteral("HTTP/1.1 203 Non-Authoritative Information");
611  break;
612  case Response::NoContent:
613  ret = QByteArrayLiteral("HTTP/1.1 204 No Content");
614  break;
615  case Response::ResetContent:
616  ret = QByteArrayLiteral("HTTP/1.1 205 Reset Content");
617  break;
618  case Response::PartialContent:
619  ret = QByteArrayLiteral("HTTP/1.1 206 Partial Content");
620  break;
621  case Response::MultipleChoices:
622  ret = QByteArrayLiteral("HTTP/1.1 300 Multiple Choices");
623  break;
624  case Response::UseProxy:
625  ret = QByteArrayLiteral("HTTP/1.1 305 Use Proxy");
626  break;
627  case Response::PaymentRequired:
628  ret = QByteArrayLiteral("HTTP/1.1 402 Payment Required");
629  break;
630  case Response::NotAcceptable:
631  ret = QByteArrayLiteral("HTTP/1.1 406 Not Acceptable");
632  break;
633  case Response::ProxyAuthenticationRequired:
634  ret = QByteArrayLiteral("HTTP/1.1 407 Proxy Authentication Required");
635  break;
636  case Response::Conflict:
637  ret = QByteArrayLiteral("HTTP/1.1 409 Conflict");
638  break;
639  case Response::Gone:
640  ret = QByteArrayLiteral("HTTP/1.1 410 Gone");
641  break;
642  case Response::LengthRequired:
643  ret = QByteArrayLiteral("HTTP/1.1 411 Length Required");
644  break;
645  case Response::PreconditionFailed:
646  ret = QByteArrayLiteral("HTTP/1.1 412 Precondition Failed");
647  break;
648  case Response::RequestEntityTooLarge:
649  ret = QByteArrayLiteral("HTTP/1.1 413 Request Entity Too Large");
650  break;
651  case Response::RequestURITooLong:
652  ret = QByteArrayLiteral("HTTP/1.1 414 Request-URI Too Long");
653  break;
654  case Response::UnsupportedMediaType:
655  ret = QByteArrayLiteral("HTTP/1.1 415 Unsupported Media Type");
656  break;
657  case Response::RequestedRangeNotSatisfiable:
658  ret = QByteArrayLiteral("HTTP/1.1 416 Requested Range Not Satisfiable");
659  break;
660  case Response::ExpectationFailed:
661  ret = QByteArrayLiteral("HTTP/1.1 417 Expectation Failed");
662  break;
663  case Response::NotImplemented:
664  ret = QByteArrayLiteral("HTTP/1.1 501 Not Implemented");
665  break;
666  case Response::BadGateway:
667  ret = QByteArrayLiteral("HTTP/1.1 502 Bad Gateway");
668  break;
669  case Response::ServiceUnavailable:
670  ret = QByteArrayLiteral("HTTP/1.1 503 Service Unavailable");
671  break;
672  case Response::MultiStatus:
673  ret = QByteArrayLiteral("HTTP/1.1 207 Multi-Status");
674  break;
675  case Response::GatewayTimeout:
676  ret = QByteArrayLiteral("HTTP/1.1 504 Gateway Timeout");
677  break;
678  case Response::HTTPVersionNotSupported:
679  ret = QByteArrayLiteral("HTTP/1.1 505 HTTP Version Not Supported");
680  break;
681  case Response::BandwidthLimitExceeded:
682  ret = QByteArrayLiteral("HTTP/1.1 509 Bandwidth Limit Exceeded");
683  break;
684  default:
685  ret = QByteArrayLiteral("HTTP/1.1 ").append(QByteArray::number(status));
686  break;
687  }
688 
689  return ret;
690 }
691 
692 #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:110
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:312
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:420
virtual qint64 bytesAvailable() const const
bool writeHeaders(quint16 status, const Cutelyst::Headers &headers) override final
QByteArray & append(QByteArrayView data)
QString fromLatin1(QByteArrayView str)
QByteArray left(qsizetype len) const const
QByteArray hash(QByteArrayView data, Algorithm method)
QByteArray toBase64(Base64Options options) const const
QByteArray header(QAnyStringView key) const noexcept
Definition: headers.cpp:419
qsizetype size() const const
Response * response() const noexcept
Definition: context.cpp:98
void setStatus(quint16 status) noexcept
Definition: response.cpp:74
void setHeader(const QByteArray &key, const QByteArray &value)
Definition: headers.cpp:466
QByteArray toUtf8() const const