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 *server)
98 , m_headerTableSize(qint32(server->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
123 auto request =
static_cast<ProtoRequestHttp2 *
>(sock->protoData);
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);
277 return new ProtoRequestHttp2(sock, m_bufferSize);
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);
411 if (fr.flags & FlagHeadersPriority) {
413 quint32 streamDependency = net_be32(ptr + pos);
414 if (fr.streamId == streamDependency) {
416 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
426 auto streamIt = request->streams.constFind(fr.streamId);
427 if (streamIt != request->streams.constEnd()) {
428 stream = streamIt.value();
433 if (!(fr.flags & FlagHeadersEndStream) && stream->state == H2Stream::Open &&
434 request->streamForContinuation == 0) {
435 qCDebug(C_SERVER_H2) <<
"header FlagHeadersEndStream stream->headers.size()";
436 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
438 if (stream->state == H2Stream::HalfClosed && request->streamForContinuation == 0) {
439 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
441 if (stream->state == H2Stream::Closed) {
442 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
445 if (request->maxStreamId >= fr.streamId) {
447 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
449 request->maxStreamId = fr.streamId;
451 stream =
new H2Stream(fr.streamId, request->settingsInitialWindowSize, request);
455 request->streams.insert(fr.streamId, stream);
458 if (stream->state == H2Stream::Idle) {
459 stream->state = H2Stream::Open;
462 if (fr.flags & FlagHeadersEndStream) {
463 stream->state = H2Stream::HalfClosed;
466 if (!request->hpack) {
467 request->hpack =
new HPack(m_headerTableSize);
470 if (fr.flags & FlagHeadersEndHeaders) {
471 request->streamForContinuation = 0;
472 if (!request->headersBuffer.isEmpty()) {
473 request->headersBuffer.append(ptr, qint32(fr.len) - pos - padLength);
478 request->streamForContinuation = fr.streamId;
479 request->headersBuffer.append(ptr, qint32(fr.len) - pos - padLength);
485 if (request->headersBuffer.size()) {
486 it =
reinterpret_cast<quint8 *
>(request->headersBuffer.begin());
487 itEnd =
reinterpret_cast<quint8 *
>(request->headersBuffer.end());
489 it =
reinterpret_cast<quint8 *
>(ptr);
490 itEnd = it + fr.len - pos - padLength;
493 int ret = request->hpack->decode(it, itEnd, stream);
497 return sendGoAway(io, request->maxStreamId, quint32(ret));
503 if ((stream->state == H2Stream::HalfClosed || fr.flags & FlagHeadersEndStream) &&
504 request->streamForContinuation == 0) {
507 queueStream(request->sock, stream);
519 return sendGoAway(io, sock->maxStreamId, ErrorFrameSizeError);
520 }
else if (fr.streamId == 0) {
521 return sendGoAway(io, sock->maxStreamId, ErrorProtocolError);
525 while (fr.len > pos) {
527 quint32 exclusiveAndStreamDep = net_be32(sock->buffer + 9 + pos);
532 if (fr.streamId == exclusiveAndStreamDep) {
535 return sendGoAway(io, sock->maxStreamId, ErrorProtocolError);
551 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
552 }
else if (fr.streamId) {
553 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
556 if (!(fr.flags & FlagPingAck)) {
557 sendPing(io, FlagPingAck, request->buffer + 9, 8);
568 if (fr.streamId == 0) {
569 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
570 }
else if (request->pktsize != 4) {
571 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
575 auto streamIt = request->streams.constFind(fr.streamId);
576 if (streamIt != request->streams.constEnd()) {
577 stream = streamIt.value();
580 if (stream->state == H2Stream::Idle) {
581 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
585 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
588 stream->state = H2Stream::Closed;
601 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
604 quint32 windowSizeIncrement = net_be32(request->buffer + 9);
605 if (windowSizeIncrement == 0) {
606 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
614 auto streamIt = request->streams.constFind(fr.streamId);
615 if (streamIt != request->streams.constEnd()) {
616 stream = streamIt.value();
618 if (stream->state == H2Stream::Idle) {
619 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
622 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
625 const qint64 result = qint64(stream->windowSize) + windowSizeIncrement;
626 if (result > 2147483647) {
627 stream->state = H2Stream::Closed;
628 return sendRstStream(io, fr.streamId, ErrorFlowControlError);
630 stream->windowSize = qint32(result);
631 stream->windowUpdated();
634 const qint64 result = qint64(request->windowSize) + windowSizeIncrement;
635 if (result > 2147483647) {
636 return sendGoAway(io, request->maxStreamId, ErrorFlowControlError);
638 request->windowSize = qint32(result);
641 for (
const auto &stream : std::as_const(request->streams)) {
642 stream->windowUpdated();
650int ProtocolHttp2::sendGoAway(QIODevice *io, quint32 lastStreamId, quint32 error)
const
654 data.append(
char(lastStreamId >> 24));
655 data.append(
char(lastStreamId >> 16));
656 data.append(
char(lastStreamId >> 8));
657 data.append(
char(lastStreamId));
658 data.append(
char(error >> 24));
659 data.append(
char(error >> 16));
660 data.append(
char(error >> 8));
661 data.append(
char(error));
664 int ret = sendFrame(io, FrameGoaway, 0, 0, data.constData(), 8);
669int ProtocolHttp2::sendRstStream(QIODevice *io, quint32 streamId, quint32 error)
const
673 data.append(
char(error >> 24));
674 data.append(
char(error >> 16));
675 data.append(
char(error >> 8));
676 data.append(
char(error));
679 int ret = sendFrame(io, FrameRstStream, 0, streamId, data.constData(), 4);
684int ProtocolHttp2::sendSettings(QIODevice *io,
685 const std::vector<std::pair<quint16, quint32>> &settings)
const
688 for (
const std::pair<quint16, quint32> &pair : settings) {
689 data.append(
char(pair.first >> 8));
690 data.append(
char(pair.first));
691 data.append(
char(pair.second >> 24));
692 data.append(
char(pair.second >> 16));
693 data.append(
char(pair.second >> 8));
694 data.append(
char(pair.second));
697 return sendFrame(io, FrameSettings, 0, 0, data.constData(), data.length());
700int ProtocolHttp2::sendSettingsAck(QIODevice *io)
const
702 return sendFrame(io, FrameSettings, FlagSettingsAck);
705int ProtocolHttp2::sendPing(QIODevice *io, quint8 flags,
const char *data, qint32 dataLen)
const
707 return sendFrame(io, FramePing, flags, 0, data, dataLen);
710int ProtocolHttp2::sendData(QIODevice *io,
714 qint32 dataLen)
const
716 if (windowSize < 1) {
720 if (windowSize < dataLen) {
723 while (i < dataLen) {
724 int ret = sendFrame(io, FrameData, flags, streamId, data + i, windowSize);
729 if ((i + 1) == dataLen) {
730 flags = FlagDataEndStream;
735 return sendFrame(io, FrameData, FlagDataEndStream, streamId, data, dataLen);
739int ProtocolHttp2::sendFrame(QIODevice *io,
744 qint32 dataLen)
const
748 fr.size2 = quint8(dataLen >> 16);
749 fr.size1 = quint8(dataLen >> 8);
750 fr.size0 = quint8(dataLen);
753 fr.rbit_stream_id3 = quint8(streamId >> 24);
754 fr.rbit_stream_id2 = quint8(streamId >> 16);
755 fr.rbit_stream_id1 = quint8(streamId >> 8);
756 fr.rbit_stream_id0 = quint8(streamId);
766 if (io->write(
reinterpret_cast<const char *
>(&fr),
sizeof(
struct h2_frame)) !=
767 sizeof(
struct h2_frame)) {
771 if (dataLen && io->write(data, dataLen) != dataLen) {
777void ProtocolHttp2::queueStream(
Socket *socket,
H2Stream *stream)
const
779 ++socket->processing;
781 stream->
body->seek(0);
786bool ProtocolHttp2::upgradeH2C(
Socket *socket,
788 const Cutelyst::EngineRequest &request)
790 const Cutelyst::Headers &headers = request.
headers;
791 if (headers.
header(
"Upgrade").compare(
"h2c") == 0 &&
792 headers.
connection().compare(
"Upgrade, HTTP2-Settings") == 0) {
793 const auto settings = headers.
header(
"Http2-Settings");
794 if (!settings.isEmpty()) {
795 io->write(
"HTTP/1.1 101 Switching Protocols\r\n"
796 "Connection: Upgrade\r\n"
797 "Upgrade: h2c\r\n\r\n");
798 socket->proto =
this;
799 auto protoRequest =
new ProtoRequestHttp2(socket, m_bufferSize);
800 protoRequest->upgradedFrom = socket->protoData;
801 socket->protoData = protoRequest;
803 protoRequest->hpack =
new HPack(m_headerTableSize);
804 protoRequest->maxStreamId = 1;
806 auto stream =
new H2Stream(1, 65535, protoRequest);
816 stream->state = H2Stream::HalfClosed;
817 protoRequest->streams.insert(1, stream);
818 protoRequest->maxStreamId = 1;
822 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
823 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
827 queueStream(socket, stream);
828 qCDebug(C_SERVER_H2) <<
"upgraded";
835ProtoRequestHttp2::ProtoRequestHttp2(Cutelyst::Socket *sock,
int bufferSize)
840ProtoRequestHttp2::~ProtoRequestHttp2()
844void ProtoRequestHttp2::setupNewConnection(
Socket *sock)
849H2Stream::H2Stream(quint32 _streamId, qint32 _initialWindowSize,
ProtoRequestHttp2 *protoRequestH2)
850 : protoRequest(protoRequestH2)
851 , streamId(_streamId)
852 , windowSize(_initialWindowSize)
854 protocol =
"HTTP/2"_ba;
855 serverAddress = protoRequestH2->sock->serverAddress;
856 remoteAddress = protoRequestH2->sock->remoteAddress;
857 remotePort = protoRequestH2->sock->remotePort;
858 isSecure = protoRequestH2->sock->isSecure;
872 auto parser =
dynamic_cast<ProtocolHttp2 *
>(protoRequest->sock->proto);
874 qint64 remainingData = len;
876 while (remainingData > 0 && state != H2Stream::Closed) {
877 qint64 availableWindowSize = qMin(windowSize, protoRequest->windowSize);
882 availableWindowSize =
883 qMin(availableWindowSize, (qint64) protoRequest->settingsMaxFrameSize);
884 if (availableWindowSize == 0) {
886 loop =
new QEventLoop;
888 if (loop->exec() == 0) {
898 if (availableWindowSize > remainingData) {
899 ret = parser->sendFrame(protoRequest->io,
904 qint32(remainingData));
906 protoRequest->windowSize -= remainingData;
907 windowSize -= remainingData;
908 sent += remainingData;
910 ret = parser->sendFrame(protoRequest->io,
915 qint32(availableWindowSize));
916 remainingData -= availableWindowSize;
917 protoRequest->windowSize -= availableWindowSize;
918 windowSize -= availableWindowSize;
919 sent += availableWindowSize;
925 return ret == 0 ? len : -1;
931 protoRequest->hpack->encodeHeaders(
934 auto parser =
dynamic_cast<ProtocolHttp2 *
>(protoRequest->sock->proto);
936 int ret = parser->sendFrame(protoRequest->io,
938 FlagHeadersEndHeaders,
949 protoRequest->streams.remove(streamId);
950 protoRequest->sock->requestFinished();
954void H2Stream::windowUpdated()
959 if (protoRequest->windowSize > 0 && windowSize > 0 && loop && loop->isRunning()) {
964#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.