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>
27Q_LOGGING_CATEGORY(C_SERVER_HTTP,
"cutelyst.server.http", QtWarningMsg)
28Q_DECLARE_LOGGING_CATEGORY(C_SERVER_SOCK)
33 , m_upgradeH2c(upgradeH2c)
35 usingFrontendProxy = wsgi->usingFrontendProxy();
38ProtocolHttp::~ProtocolHttp()
40 delete m_websocketProto;
43Protocol::Type ProtocolHttp::type()
const
45 return Protocol::Type::Http11;
48inline int CrLfIndexIn(
const char *str,
int len,
int from)
51 const char *pch =
static_cast<const char *
>(memchr(str + from,
'\r',
size_t(len - from)));
53 int pos = int(pch - str);
54 if ((pos + 1) < len) {
73 if (protoRequest->status & Cutelyst::EngineRequest::Async) {
77 if (protoRequest->connState == ProtoRequestHttp::ContentBody) {
84 remaining = protoRequest->contentLength - body->
size();
85 len = io->
read(m_postBuffer, qMin(m_postBufferSize, remaining));
87 qCWarning(C_SERVER_HTTP)
88 <<
"error while reading body" << len << protoRequest->headers;
89 sock->connectionClose();
92 bytesAvailable -= len;
95 body->
write(m_postBuffer, len);
96 }
while (bytesAvailable && remaining);
98 if (remaining == len) {
99 processRequest(sock, io);
105 qint64 len = io->
read(protoRequest->buffer + protoRequest->buf_size,
106 m_bufferSize - protoRequest->buf_size);
108 qCWarning(C_SERVER_HTTP) <<
"Failed to read from socket" << io->
errorString();
111 protoRequest->buf_size += len;
113 while (protoRequest->last < protoRequest->buf_size) {
116 int ix = CrLfIndexIn(protoRequest->buffer, protoRequest->buf_size, protoRequest->last);
118 qint64 len = ix - protoRequest->beginLine;
119 char *ptr = protoRequest->buffer + protoRequest->beginLine;
120 protoRequest->beginLine = ix + 2;
121 protoRequest->last = protoRequest->beginLine;
123 if (protoRequest->connState == ProtoRequestHttp::MethodLine) {
124 if (useStats && protoRequest->startOfRequest == TimePointSteady{}) {
125 protoRequest->startOfRequest = std::chrono::steady_clock::now();
128 parseMethod(ptr, ptr + len, sock);
129 protoRequest->connState = ProtoRequestHttp::HeaderLine;
130 protoRequest->contentLength = -1;
136 }
else if (protoRequest->connState == ProtoRequestHttp::HeaderLine) {
138 parseHeader(ptr, ptr + len, sock);
140 if (protoRequest->contentLength > 0) {
141 protoRequest->connState = ProtoRequestHttp::ContentBody;
142 protoRequest->body = createBody(protoRequest->contentLength);
143 if (!protoRequest->body) {
144 qCWarning(C_SERVER_HTTP) <<
"error while creating body, closing socket";
145 sock->connectionClose();
151 qMin(protoRequest->contentLength,
152 static_cast<qint64
>(protoRequest->buf_size - protoRequest->last));
156 protoRequest->body->write(ptr, len);
158 protoRequest->last += len;
160 if (protoRequest->contentLength > len) {
173 if (!processRequest(sock, io)) {
179 if (protoRequest->startOfRequest == TimePointSteady{}) {
180 protoRequest->startOfRequest = std::chrono::steady_clock::now();
182 protoRequest->last = protoRequest->buf_size;
186 if (protoRequest->buf_size == m_bufferSize) {
205 if (m_upgradeH2c && m_upgradeH2c->upgradeH2C(sock, io, *request)) {
212 if (request->websocketUpgraded) {
216 if (request->status & Cutelyst::EngineRequest::Async) {
223void ProtocolHttp::parseMethod(
const char *ptr,
const char *end,
Socket *sock)
const
226 const char *word_boundary = ptr;
227 while (*word_boundary !=
' ' && word_boundary < end) {
233 while (*word_boundary ==
' ' && word_boundary < end) {
239 while (*word_boundary !=
' ' && *word_boundary !=
'?' && word_boundary < end) {
244 protoRequest->setPath(
const_cast<char *
>(ptr),
int(word_boundary - ptr));
246 if (*word_boundary ==
'?') {
247 ptr = word_boundary + 1;
248 while (*word_boundary !=
' ' && word_boundary < end) {
251 protoRequest->query =
QByteArray(ptr,
int(word_boundary - ptr));
257 while (*word_boundary ==
' ' && word_boundary < end) {
262 while (*word_boundary !=
' ' && word_boundary < end) {
265 protoRequest->protocol =
QByteArray(ptr,
int(word_boundary - ptr));
268void ProtocolHttp::parseHeader(
const char *ptr,
const char *end,
Socket *sock)
const
271 const char *word_boundary = ptr;
272 while (*word_boundary !=
':' && word_boundary < end) {
275 const auto key =
QByteArray(ptr,
int(word_boundary - ptr));
277 while ((*word_boundary ==
':' || *word_boundary ==
' ') && word_boundary < end) {
280 const auto value =
QByteArray(word_boundary,
int(end - word_boundary));
282 if (protoRequest->headerConnection == ProtoRequestHttp::HeaderConnection::NotSet &&
285 protoRequest->headerConnection = ProtoRequestHttp::HeaderConnection::Close;
287 protoRequest->headerConnection = ProtoRequestHttp::HeaderConnection::Keep;
289 }
else if (protoRequest->contentLength < 0 &&
292 qint64 cl = value.toLongLong(&ok);
294 protoRequest->contentLength = cl;
297 protoRequest->serverAddress = value;
298 protoRequest->headerHost =
true;
299 }
else if (usingFrontendProxy) {
300 if (!protoRequest->X_Forwarded_For &&
305 protoRequest->remotePort = 0;
306 protoRequest->X_Forwarded_For =
true;
307 }
else if (!protoRequest->X_Forwarded_Host &&
309 protoRequest->serverAddress = value;
310 protoRequest->X_Forwarded_Host =
true;
311 protoRequest->headerHost =
true;
312 }
else if (!protoRequest->X_Forwarded_Proto &&
314 protoRequest->isSecure = (value.compare(
"https") == 0);
315 protoRequest->X_Forwarded_Proto =
true;
318 protoRequest->headers.pushHeader(key, value);
321ProtoRequestHttp::ProtoRequestHttp(
Socket *sock,
int bufferSize)
324 isSecure = sock->isSecure;
327ProtoRequestHttp::~ProtoRequestHttp()
331void ProtoRequestHttp::setupNewConnection(
Socket *sock)
340 if (websocketUpgraded &&
status != Cutelyst::Response::SwitchingProtocols) {
341 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);
437bool 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()));
452bool 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()));
466bool 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()));
480bool 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();
495void ProtoRequestHttp::socketDisconnected()
497 if (websocketUpgraded) {
498 if (websocket_finn_opcode != 0x88) {
501 sock->requestFinished();
505bool 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#include "moc_protocolhttp.cpp"
Response * response() const noexcept
QHostAddress remoteAddress
static const char * httpStatusMessage(quint16 status, int *len=nullptr)
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
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