5#include "protocolhttp2.h"
12#include <QLoggingCategory>
15using namespace Qt::Literals::StringLiterals;
17Q_LOGGING_CATEGORY(C_SERVER_H2,
"cutelyst.server.http2", QtWarningMsg)
26 quint8 rbit_stream_id3;
27 quint8 rbit_stream_id2;
28 quint8 rbit_stream_id1;
29 quint8 rbit_stream_id0;
32enum SettingsFlags { FlagSettingsAck = 0x1 };
34enum PingFlags { FlagPingAck = 0x1 };
37 FlagHeadersEndStream = 0x1,
38 FlagHeadersEndHeaders = 0x4,
39 FlagHeadersPadded = 0x8,
40 FlagHeadersPriority = 0x20,
43enum PushPromiseFlags {
44 FlagPushPromiseEndHeaders = 0x4,
45 FlagPushPromisePadded = 0x8,
49 FlagDataEndStream = 0x1,
59 FramePushPromise = 0x5,
62 FrameWindowUpdate = 0x8,
63 FrameContinuation = 0x9
68 ErrorProtocolError = 0x1,
69 ErrorInternalError = 0x2,
70 ErrorFlowControlError = 0x3,
71 ErrorSettingsTimeout = 0x4,
72 ErrorStreamClosed = 0x5,
73 ErrorFrameSizeError = 0x6,
74 ErrorRefusedStream = 0x7,
76 ErrorCompressionError = 0x9,
77 ErrorConnectError = 0xA,
78 ErrorEnhanceYourCalm = 0xB,
79 ErrorInadequateSecurity = 0xC,
80 ErrorHttp11Required = 0xD
84 SETTINGS_HEADER_TABLE_SIZE = 0x1,
85 SETTINGS_ENABLE_PUSH = 0x2,
86 SETTINGS_MAX_CONCURRENT_STREAMS = 0x3,
87 SETTINGS_INITIAL_WINDOW_SIZE = 0x4,
88 SETTINGS_MAX_FRAME_SIZE = 0x5,
89 SETTINGS_MAX_HEADER_LIST_SIZE = 0x6,
90 SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x8,
93constexpr int PREFACE_SIZE = 24;
96ProtocolHttp2::ProtocolHttp2(
Server *wsgi)
98 , m_headerTableSize(qint32(wsgi->http2HeaderTableSize()))
100 m_bufferSize = qMin(m_bufferSize, 2147483647);
103 if (m_bufferSize < 16393) {
104 qFatal(
"HTTP/2 Protocol requires that buffer-size to be at least '16393' in size, current "
106 QByteArray::number(m_bufferSize).constData());
109 m_maxFrameSize = quint32(m_bufferSize - 9);
112ProtocolHttp2::~ProtocolHttp2()
116Protocol::Type ProtocolHttp2::type()
const
118 return Protocol::Type::Http2;
121void ProtocolHttp2::parse(
Socket *sock, QIODevice *io)
const
125 qint64 bytesAvailable = io->bytesAvailable();
131 io->read(request->buffer + request->buf_size, m_bufferSize - request->buf_size);
132 bytesAvailable -= len;
135 request->buf_size += len;
137 while (request->buf_size && ret == 0) {
141 if (request->connState == ProtoRequestHttp2::MethodLine) {
142 if (request->buf_size >= PREFACE_SIZE) {
143 if (memcmp(request->buffer,
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0) {
146 request->buf_size -= PREFACE_SIZE;
147 memmove(request->buffer,
148 request->buffer + PREFACE_SIZE,
149 size_t(request->buf_size));
150 request->connState = ProtoRequestHttp2::H2Frames;
154 {SETTINGS_ENABLE_CONNECT_PROTOCOL, 0},
155 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
156 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
159 qCDebug(C_SERVER_H2) <<
"Protocol Error: Invalid connection preface"
160 << sock->remoteAddress.toString();
164 sock->connectionClose();
172 }
else if (request->connState == ProtoRequestHttp2::H2Frames) {
173 if (request->buf_size >=
int(
sizeof(
struct h2_frame))) {
174 auto fr =
reinterpret_cast<struct h2_frame *
>(request->buffer);
176 frame.len = quint32(fr->size0 | (fr->size1 << 8) | (fr->size2 << 16));
178 quint32(fr->rbit_stream_id0 | (fr->rbit_stream_id1 << 8) |
179 (fr->rbit_stream_id2 << 16) |
180 ((fr->rbit_stream_id3 & ~0x80) << 24));
181 frame.type = fr->type;
182 frame.flags = fr->flags;
184 quint32(fr->size0 | (fr->size1 << 8) | (fr->size2 << 16));
186 quint32(fr->rbit_stream_id0 | (fr->rbit_stream_id1 << 8) |
187 (fr->rbit_stream_id2 << 16) |
188 ((fr->rbit_stream_id3 & ~0x80) << 24));
197 if (frame.streamId && !(frame.streamId & 1)) {
198 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
202 if (request->pktsize > m_maxFrameSize) {
205 ret = sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
209 if (request->pktsize >
210 (quint32(request->buf_size) -
sizeof(
struct h2_frame))) {
216 if (request->streamForContinuation) {
217 if (fr->type == FrameContinuation &&
218 request->streamForContinuation == frame.streamId) {
219 fr->type = FrameHeaders;
221 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
226 if (fr->type == FrameSettings) {
227 ret = parseSettings(request, io, frame);
228 }
else if (fr->type == FramePriority) {
229 ret = parsePriority(request, io, frame);
230 }
else if (fr->type == FrameHeaders) {
231 ret = parseHeaders(request, io, frame);
232 }
else if (fr->type == FramePing) {
233 ret = parsePing(request, io, frame);
234 }
else if (fr->type == FrameData) {
235 ret = parseData(request, io, frame);
236 }
else if (fr->type == FramePushPromise) {
238 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
240 }
else if (fr->type == FrameRstStream) {
241 ret = parseRstStream(request, io, frame);
242 }
else if (fr->type == FrameWindowUpdate) {
243 ret = parseWindowUpdate(request, io, frame);
244 }
else if (fr->type == FrameGoaway) {
245 sock->connectionClose();
247 }
else if (fr->type == FrameContinuation) {
248 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
251 qCDebug(C_SERVER_H2) <<
"Unknown frame type" << fr->type;
256 request->buf_size -= 9 + request->pktsize;
257 memmove(request->buffer,
258 request->buffer + 9 + request->pktsize,
259 size_t(request->buf_size));
266 sock->connectionClose();
269 qCWarning(C_SERVER_H2) <<
"Failed to read from socket" << io->errorString();
272 }
while (bytesAvailable);
283 if ((fr.flags & FlagSettingsAck && fr.len) || fr.len % 6) {
284 sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
286 }
else if (fr.streamId) {
287 sendGoAway(io, request->maxStreamId, ErrorProtocolError);
291 if (!(fr.flags & FlagSettingsAck)) {
292 QVector<std::pair<quint16, quint32>> settings;
294 while (request->pktsize > pos) {
295 quint16 identifier = net_be16(request->buffer + 9 + pos);
296 quint32 value = net_be32(request->buffer + 9 + 2 + pos);
297 settings.push_back({identifier, value});
300 if (identifier == SETTINGS_ENABLE_PUSH) {
302 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
305 request->canPush = value;
306 }
else if (identifier == SETTINGS_INITIAL_WINDOW_SIZE) {
307 if (value > 2147483647) {
308 return sendGoAway(io, request->maxStreamId, ErrorFlowControlError);
311 const qint32 difference = qint32(value) - request->settingsInitialWindowSize;
312 request->settingsInitialWindowSize = qint32(value);
314 for (
const auto &stream : std::as_const(request->streams)) {
315 stream->windowSize += difference;
316 stream->windowUpdated();
320 }
else if (identifier == SETTINGS_MAX_FRAME_SIZE) {
321 if (value < 16384 || value > 16777215) {
322 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
324 request->settingsMaxFrameSize = value;
336 if (fr.streamId == 0) {
337 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
340 quint8 padLength = 0;
341 if (fr.flags & FlagDataPadded) {
342 padLength = quint8(*(request->buffer + 9));
343 if (padLength >= fr.len) {
344 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
349 auto streamIt = request->streams.constFind(fr.streamId);
350 if (streamIt != request->streams.constEnd()) {
351 stream = streamIt.value();
353 if (stream->state == H2Stream::Idle) {
354 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
355 }
else if (stream->state == H2Stream::HalfClosed || stream->state == H2Stream::Closed) {
356 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
359 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
366 stream->
body = createBody(request->contentLength);
369 return sendGoAway(io, request->maxStreamId, ErrorInternalError);
372 stream->
body->write(request->buffer + 9, fr.len - padLength);
374 stream->consumedData += fr.len - padLength;
375 if (stream->contentLength != -1 &&
376 ((fr.flags & FlagDataEndStream && stream->contentLength != stream->consumedData) ||
377 (stream->contentLength > stream->consumedData))) {
378 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
381 if (fr.flags & FlagDataEndStream) {
382 queueStream(request->sock, stream);
391 if (fr.streamId == 0) {
392 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
394 if (fr.len > request->settingsMaxFrameSize) {
395 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
398 char *ptr = request->buffer + 9;
399 quint8 padLength = 0;
400 if (fr.flags & FlagHeadersPadded) {
401 padLength = quint8(*(ptr + pos));
402 if (padLength > fr.len) {
404 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
410 quint32 streamDependency = 0;
412 if (fr.flags & FlagHeadersPriority) {
414 streamDependency = net_be32(ptr + pos);
415 if (fr.streamId == streamDependency) {
417 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
427 auto streamIt = request->streams.constFind(fr.streamId);
428 if (streamIt != request->streams.constEnd()) {
429 stream = streamIt.value();
434 if (!(fr.flags & FlagHeadersEndStream) && stream->state == H2Stream::Open &&
435 request->streamForContinuation == 0) {
436 qCDebug(C_SERVER_H2) <<
"header FlagHeadersEndStream stream->headers.size()";
437 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
439 if (stream->state == H2Stream::HalfClosed && request->streamForContinuation == 0) {
440 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
442 if (stream->state == H2Stream::Closed) {
443 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
446 if (request->maxStreamId >= fr.streamId) {
448 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
450 request->maxStreamId = fr.streamId;
452 stream =
new H2Stream(fr.streamId, request->settingsInitialWindowSize, request);
456 request->streams.insert(fr.streamId, stream);
459 if (stream->state == H2Stream::Idle) {
460 stream->state = H2Stream::Open;
463 if (fr.flags & FlagHeadersEndStream) {
464 stream->state = H2Stream::HalfClosed;
467 if (!request->hpack) {
468 request->hpack =
new HPack(m_headerTableSize);
471 if (fr.flags & FlagHeadersEndHeaders) {
472 request->streamForContinuation = 0;
473 if (!request->headersBuffer.isEmpty()) {
474 request->headersBuffer.append(ptr, qint32(fr.len) - pos - padLength);
479 request->streamForContinuation = fr.streamId;
480 request->headersBuffer.append(ptr, qint32(fr.len) - pos - padLength);
486 if (request->headersBuffer.size()) {
487 it =
reinterpret_cast<quint8 *
>(request->headersBuffer.begin());
488 itEnd =
reinterpret_cast<quint8 *
>(request->headersBuffer.end());
490 it =
reinterpret_cast<quint8 *
>(ptr);
491 itEnd = it + fr.len - pos - padLength;
494 int ret = request->hpack->decode(it, itEnd, stream);
498 return sendGoAway(io, request->maxStreamId, quint32(ret));
504 if ((stream->state == H2Stream::HalfClosed || fr.flags & FlagHeadersEndStream) &&
505 request->streamForContinuation == 0) {
508 queueStream(request->sock, stream);
518 return sendGoAway(io, sock->maxStreamId, ErrorFrameSizeError);
519 }
else if (fr.streamId == 0) {
520 return sendGoAway(io, sock->maxStreamId, ErrorProtocolError);
524 while (fr.len > pos) {
526 quint32 exclusiveAndStreamDep = net_be32(sock->buffer + 9 + pos);
531 if (fr.streamId == exclusiveAndStreamDep) {
534 return sendGoAway(io, sock->maxStreamId, ErrorProtocolError);
548 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
549 }
else if (fr.streamId) {
550 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
553 if (!(fr.flags & FlagPingAck)) {
554 sendPing(io, FlagPingAck, request->buffer + 9, 8);
565 if (fr.streamId == 0) {
566 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
567 }
else if (request->pktsize != 4) {
568 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
572 auto streamIt = request->streams.constFind(fr.streamId);
573 if (streamIt != request->streams.constEnd()) {
574 stream = streamIt.value();
577 if (stream->state == H2Stream::Idle) {
578 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
582 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
585 stream->state = H2Stream::Closed;
598 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
601 quint32 windowSizeIncrement = net_be32(request->buffer + 9);
602 if (windowSizeIncrement == 0) {
603 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
611 auto streamIt = request->streams.constFind(fr.streamId);
612 if (streamIt != request->streams.constEnd()) {
613 stream = streamIt.value();
615 if (stream->state == H2Stream::Idle) {
616 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
619 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
622 const qint64 result = qint64(stream->windowSize) + windowSizeIncrement;
623 if (result > 2147483647) {
624 stream->state = H2Stream::Closed;
625 return sendRstStream(io, fr.streamId, ErrorFlowControlError);
627 stream->windowSize = qint32(result);
628 stream->windowUpdated();
631 const qint64 result = qint64(request->windowSize) + windowSizeIncrement;
632 if (result > 2147483647) {
633 return sendGoAway(io, request->maxStreamId, ErrorFlowControlError);
635 request->windowSize = qint32(result);
638 for (
const auto &stream : std::as_const(request->streams)) {
639 stream->windowUpdated();
647int ProtocolHttp2::sendGoAway(QIODevice *io, quint32 lastStreamId, quint32 error)
const
651 data.append(
char(lastStreamId >> 24));
652 data.append(
char(lastStreamId >> 16));
653 data.append(
char(lastStreamId >> 8));
654 data.append(
char(lastStreamId));
655 data.append(
char(error >> 24));
656 data.append(
char(error >> 16));
657 data.append(
char(error >> 8));
658 data.append(
char(error));
661 int ret = sendFrame(io, FrameGoaway, 0, 0, data.constData(), 8);
666int ProtocolHttp2::sendRstStream(QIODevice *io, quint32 streamId, quint32 error)
const
670 data.append(
char(error >> 24));
671 data.append(
char(error >> 16));
672 data.append(
char(error >> 8));
673 data.append(
char(error));
676 int ret = sendFrame(io, FrameRstStream, 0, streamId, data.constData(), 4);
681int ProtocolHttp2::sendSettings(QIODevice *io,
682 const std::vector<std::pair<quint16, quint32>> &settings)
const
685 for (
const std::pair<quint16, quint32> &pair : settings) {
686 data.append(
char(pair.first >> 8));
687 data.append(
char(pair.first));
688 data.append(
char(pair.second >> 24));
689 data.append(
char(pair.second >> 16));
690 data.append(
char(pair.second >> 8));
691 data.append(
char(pair.second));
694 return sendFrame(io, FrameSettings, 0, 0, data.constData(), data.length());
697int ProtocolHttp2::sendSettingsAck(QIODevice *io)
const
699 return sendFrame(io, FrameSettings, FlagSettingsAck);
702int ProtocolHttp2::sendPing(QIODevice *io, quint8 flags,
const char *data, qint32 dataLen)
const
704 return sendFrame(io, FramePing, flags, 0, data, dataLen);
707int ProtocolHttp2::sendData(QIODevice *io,
711 qint32 dataLen)
const
713 if (windowSize < 1) {
717 if (windowSize < dataLen) {
720 while (i < dataLen) {
721 int ret = sendFrame(io, FrameData, flags, streamId, data + i, windowSize);
726 if ((i + 1) == dataLen) {
727 flags = FlagDataEndStream;
732 return sendFrame(io, FrameData, FlagDataEndStream, streamId, data, dataLen);
736int ProtocolHttp2::sendFrame(QIODevice *io,
741 qint32 dataLen)
const
745 fr.size2 = quint8(dataLen >> 16);
746 fr.size1 = quint8(dataLen >> 8);
747 fr.size0 = quint8(dataLen);
750 fr.rbit_stream_id3 = quint8(streamId >> 24);
751 fr.rbit_stream_id2 = quint8(streamId >> 16);
752 fr.rbit_stream_id1 = quint8(streamId >> 8);
753 fr.rbit_stream_id0 = quint8(streamId);
763 if (io->write(
reinterpret_cast<const char *
>(&fr),
sizeof(
struct h2_frame)) !=
764 sizeof(
struct h2_frame)) {
768 if (dataLen && io->write(data, dataLen) != dataLen) {
774void ProtocolHttp2::queueStream(
Socket *socket,
H2Stream *stream)
const
776 ++socket->processing;
778 stream->
body->seek(0);
783bool ProtocolHttp2::upgradeH2C(
Socket *socket,
788 if (headers.
header(
"Upgrade").compare(
"h2c") == 0 &&
789 headers.
connection().compare(
"Upgrade, HTTP2-Settings") == 0) {
790 const auto settings = headers.
header(
"Http2-Settings");
791 if (!settings.isEmpty()) {
792 io->write(
"HTTP/1.1 101 Switching Protocols\r\n"
793 "Connection: Upgrade\r\n"
794 "Upgrade: h2c\r\n\r\n");
795 socket->proto =
this;
797 protoRequest->upgradedFrom = socket->protoData;
798 socket->protoData = protoRequest;
800 protoRequest->hpack =
new HPack(m_headerTableSize);
801 protoRequest->maxStreamId = 1;
803 auto stream =
new H2Stream(1, 65535, protoRequest);
813 stream->state = H2Stream::HalfClosed;
814 protoRequest->streams.insert(1, stream);
815 protoRequest->maxStreamId = 1;
819 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
820 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
824 queueStream(socket, stream);
825 qCDebug(C_SERVER_H2) <<
"upgraded";
837ProtoRequestHttp2::~ProtoRequestHttp2()
841void ProtoRequestHttp2::setupNewConnection(
Socket *sock)
846H2Stream::H2Stream(quint32 _streamId, qint32 _initialWindowSize,
ProtoRequestHttp2 *protoRequestH2)
847 : protoRequest(protoRequestH2)
848 , streamId(_streamId)
849 , windowSize(_initialWindowSize)
851 protocol =
"HTTP/2"_ba;
852 serverAddress = protoRequestH2->sock->serverAddress;
853 remoteAddress = protoRequestH2->sock->remoteAddress;
854 remotePort = protoRequestH2->sock->remotePort;
855 isSecure = protoRequestH2->sock->isSecure;
869 auto parser =
dynamic_cast<ProtocolHttp2 *
>(protoRequest->sock->proto);
871 qint64 remainingData = len;
873 while (remainingData > 0 && state != H2Stream::Closed) {
874 qint64 availableWindowSize = qMin(windowSize, protoRequest->windowSize);
879 availableWindowSize =
880 qMin(availableWindowSize, (qint64) protoRequest->settingsMaxFrameSize);
881 if (availableWindowSize == 0) {
883 loop =
new QEventLoop;
885 if (loop->exec() == 0) {
895 if (availableWindowSize > remainingData) {
896 ret = parser->sendFrame(protoRequest->io,
901 qint32(remainingData));
903 protoRequest->windowSize -= remainingData;
904 windowSize -= remainingData;
905 sent += remainingData;
907 ret = parser->sendFrame(protoRequest->io,
912 qint32(availableWindowSize));
913 remainingData -= availableWindowSize;
914 protoRequest->windowSize -= availableWindowSize;
915 windowSize -= availableWindowSize;
916 sent += availableWindowSize;
922 return ret == 0 ? len : -1;
928 protoRequest->hpack->encodeHeaders(
931 auto parser =
dynamic_cast<ProtocolHttp2 *
>(protoRequest->sock->proto);
933 int ret = parser->sendFrame(protoRequest->io,
935 FlagHeadersEndHeaders,
946 protoRequest->streams.remove(streamId);
947 protoRequest->sock->requestFinished();
951void H2Stream::windowUpdated()
956 if (protoRequest->windowSize > 0 && windowSize > 0 && loop && loop->isRunning()) {
961#include "moc_protocolhttp2.cpp"
TimePointSteady startOfRequest
void processRequestAsync(Cutelyst::EngineRequest *request)
qint64 doWrite(const char *data, qint64 len) override final
void processingFinished() override final
bool writeHeaders(quint16 status, const Cutelyst::Headers &headers) override final
The Cutelyst namespace holds all public Cutelyst API.