5 #include "protocolhttp.h"
7 #include "protocolhttp2.h"
8 #include "protocolwebsocket.h"
12 #include <Cutelyst/Context>
13 #include <Cutelyst/Headers>
14 #include <Cutelyst/Response>
18 #include <QCoreApplication>
19 #include <QCryptographicHash>
22 #include <QLoggingCategory>
27 QByteArray http11StatusMessage(quint16 status);
29 Q_LOGGING_CATEGORY(C_SERVER_HTTP,
"cutelyst.server.http", QtWarningMsg)
30 Q_DECLARE_LOGGING_CATEGORY(C_SERVER_SOCK)
35 , m_upgradeH2c(upgradeH2c)
37 usingFrontendProxy = wsgi->usingFrontendProxy();
40 ProtocolHttp::~ProtocolHttp()
42 delete m_websocketProto;
45 Protocol::Type ProtocolHttp::type()
const
47 return Protocol::Type::Http11;
50 inline int CrLfIndexIn(
const char *str,
int len,
int from)
53 const char *pch =
static_cast<const char *
>(memchr(str + from,
'\r',
size_t(len - from)));
55 int pos = int(pch - str);
56 if ((pos + 1) < len) {
75 if (protoRequest->status & Cutelyst::EngineRequest::Async) {
79 if (protoRequest->connState == ProtoRequestHttp::ContentBody) {
86 remaining = protoRequest->contentLength - body->
size();
87 len = io->
read(m_postBuffer, qMin(m_postBufferSize, remaining));
89 qCWarning(C_SERVER_HTTP)
90 <<
"error while reading body" << len << protoRequest->headers;
91 sock->connectionClose();
94 bytesAvailable -= len;
97 body->
write(m_postBuffer, len);
98 }
while (bytesAvailable && remaining);
100 if (remaining == len) {
101 processRequest(sock, io);
107 qint64 len = io->
read(protoRequest->buffer + protoRequest->buf_size,
108 m_bufferSize - protoRequest->buf_size);
110 qCWarning(C_SERVER_HTTP) <<
"Failed to read from socket" << io->
errorString();
113 protoRequest->buf_size += len;
115 while (protoRequest->last < protoRequest->buf_size) {
118 int ix = CrLfIndexIn(protoRequest->buffer, protoRequest->buf_size, protoRequest->last);
120 qint64 len = ix - protoRequest->beginLine;
121 char *ptr = protoRequest->buffer + protoRequest->beginLine;
122 protoRequest->beginLine = ix + 2;
123 protoRequest->last = protoRequest->beginLine;
125 if (protoRequest->connState == ProtoRequestHttp::MethodLine) {
126 if (useStats && protoRequest->startOfRequest == TimePointSteady{}) {
127 protoRequest->startOfRequest = std::chrono::steady_clock::now();
130 parseMethod(ptr, ptr + len, sock);
131 protoRequest->connState = ProtoRequestHttp::HeaderLine;
132 protoRequest->contentLength = -1;
138 }
else if (protoRequest->connState == ProtoRequestHttp::HeaderLine) {
140 parseHeader(ptr, ptr + len, sock);
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();
153 qMin(protoRequest->contentLength,
154 static_cast<qint64
>(protoRequest->buf_size - protoRequest->last));
158 protoRequest->body->write(ptr, len);
160 protoRequest->last += len;
162 if (protoRequest->contentLength > len) {
175 if (!processRequest(sock, io)) {
181 if (protoRequest->startOfRequest == TimePointSteady{}) {
182 protoRequest->startOfRequest = std::chrono::steady_clock::now();
184 protoRequest->last = protoRequest->buf_size;
188 if (protoRequest->buf_size == m_bufferSize) {
207 if (m_upgradeH2c && m_upgradeH2c->upgradeH2C(sock, io, *request)) {
214 if (request->websocketUpgraded) {
218 if (request->status & Cutelyst::EngineRequest::Async) {
225 void ProtocolHttp::parseMethod(
const char *ptr,
const char *end,
Socket *sock)
const
228 const char *word_boundary = ptr;
229 while (*word_boundary !=
' ' && word_boundary < end) {
235 while (*word_boundary ==
' ' && word_boundary < end) {
241 while (*word_boundary !=
' ' && *word_boundary !=
'?' && word_boundary < end) {
246 protoRequest->setPath(
const_cast<char *
>(ptr),
int(word_boundary - ptr));
248 if (*word_boundary ==
'?') {
249 ptr = word_boundary + 1;
250 while (*word_boundary !=
' ' && word_boundary < end) {
253 protoRequest->query =
QByteArray(ptr,
int(word_boundary - ptr));
259 while (*word_boundary ==
' ' && word_boundary < end) {
264 while (*word_boundary !=
' ' && word_boundary < end) {
267 protoRequest->protocol =
QByteArray(ptr,
int(word_boundary - ptr));
270 void ProtocolHttp::parseHeader(
const char *ptr,
const char *end,
Socket *sock)
const
273 const char *word_boundary = ptr;
274 while (*word_boundary !=
':' && word_boundary < end) {
277 const auto key =
QByteArray(ptr,
int(word_boundary - ptr));
279 while ((*word_boundary ==
':' || *word_boundary ==
' ') && word_boundary < end) {
282 const auto value =
QByteArray(word_boundary,
int(end - word_boundary));
284 if (protoRequest->headerConnection == ProtoRequestHttp::HeaderConnection::NotSet &&
287 protoRequest->headerConnection = ProtoRequestHttp::HeaderConnection::Close;
289 protoRequest->headerConnection = ProtoRequestHttp::HeaderConnection::Keep;
291 }
else if (protoRequest->contentLength < 0 &&
294 qint64 cl = value.toLongLong(&ok);
296 protoRequest->contentLength = cl;
299 protoRequest->serverAddress = value;
300 protoRequest->headerHost =
true;
301 }
else if (usingFrontendProxy) {
302 if (!protoRequest->X_Forwarded_For &&
307 protoRequest->remotePort = 0;
308 protoRequest->X_Forwarded_For =
true;
309 }
else if (!protoRequest->X_Forwarded_Host &&
311 protoRequest->serverAddress = value;
312 protoRequest->X_Forwarded_Host =
true;
313 protoRequest->headerHost =
true;
314 }
else if (!protoRequest->X_Forwarded_Proto &&
316 protoRequest->isSecure = (value.compare(
"https") == 0);
317 protoRequest->X_Forwarded_Proto =
true;
320 protoRequest->headers.pushHeader(key, value);
323 ProtoRequestHttp::ProtoRequestHttp(
Socket *sock,
int bufferSize)
326 isSecure = sock->isSecure;
329 ProtoRequestHttp::~ProtoRequestHttp()
333 void ProtoRequestHttp::setupNewConnection(
Socket *sock)
342 if (websocketUpgraded &&
status != Cutelyst::Response::SwitchingProtocols) {
343 qCWarning(C_SERVER_SOCK) <<
"Trying to write header while on an Websocket context";
350 ProtoRequestHttp::HeaderConnection fallbackConnection = headerConnection;
351 headerConnection = ProtoRequestHttp::HeaderConnection::NotSet;
353 bool hasDate =
false;
354 auto it = headersData.begin();
355 while (it != headersData.end()) {
356 if (headerConnection == ProtoRequestHttp::HeaderConnection::NotSet &&
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;
363 headerConnection = ProtoRequestHttp::HeaderConnection::Keep;
377 if (headerConnection == ProtoRequestHttp::HeaderConnection::NotSet) {
378 if (fallbackConnection == ProtoRequestHttp::HeaderConnection::Keep ||
379 (fallbackConnection != ProtoRequestHttp::HeaderConnection::Close &&
381 headerConnection = ProtoRequestHttp::HeaderConnection::Keep;
382 data.
append(
"\r\nConnection: keep-alive", 24);
384 headerConnection = ProtoRequestHttp::HeaderConnection::Close;
385 data.
append(
"\r\nConnection: close", 19);
392 data.
append(
"\r\n\r\n", 4);
399 return io->
write(data, len);
404 if (websocketUpgraded) {
407 websocket_phase = ProtoRequestHttp::WebSocketPhaseHeaders;
412 if (!sock->requestFinished()) {
417 if (headerConnection == ProtoRequestHttp::HeaderConnection::Close) {
418 sock->connectionClose();
422 if (last < buf_size) {
424 int remaining = buf_size - last;
425 memmove(buffer, buffer + last,
size_t(remaining));
427 buf_size = remaining;
429 if (
status & EngineRequest::Async) {
430 sock->proto->parse(sock, io);
437 bool ProtoRequestHttp::webSocketSendTextMessage(
const QString &message)
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();
448 ProtoRequestHttp::OpCodeText, quint64(rawMessage.
size()));
452 bool ProtoRequestHttp::webSocketSendBinaryMessage(
const QByteArray &message)
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();
462 ProtoRequestHttp::OpCodeBinary, quint64(message.
size()));
466 bool ProtoRequestHttp::webSocketSendPing(
const QByteArray &payload)
468 if (headerConnection != ProtoRequestHttp::HeaderConnection::Upgrade) {
469 qCWarning(C_SERVER_HTTP) <<
"Not sending websocket ping due connection header not upgraded"
470 << headerConnection << payload.
size();
476 ProtoRequestHttp::OpCodePing, quint64(rawMessage.
size()));
480 bool ProtoRequestHttp::webSocketClose(quint16 code,
const QString &reason)
482 if (headerConnection != ProtoRequestHttp::HeaderConnection::Upgrade) {
483 qCWarning(C_SERVER_HTTP) <<
"Not sending websocket close due connection header not upgraded"
484 << headerConnection << code << reason;
488 const QByteArray reply = ProtocolWebSocket::createWebsocketCloseReply(reason, code);
490 sock->requestFinished();
491 sock->connectionClose();
495 void ProtoRequestHttp::socketDisconnected()
497 if (websocketUpgraded) {
498 if (websocket_finn_opcode != 0x88) {
501 sock->requestFinished();
505 bool ProtoRequestHttp::webSocketHandshakeDo(
const QByteArray &key,
509 if (headerConnection == ProtoRequestHttp::HeaderConnection::Upgrade) {
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();
524 response->
setStatus(Cutelyst::Response::SwitchingProtocols);
527 const auto localOrigin = origin.
isEmpty() ? requestHeaders.
header(
"Origin") : origin;
528 headers.
setHeader(
"Sec-Websocket-Origin"_qba, localOrigin.isEmpty() ?
"*"_qba : localOrigin);
532 }
else if (
const auto wsProtocol = requestHeaders.
header(
"Sec-Websocket-Protocol");
538 const QByteArray wsKey = localKey +
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
539 if (wsKey.
length() == 36) {
540 qCWarning(C_SERVER_SOCK) <<
"Missing websocket key";
548 headerConnection = ProtoRequestHttp::HeaderConnection::Upgrade;
549 websocketUpgraded =
true;
550 auto httpProto =
static_cast<ProtocolHttp *
>(sock->proto);
551 sock->proto = httpProto->m_websocketProto;
556 QByteArray http11StatusMessage(quint16 status)
561 ret = QByteArrayLiteral(
"HTTP/1.1 200 OK");
563 case Response::Found:
564 ret = QByteArrayLiteral(
"HTTP/1.1 302 Found");
566 case Response::NotFound:
567 ret = QByteArrayLiteral(
"HTTP/1.1 404 Not Found");
569 case Response::InternalServerError:
570 ret = QByteArrayLiteral(
"HTTP/1.1 500 Internal Server Error");
572 case Response::MovedPermanently:
573 ret = QByteArrayLiteral(
"HTTP/1.1 301 Moved Permanently");
575 case Response::NotModified:
576 ret = QByteArrayLiteral(
"HTTP/1.1 304 Not Modified");
578 case Response::SeeOther:
579 ret = QByteArrayLiteral(
"HTTP/1.1 303 See Other");
581 case Response::Forbidden:
582 ret = QByteArrayLiteral(
"HTTP/1.1 403 Forbidden");
584 case Response::TemporaryRedirect:
585 ret = QByteArrayLiteral(
"HTTP/1.1 307 Temporary Redirect");
587 case Response::Unauthorized:
588 ret = QByteArrayLiteral(
"HTTP/1.1 401 Unauthorized");
590 case Response::BadRequest:
591 ret = QByteArrayLiteral(
"HTTP/1.1 400 Bad Request");
593 case Response::MethodNotAllowed:
594 ret = QByteArrayLiteral(
"HTTP/1.1 405 Method Not Allowed");
596 case Response::RequestTimeout:
597 ret = QByteArrayLiteral(
"HTTP/1.1 408 Request Timeout");
599 case Response::Continue:
600 ret = QByteArrayLiteral(
"HTTP/1.1 100 Continue");
602 case Response::SwitchingProtocols:
603 ret = QByteArrayLiteral(
"HTTP/1.1 101 Switching Protocols");
605 case Response::Created:
606 ret = QByteArrayLiteral(
"HTTP/1.1 201 Created");
608 case Response::Accepted:
609 ret = QByteArrayLiteral(
"HTTP/1.1 202 Accepted");
611 case Response::NonAuthoritativeInformation:
612 ret = QByteArrayLiteral(
"HTTP/1.1 203 Non-Authoritative Information");
614 case Response::NoContent:
615 ret = QByteArrayLiteral(
"HTTP/1.1 204 No Content");
617 case Response::ResetContent:
618 ret = QByteArrayLiteral(
"HTTP/1.1 205 Reset Content");
620 case Response::PartialContent:
621 ret = QByteArrayLiteral(
"HTTP/1.1 206 Partial Content");
623 case Response::MultipleChoices:
624 ret = QByteArrayLiteral(
"HTTP/1.1 300 Multiple Choices");
626 case Response::UseProxy:
627 ret = QByteArrayLiteral(
"HTTP/1.1 305 Use Proxy");
629 case Response::PaymentRequired:
630 ret = QByteArrayLiteral(
"HTTP/1.1 402 Payment Required");
632 case Response::NotAcceptable:
633 ret = QByteArrayLiteral(
"HTTP/1.1 406 Not Acceptable");
635 case Response::ProxyAuthenticationRequired:
636 ret = QByteArrayLiteral(
"HTTP/1.1 407 Proxy Authentication Required");
638 case Response::Conflict:
639 ret = QByteArrayLiteral(
"HTTP/1.1 409 Conflict");
642 ret = QByteArrayLiteral(
"HTTP/1.1 410 Gone");
644 case Response::LengthRequired:
645 ret = QByteArrayLiteral(
"HTTP/1.1 411 Length Required");
647 case Response::PreconditionFailed:
648 ret = QByteArrayLiteral(
"HTTP/1.1 412 Precondition Failed");
650 case Response::RequestEntityTooLarge:
651 ret = QByteArrayLiteral(
"HTTP/1.1 413 Request Entity Too Large");
653 case Response::RequestURITooLong:
654 ret = QByteArrayLiteral(
"HTTP/1.1 414 Request-URI Too Long");
656 case Response::UnsupportedMediaType:
657 ret = QByteArrayLiteral(
"HTTP/1.1 415 Unsupported Media Type");
659 case Response::RequestedRangeNotSatisfiable:
660 ret = QByteArrayLiteral(
"HTTP/1.1 416 Requested Range Not Satisfiable");
662 case Response::ExpectationFailed:
663 ret = QByteArrayLiteral(
"HTTP/1.1 417 Expectation Failed");
665 case Response::NotImplemented:
666 ret = QByteArrayLiteral(
"HTTP/1.1 501 Not Implemented");
668 case Response::BadGateway:
669 ret = QByteArrayLiteral(
"HTTP/1.1 502 Bad Gateway");
671 case Response::ServiceUnavailable:
672 ret = QByteArrayLiteral(
"HTTP/1.1 503 Service Unavailable");
674 case Response::MultiStatus:
675 ret = QByteArrayLiteral(
"HTTP/1.1 207 Multi-Status");
677 case Response::GatewayTimeout:
678 ret = QByteArrayLiteral(
"HTTP/1.1 504 Gateway Timeout");
680 case Response::HTTPVersionNotSupported:
681 ret = QByteArrayLiteral(
"HTTP/1.1 505 HTTP Version Not Supported");
683 case Response::BandwidthLimitExceeded:
684 ret = QByteArrayLiteral(
"HTTP/1.1 509 Bandwidth Limit Exceeded");
694 #include "moc_protocolhttp.cpp"
Response * response() const noexcept
QHostAddress remoteAddress
void processRequest(EngineRequest *request)
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
void setStatus(quint16 status) noexcept
Headers & headers() noexcept
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