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
25using namespace Cutelyst;
26
27QByteArray http11StatusMessage(quint16 status);
28
29Q_LOGGING_CATEGORY(C_SERVER_HTTP, "cutelyst.server.http", QtWarningMsg)
30Q_DECLARE_LOGGING_CATEGORY(C_SERVER_SOCK)
31
32ProtocolHttp::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
40ProtocolHttp::~ProtocolHttp()
41{
42 delete m_websocketProto;
43}
44
45Protocol::Type ProtocolHttp::type() const
46{
47 return Protocol::Type::Http11;
48}
49
50inline 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
71void 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
193ProtocolData *ProtocolHttp::createData(Socket *sock) const
194{
195 return new ProtoRequestHttp(sock, m_bufferSize);
196}
197
198bool 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
225void 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
270void 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
323ProtoRequestHttp::ProtoRequestHttp(Socket *sock, int bufferSize)
324 : ProtocolData(sock, bufferSize)
325{
326 isSecure = sock->isSecure;
327}
328
329ProtoRequestHttp::~ProtoRequestHttp()
330{
331}
332
333void ProtoRequestHttp::setupNewConnection(Socket *sock)
334{
335 serverAddress = sock->serverAddress;
336 remoteAddress = sock->remoteAddress;
337 remotePort = sock->remotePort;
338}
339
340bool 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
397qint64 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
437bool 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
452bool 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
466bool 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
480bool 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
495void 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
505bool 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 =
545 QCryptographicHash::hash(wsKey, QCryptographicHash::Sha1).toBase64();
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
556QByteArray 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
QVector< HeaderKeyValue > data() const
Definition: headers.h:419
QByteArray header(QByteArrayView key) const noexcept
Definition: headers.cpp:392
void setHeader(const QByteArray &key, const QByteArray &value)
Definition: headers.cpp:436
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.