5#include "protocolhttp2.h"
12#include <QLoggingCategory>
15using namespace Qt::Literals::StringLiterals;
17Q_LOGGING_CATEGORY(C_SERVER_H2,
"cutelyst.server.http2", QtWarningMsg)
25 quint8 rbit_stream_id3;
26 quint8 rbit_stream_id2;
27 quint8 rbit_stream_id1;
28 quint8 rbit_stream_id0;
31enum SettingsFlags { FlagSettingsAck = 0x1 };
33enum PingFlags { FlagPingAck = 0x1 };
36 FlagHeadersEndStream = 0x1,
37 FlagHeadersEndHeaders = 0x4,
38 FlagHeadersPadded = 0x8,
39 FlagHeadersPriority = 0x20,
42enum PushPromiseFlags {
43 FlagPushPromiseEndHeaders = 0x4,
44 FlagPushPromisePadded = 0x8,
48 FlagDataEndStream = 0x1,
58 FramePushPromise = 0x5,
61 FrameWindowUpdate = 0x8,
62 FrameContinuation = 0x9
67 ErrorProtocolError = 0x1,
68 ErrorInternalError = 0x2,
69 ErrorFlowControlError = 0x3,
70 ErrorSettingsTimeout = 0x4,
71 ErrorStreamClosed = 0x5,
72 ErrorFrameSizeError = 0x6,
73 ErrorRefusedStream = 0x7,
75 ErrorCompressionError = 0x9,
76 ErrorConnectError = 0xA,
77 ErrorEnhanceYourCalm = 0xB,
78 ErrorInadequateSecurity = 0xC,
79 ErrorHttp11Required = 0xD
83 SETTINGS_HEADER_TABLE_SIZE = 0x1,
84 SETTINGS_ENABLE_PUSH = 0x2,
85 SETTINGS_MAX_CONCURRENT_STREAMS = 0x3,
86 SETTINGS_INITIAL_WINDOW_SIZE = 0x4,
87 SETTINGS_MAX_FRAME_SIZE = 0x5,
88 SETTINGS_MAX_HEADER_LIST_SIZE = 0x6,
89 SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x8,
92#define PREFACE_SIZE 24
94ProtocolHttp2::ProtocolHttp2(
Server *wsgi)
96 , m_headerTableSize(qint32(wsgi->http2HeaderTableSize()))
98 m_bufferSize = qMin(m_bufferSize, 2147483647);
101 if (m_bufferSize < 16393) {
102 qFatal(
"HTTP/2 Protocol requires that buffer-size to be at least '16393' in size, current "
104 QByteArray::number(m_bufferSize).constData());
107 m_maxFrameSize = quint32(m_bufferSize - 9);
110ProtocolHttp2::~ProtocolHttp2()
114Protocol::Type ProtocolHttp2::type()
const
116 return Protocol::Type::Http2;
119void ProtocolHttp2::parse(
Socket *sock, QIODevice *io)
const
121 auto request =
static_cast<ProtoRequestHttp2 *
>(sock->protoData);
123 qint64 bytesAvailable = io->bytesAvailable();
129 io->read(request->buffer + request->buf_size, m_bufferSize - request->buf_size);
130 bytesAvailable -= len;
133 request->buf_size += len;
135 while (request->buf_size && ret == 0) {
139 if (request->connState == ProtoRequestHttp2::MethodLine) {
140 if (request->buf_size >= PREFACE_SIZE) {
141 if (memcmp(request->buffer,
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0) {
144 request->buf_size -= PREFACE_SIZE;
145 memmove(request->buffer,
146 request->buffer + PREFACE_SIZE,
147 size_t(request->buf_size));
148 request->connState = ProtoRequestHttp2::H2Frames;
152 {SETTINGS_ENABLE_CONNECT_PROTOCOL, 0},
153 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
154 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
157 qCDebug(C_SERVER_H2) <<
"Protocol Error: Invalid connection preface"
158 << sock->remoteAddress.toString();
162 sock->connectionClose();
170 }
else if (request->connState == ProtoRequestHttp2::H2Frames) {
171 if (request->buf_size >=
int(
sizeof(
struct h2_frame))) {
172 auto fr =
reinterpret_cast<struct h2_frame *
>(request->buffer);
174 frame.len = quint32(fr->size0 | (fr->size1 << 8) | (fr->size2 << 16));
176 quint32(fr->rbit_stream_id0 | (fr->rbit_stream_id1 << 8) |
177 (fr->rbit_stream_id2 << 16) |
178 ((fr->rbit_stream_id3 & ~0x80) << 24));
179 frame.type = fr->type;
180 frame.flags = fr->flags;
182 quint32(fr->size0 | (fr->size1 << 8) | (fr->size2 << 16));
184 quint32(fr->rbit_stream_id0 | (fr->rbit_stream_id1 << 8) |
185 (fr->rbit_stream_id2 << 16) |
186 ((fr->rbit_stream_id3 & ~0x80) << 24));
195 if (frame.streamId && !(frame.streamId & 1)) {
196 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
200 if (request->pktsize > m_maxFrameSize) {
203 ret = sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
207 if (request->pktsize >
208 (quint32(request->buf_size) -
sizeof(
struct h2_frame))) {
214 if (request->streamForContinuation) {
215 if (fr->type == FrameContinuation &&
216 request->streamForContinuation == frame.streamId) {
217 fr->type = FrameHeaders;
219 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
224 if (fr->type == FrameSettings) {
225 ret = parseSettings(request, io, frame);
226 }
else if (fr->type == FramePriority) {
227 ret = parsePriority(request, io, frame);
228 }
else if (fr->type == FrameHeaders) {
229 ret = parseHeaders(request, io, frame);
230 }
else if (fr->type == FramePing) {
231 ret = parsePing(request, io, frame);
232 }
else if (fr->type == FrameData) {
233 ret = parseData(request, io, frame);
234 }
else if (fr->type == FramePushPromise) {
236 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
238 }
else if (fr->type == FrameRstStream) {
239 ret = parseRstStream(request, io, frame);
240 }
else if (fr->type == FrameWindowUpdate) {
241 ret = parseWindowUpdate(request, io, frame);
242 }
else if (fr->type == FrameGoaway) {
243 sock->connectionClose();
245 }
else if (fr->type == FrameContinuation) {
246 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
249 qCDebug(C_SERVER_H2) <<
"Unknown frame type" << fr->type;
254 request->buf_size -= 9 + request->pktsize;
255 memmove(request->buffer,
256 request->buffer + 9 + request->pktsize,
257 size_t(request->buf_size));
264 sock->connectionClose();
267 qCWarning(C_SERVER_H2) <<
"Failed to read from socket" << io->errorString();
270 }
while (bytesAvailable);
275 return new ProtoRequestHttp2(sock, m_bufferSize);
281 if ((fr.flags & FlagSettingsAck && fr.len) || fr.len % 6) {
282 sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
284 }
else if (fr.streamId) {
285 sendGoAway(io, request->maxStreamId, ErrorProtocolError);
289 if (!(fr.flags & FlagSettingsAck)) {
290 QVector<std::pair<quint16, quint32>> settings;
292 while (request->pktsize > pos) {
293 quint16 identifier = net_be16(request->buffer + 9 + pos);
294 quint32 value = net_be32(request->buffer + 9 + 2 + pos);
295 settings.push_back({identifier, value});
298 if (identifier == SETTINGS_ENABLE_PUSH) {
300 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
303 request->canPush = value;
304 }
else if (identifier == SETTINGS_INITIAL_WINDOW_SIZE) {
305 if (value > 2147483647) {
306 return sendGoAway(io, request->maxStreamId, ErrorFlowControlError);
309 const qint32 difference = qint32(value) - request->settingsInitialWindowSize;
310 request->settingsInitialWindowSize = qint32(value);
312 auto it = request->streams.begin();
313 while (it != request->streams.end()) {
314 (*it)->windowSize += difference;
315 (*it)->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 auto streamIt = request->streams.constBegin();
639 if (streamIt != request->streams.constEnd()) {
640 (*streamIt)->windowUpdated();
649int ProtocolHttp2::sendGoAway(QIODevice *io, quint32 lastStreamId, quint32 error)
const
653 data.append(
char(lastStreamId >> 24));
654 data.append(
char(lastStreamId >> 16));
655 data.append(
char(lastStreamId >> 8));
656 data.append(
char(lastStreamId));
657 data.append(
char(error >> 24));
658 data.append(
char(error >> 16));
659 data.append(
char(error >> 8));
660 data.append(
char(error));
663 int ret = sendFrame(io, FrameGoaway, 0, 0, data.constData(), 8);
668int ProtocolHttp2::sendRstStream(QIODevice *io, quint32 streamId, quint32 error)
const
672 data.append(
char(error >> 24));
673 data.append(
char(error >> 16));
674 data.append(
char(error >> 8));
675 data.append(
char(error));
678 int ret = sendFrame(io, FrameRstStream, 0, streamId, data.constData(), 4);
683int ProtocolHttp2::sendSettings(QIODevice *io,
684 const std::vector<std::pair<quint16, quint32>> &settings)
const
687 for (
const std::pair<quint16, quint32> &pair : settings) {
688 data.append(
char(pair.first >> 8));
689 data.append(
char(pair.first));
690 data.append(
char(pair.second >> 24));
691 data.append(
char(pair.second >> 16));
692 data.append(
char(pair.second >> 8));
693 data.append(
char(pair.second));
696 return sendFrame(io, FrameSettings, 0, 0, data.constData(), data.length());
699int ProtocolHttp2::sendSettingsAck(QIODevice *io)
const
701 return sendFrame(io, FrameSettings, FlagSettingsAck);
704int ProtocolHttp2::sendPing(QIODevice *io, quint8 flags,
const char *data, qint32 dataLen)
const
706 return sendFrame(io, FramePing, flags, 0, data, dataLen);
709int ProtocolHttp2::sendData(QIODevice *io,
713 qint32 dataLen)
const
715 if (windowSize < 1) {
719 if (windowSize < dataLen) {
722 while (i < dataLen) {
723 int ret = sendFrame(io, FrameData, flags, streamId, data + i, windowSize);
728 if ((i + 1) == dataLen) {
729 flags = FlagDataEndStream;
734 return sendFrame(io, FrameData, FlagDataEndStream, streamId, data, dataLen);
738int ProtocolHttp2::sendFrame(QIODevice *io,
743 qint32 dataLen)
const
747 fr.size2 = quint8(dataLen >> 16);
748 fr.size1 = quint8(dataLen >> 8);
749 fr.size0 = quint8(dataLen);
752 fr.rbit_stream_id3 = quint8(streamId >> 24);
753 fr.rbit_stream_id2 = quint8(streamId >> 16);
754 fr.rbit_stream_id1 = quint8(streamId >> 8);
755 fr.rbit_stream_id0 = quint8(streamId);
765 if (io->write(
reinterpret_cast<const char *
>(&fr),
sizeof(
struct h2_frame)) !=
766 sizeof(
struct h2_frame)) {
770 if (dataLen && io->write(data, dataLen) != dataLen) {
776void ProtocolHttp2::queueStream(
Socket *socket,
H2Stream *stream)
const
778 ++socket->processing;
780 stream->
body->seek(0);
785bool ProtocolHttp2::upgradeH2C(
Socket *socket,
787 const Cutelyst::EngineRequest &request)
789 const Cutelyst::Headers &headers = request.
headers;
790 if (headers.
header(
"Upgrade").compare(
"h2c") == 0 &&
791 headers.
connection().compare(
"Upgrade, HTTP2-Settings") == 0) {
792 const auto settings = headers.
header(
"Http2-Settings");
793 if (!settings.isEmpty()) {
794 io->write(
"HTTP/1.1 101 Switching Protocols\r\n"
795 "Connection: Upgrade\r\n"
796 "Upgrade: h2c\r\n\r\n");
797 socket->proto =
this;
798 auto protoRequest =
new ProtoRequestHttp2(socket, m_bufferSize);
799 protoRequest->upgradedFrom = socket->protoData;
800 socket->protoData = protoRequest;
802 protoRequest->hpack =
new HPack(m_headerTableSize);
803 protoRequest->maxStreamId = 1;
805 auto stream =
new H2Stream(1, 65535, protoRequest);
815 stream->state = H2Stream::HalfClosed;
816 protoRequest->streams.insert(1, stream);
817 protoRequest->maxStreamId = 1;
821 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
822 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
826 queueStream(socket, stream);
827 qCDebug(C_SERVER_H2) <<
"upgraded";
834ProtoRequestHttp2::ProtoRequestHttp2(Cutelyst::Socket *sock,
int bufferSize)
839ProtoRequestHttp2::~ProtoRequestHttp2()
843void ProtoRequestHttp2::setupNewConnection(
Socket *sock)
848H2Stream::H2Stream(quint32 _streamId, qint32 _initialWindowSize,
ProtoRequestHttp2 *protoRequestH2)
849 : protoRequest(protoRequestH2)
850 , streamId(_streamId)
851 , windowSize(_initialWindowSize)
853 protocol =
"HTTP/2"_ba;
854 serverAddress = protoRequestH2->sock->serverAddress;
855 remoteAddress = protoRequestH2->sock->remoteAddress;
856 remotePort = protoRequestH2->sock->remotePort;
857 isSecure = protoRequestH2->sock->isSecure;
871 auto parser =
dynamic_cast<ProtocolHttp2 *
>(protoRequest->sock->proto);
873 qint64 remainingData = len;
875 while (remainingData > 0 && state != H2Stream::Closed) {
876 qint64 availableWindowSize = qMin(windowSize, protoRequest->windowSize);
881 availableWindowSize =
882 qMin(availableWindowSize, (qint64) protoRequest->settingsMaxFrameSize);
883 if (availableWindowSize == 0) {
885 loop =
new QEventLoop;
887 if (loop->exec() == 0) {
897 if (availableWindowSize > remainingData) {
898 ret = parser->sendFrame(protoRequest->io,
903 qint32(remainingData));
905 protoRequest->windowSize -= remainingData;
906 windowSize -= remainingData;
907 sent += remainingData;
909 ret = parser->sendFrame(protoRequest->io,
914 qint32(availableWindowSize));
915 remainingData -= availableWindowSize;
916 protoRequest->windowSize -= availableWindowSize;
917 windowSize -= availableWindowSize;
918 sent += availableWindowSize;
924 return ret == 0 ? len : -1;
930 protoRequest->hpack->encodeHeaders(
933 auto parser =
dynamic_cast<ProtocolHttp2 *
>(protoRequest->sock->proto);
935 int ret = parser->sendFrame(protoRequest->io,
937 FlagHeadersEndHeaders,
948 protoRequest->streams.remove(streamId);
949 protoRequest->sock->requestFinished();
953void H2Stream::windowUpdated()
958 if (protoRequest->windowSize > 0 && windowSize > 0 && loop && loop->isRunning()) {
963#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.