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 *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 "
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);
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));
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));
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;
786bool ProtocolHttp2::upgradeH2C(
Socket *socket,
793 const auto settings = headers.
header(
"Http2-Settings");
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;
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";
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) {
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.
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)