5#include "protocolhttp2.h"
12#include <QLoggingCategory>
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 "
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);
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"
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);
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));
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));
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;
785bool ProtocolHttp2::upgradeH2C(
Socket *socket,
787 const Cutelyst::EngineRequest &request)
789 const Cutelyst::Headers &headers = request.
headers;
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) {
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.
QByteArray & append(QByteArrayView data)
QByteArray::iterator begin()
int compare(QByteArrayView bv, Qt::CaseSensitivity cs) const const
const char * constData() const const
QByteArray::iterator end()
bool isEmpty() const const
qsizetype length() const const
QByteArray number(double n, char format, int precision)
qsizetype size() const const
bool isRunning() const const
QString toString() const const
virtual qint64 bytesAvailable() const const
QString errorString() const const
QByteArray read(qint64 maxSize)
virtual bool seek(qint64 pos)
qint64 write(const QByteArray &data)
void push_back(QList< T >::parameter_type value)