5#include "protocolhttp2.h"
12#include <QLoggingCategory>
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 "
109 m_maxFrameSize = quint32(m_bufferSize - 9);
112ProtocolHttp2::~ProtocolHttp2()
116Protocol::Type ProtocolHttp2::type()
const
118 return Protocol::Type::Http2;
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"
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)) {
294 while (request->pktsize > pos) {
295 quint16 identifier = net_be16(request->buffer + 9 + pos);
296 quint32 value = net_be32(request->buffer + 9 + 2 + pos);
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));
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));
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;
783bool ProtocolHttp2::upgradeH2C(
Socket *socket,
790 const auto settings = headers.
header(
"Http2-Settings");
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) {
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.
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
int exec(QEventLoop::ProcessEventsFlags flags)
void exit(int returnCode)
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)
bool isEmpty() const const
void push_back(QList::parameter_type value)