cutelyst 5.0.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
25using namespace Cutelyst;
26using namespace Qt::Literals::StringLiterals;
27
28QByteArray http11StatusMessage(quint16 status);
29
30Q_LOGGING_CATEGORY(C_SERVER_HTTP, "cutelyst.server.http", QtWarningMsg)
31Q_DECLARE_LOGGING_CATEGORY(C_SERVER_SOCK)
32
33ProtocolHttp::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
41ProtocolHttp::~ProtocolHttp()
42{
43 delete m_websocketProto;
44}
45
46Protocol::Type ProtocolHttp::type() const
47{
48 return Protocol::Type::Http11;
49}
50
51inline 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
72void 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
194ProtocolData *ProtocolHttp::createData(Socket *sock) const
195{
196 return new ProtoRequestHttp(sock, m_bufferSize);
197}
198
199bool 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
226void 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
271void 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
324ProtoRequestHttp::ProtoRequestHttp(Socket *sock, int bufferSize)
325 : ProtocolData(sock, bufferSize)
326{
327 isSecure = sock->isSecure;
328}
329
330ProtoRequestHttp::~ProtoRequestHttp()
331{
332}
333
334void ProtoRequestHttp::setupNewConnection(Socket *sock)
335{
336 serverAddress = sock->serverAddress;
337 remoteAddress = sock->remoteAddress;
338 remotePort = sock->remotePort;
339}
340
341bool 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
395qint64 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
435bool 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
450bool 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
464bool 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
478bool 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
493void 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
503bool 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 auto httpProto = static_cast<ProtocolHttp *>(sock->proto);
549 sock->proto = httpProto->m_websocketProto;
550
551 return writeHeaders(Cutelyst::Response::SwitchingProtocols, headers);
552}
553
554QByteArray 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:71
Response * response() const noexcept
Definition context.cpp:98
void processRequest(EngineRequest *request)
Definition engine.cpp:110
Container for HTTP headers.
Definition headers.h:24
QVector< HeaderKeyValue > data() const
Definition headers.h:420
QByteArray header(QAnyStringView key) const noexcept
Definition headers.cpp:393
void setHeader(const QByteArray &key, const QByteArray &value)
Definition headers.cpp:440
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:313
A Cutelyst response.
Definition response.h:29
void setStatus(quint16 status) noexcept
Definition response.cpp:74
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