5#include "protocolhttp2.h"
12#include <QLoggingCategory>
16Q_LOGGING_CATEGORY(C_SERVER_H2,
"cutelyst.server.http2", QtWarningMsg)
24 quint8 rbit_stream_id3;
25 quint8 rbit_stream_id2;
26 quint8 rbit_stream_id1;
27 quint8 rbit_stream_id0;
30enum SettingsFlags { FlagSettingsAck = 0x1 };
32enum PingFlags { FlagPingAck = 0x1 };
35 FlagHeadersEndStream = 0x1,
36 FlagHeadersEndHeaders = 0x4,
37 FlagHeadersPadded = 0x8,
38 FlagHeadersPriority = 0x20,
41enum PushPromiseFlags {
42 FlagPushPromiseEndHeaders = 0x4,
43 FlagPushPromisePadded = 0x8,
47 FlagDataEndStream = 0x1,
57 FramePushPromise = 0x5,
60 FrameWindowUpdate = 0x8,
61 FrameContinuation = 0x9
66 ErrorProtocolError = 0x1,
67 ErrorInternalError = 0x2,
68 ErrorFlowControlError = 0x3,
69 ErrorSettingsTimeout = 0x4,
70 ErrorStreamClosed = 0x5,
71 ErrorFrameSizeError = 0x6,
72 ErrorRefusedStream = 0x7,
74 ErrorCompressionError = 0x9,
75 ErrorConnectError = 0xA,
76 ErrorEnhanceYourCalm = 0xB,
77 ErrorInadequateSecurity = 0xC,
78 ErrorHttp11Required = 0xD
82 SETTINGS_HEADER_TABLE_SIZE = 0x1,
83 SETTINGS_ENABLE_PUSH = 0x2,
84 SETTINGS_MAX_CONCURRENT_STREAMS = 0x3,
85 SETTINGS_INITIAL_WINDOW_SIZE = 0x4,
86 SETTINGS_MAX_FRAME_SIZE = 0x5,
87 SETTINGS_MAX_HEADER_LIST_SIZE = 0x6,
88 SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x8,
91#define PREFACE_SIZE 24
93ProtocolHttp2::ProtocolHttp2(
Server *wsgi)
95 , m_headerTableSize(qint32(wsgi->http2HeaderTableSize()))
97 m_bufferSize = qMin(m_bufferSize, 2147483647);
100 if (m_bufferSize < 16393) {
101 qFatal(
"HTTP/2 Protocol requires that buffer-size to be at least '16393' in size, current "
106 m_maxFrameSize = quint32(m_bufferSize - 9);
109ProtocolHttp2::~ProtocolHttp2()
113Protocol::Type ProtocolHttp2::type()
const
115 return Protocol::Type::Http2;
128 io->
read(request->buffer + request->buf_size, m_bufferSize - request->buf_size);
129 bytesAvailable -= len;
132 request->buf_size += len;
134 while (request->buf_size && ret == 0) {
138 if (request->connState == ProtoRequestHttp2::MethodLine) {
139 if (request->buf_size >= PREFACE_SIZE) {
140 if (memcmp(request->buffer,
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0) {
143 request->buf_size -= PREFACE_SIZE;
144 memmove(request->buffer,
145 request->buffer + PREFACE_SIZE,
146 size_t(request->buf_size));
147 request->connState = ProtoRequestHttp2::H2Frames;
151 {SETTINGS_ENABLE_CONNECT_PROTOCOL, 0},
152 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
153 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
156 qCDebug(C_SERVER_H2) <<
"Protocol Error: Invalid connection preface"
161 sock->connectionClose();
169 }
else if (request->connState == ProtoRequestHttp2::H2Frames) {
170 if (request->buf_size >=
int(
sizeof(
struct h2_frame))) {
171 auto fr =
reinterpret_cast<struct
h2_frame *
>(request->buffer);
173 frame.len = quint32(fr->size0 | (fr->size1 << 8) | (fr->size2 << 16));
175 quint32(fr->rbit_stream_id0 | (fr->rbit_stream_id1 << 8) |
176 (fr->rbit_stream_id2 << 16) |
177 ((fr->rbit_stream_id3 & ~0x80) << 24));
178 frame.type = fr->type;
179 frame.flags = fr->flags;
181 quint32(fr->size0 | (fr->size1 << 8) | (fr->size2 << 16));
183 quint32(fr->rbit_stream_id0 | (fr->rbit_stream_id1 << 8) |
184 (fr->rbit_stream_id2 << 16) |
185 ((fr->rbit_stream_id3 & ~0x80) << 24));
194 if (frame.streamId && !(frame.streamId & 1)) {
195 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
199 if (request->pktsize > m_maxFrameSize) {
202 ret = sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
206 if (request->pktsize >
207 (quint32(request->buf_size) -
sizeof(
struct h2_frame))) {
213 if (request->streamForContinuation) {
214 if (fr->type == FrameContinuation &&
215 request->streamForContinuation == frame.streamId) {
216 fr->type = FrameHeaders;
218 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
223 if (fr->type == FrameSettings) {
224 ret = parseSettings(request, io, frame);
225 }
else if (fr->type == FramePriority) {
226 ret = parsePriority(request, io, frame);
227 }
else if (fr->type == FrameHeaders) {
228 ret = parseHeaders(request, io, frame);
229 }
else if (fr->type == FramePing) {
230 ret = parsePing(request, io, frame);
231 }
else if (fr->type == FrameData) {
232 ret = parseData(request, io, frame);
233 }
else if (fr->type == FramePushPromise) {
235 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
237 }
else if (fr->type == FrameRstStream) {
238 ret = parseRstStream(request, io, frame);
239 }
else if (fr->type == FrameWindowUpdate) {
240 ret = parseWindowUpdate(request, io, frame);
241 }
else if (fr->type == FrameGoaway) {
242 sock->connectionClose();
244 }
else if (fr->type == FrameContinuation) {
245 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
248 qCDebug(C_SERVER_H2) <<
"Unknown frame type" << fr->type;
253 request->buf_size -= 9 + request->pktsize;
254 memmove(request->buffer,
255 request->buffer + 9 + request->pktsize,
256 size_t(request->buf_size));
263 sock->connectionClose();
266 qCWarning(C_SERVER_H2) <<
"Failed to read from socket" << io->
errorString();
269 }
while (bytesAvailable);
280 if ((fr.flags & FlagSettingsAck && fr.len) || fr.len % 6) {
281 sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
283 }
else if (fr.streamId) {
284 sendGoAway(io, request->maxStreamId, ErrorProtocolError);
288 if (!(fr.flags & FlagSettingsAck)) {
291 while (request->pktsize > pos) {
292 quint16 identifier = net_be16(request->buffer + 9 + pos);
293 quint32 value = net_be32(request->buffer + 9 + 2 + pos);
297 if (identifier == SETTINGS_ENABLE_PUSH) {
299 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
302 request->canPush = value;
303 }
else if (identifier == SETTINGS_INITIAL_WINDOW_SIZE) {
304 if (value > 2147483647) {
305 return sendGoAway(io, request->maxStreamId, ErrorFlowControlError);
308 const qint32 difference = qint32(value) - request->settingsInitialWindowSize;
309 request->settingsInitialWindowSize = qint32(value);
311 auto it = request->streams.begin();
312 while (it != request->streams.end()) {
313 (*it)->windowSize += difference;
314 (*it)->windowUpdated();
319 }
else if (identifier == SETTINGS_MAX_FRAME_SIZE) {
320 if (value < 16384 || value > 16777215) {
321 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
323 request->settingsMaxFrameSize = value;
335 if (fr.streamId == 0) {
336 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
339 quint8 padLength = 0;
340 if (fr.flags & FlagDataPadded) {
341 padLength = quint8(*(request->buffer + 9));
342 if (padLength >= fr.len) {
343 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
348 auto streamIt = request->streams.constFind(fr.streamId);
349 if (streamIt != request->streams.constEnd()) {
350 stream = streamIt.value();
352 if (stream->state == H2Stream::Idle) {
353 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
354 }
else if (stream->state == H2Stream::HalfClosed || stream->state == H2Stream::Closed) {
355 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
358 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
365 stream->
body = createBody(request->contentLength);
368 return sendGoAway(io, request->maxStreamId, ErrorInternalError);
371 stream->
body->
write(request->buffer + 9, fr.len - padLength);
373 stream->consumedData += fr.len - padLength;
374 if (stream->contentLength != -1 &&
375 ((fr.flags & FlagDataEndStream && stream->contentLength != stream->consumedData) ||
376 (stream->contentLength > stream->consumedData))) {
377 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
380 if (fr.flags & FlagDataEndStream) {
381 queueStream(request->sock, stream);
390 if (fr.streamId == 0) {
391 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
393 if (fr.len > request->settingsMaxFrameSize) {
394 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
397 char *ptr = request->buffer + 9;
398 quint8 padLength = 0;
399 if (fr.flags & FlagHeadersPadded) {
400 padLength = quint8(*(ptr + pos));
401 if (padLength > fr.len) {
403 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
409 quint32 streamDependency = 0;
411 if (fr.flags & FlagHeadersPriority) {
413 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);
517 return sendGoAway(io, sock->maxStreamId, ErrorFrameSizeError);
518 }
else if (fr.streamId == 0) {
519 return sendGoAway(io, sock->maxStreamId, ErrorProtocolError);
523 while (fr.len > pos) {
525 quint32 exclusiveAndStreamDep = net_be32(sock->buffer + 9 + pos);
530 if (fr.streamId == exclusiveAndStreamDep) {
533 return sendGoAway(io, sock->maxStreamId, ErrorProtocolError);
547 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
548 }
else if (fr.streamId) {
549 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
552 if (!(fr.flags & FlagPingAck)) {
553 sendPing(io, FlagPingAck, request->buffer + 9, 8);
564 if (fr.streamId == 0) {
565 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
566 }
else if (request->pktsize != 4) {
567 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
571 auto streamIt = request->streams.constFind(fr.streamId);
572 if (streamIt != request->streams.constEnd()) {
573 stream = streamIt.value();
576 if (stream->state == H2Stream::Idle) {
577 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
581 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
584 stream->state = H2Stream::Closed;
597 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
600 quint32 windowSizeIncrement = net_be32(request->buffer + 9);
601 if (windowSizeIncrement == 0) {
602 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
610 auto streamIt = request->streams.constFind(fr.streamId);
611 if (streamIt != request->streams.constEnd()) {
612 stream = streamIt.value();
614 if (stream->state == H2Stream::Idle) {
615 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
618 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
621 const qint64 result = qint64(stream->windowSize) + windowSizeIncrement;
622 if (result > 2147483647) {
623 stream->state = H2Stream::Closed;
624 return sendRstStream(io, fr.streamId, ErrorFlowControlError);
626 stream->windowSize = qint32(result);
627 stream->windowUpdated();
630 const qint64 result = qint64(request->windowSize) + windowSizeIncrement;
631 if (result > 2147483647) {
632 return sendGoAway(io, request->maxStreamId, ErrorFlowControlError);
634 request->windowSize = qint32(result);
637 auto streamIt = request->streams.constBegin();
638 if (streamIt != request->streams.constEnd()) {
639 (*streamIt)->windowUpdated();
648int ProtocolHttp2::sendGoAway(
QIODevice *io, quint32 lastStreamId, quint32 error)
const
652 data.
append(
char(lastStreamId >> 24));
653 data.
append(
char(lastStreamId >> 16));
654 data.
append(
char(lastStreamId >> 8));
655 data.
append(
char(lastStreamId));
656 data.
append(
char(error >> 24));
657 data.
append(
char(error >> 16));
658 data.
append(
char(error >> 8));
662 int ret = sendFrame(io, FrameGoaway, 0, 0, data.
constData(), 8);
667int ProtocolHttp2::sendRstStream(
QIODevice *io, quint32 streamId, quint32 error)
const
671 data.
append(
char(error >> 24));
672 data.
append(
char(error >> 16));
673 data.
append(
char(error >> 8));
677 int ret = sendFrame(io, FrameRstStream, 0, streamId, data.
constData(), 4);
682int ProtocolHttp2::sendSettings(
QIODevice *io,
683 const std::vector<std::pair<quint16, quint32>> &settings)
const
686 for (
const std::pair<quint16, quint32> &pair : settings) {
687 data.
append(
char(pair.first >> 8));
688 data.
append(
char(pair.first));
689 data.
append(
char(pair.second >> 24));
690 data.
append(
char(pair.second >> 16));
691 data.
append(
char(pair.second >> 8));
692 data.
append(
char(pair.second));
695 return sendFrame(io, FrameSettings, 0, 0, data.
constData(), data.
length());
698int ProtocolHttp2::sendSettingsAck(
QIODevice *io)
const
700 return sendFrame(io, FrameSettings, FlagSettingsAck);
703int ProtocolHttp2::sendPing(
QIODevice *io, quint8 flags,
const char *data, qint32 dataLen)
const
705 return sendFrame(io, FramePing, flags, 0, data, dataLen);
708int ProtocolHttp2::sendData(
QIODevice *io,
712 qint32 dataLen)
const
714 if (windowSize < 1) {
718 if (windowSize < dataLen) {
721 while (i < dataLen) {
722 int ret = sendFrame(io, FrameData, flags, streamId, data + i, windowSize);
727 if ((i + 1) == dataLen) {
728 flags = FlagDataEndStream;
733 return sendFrame(io, FrameData, FlagDataEndStream, streamId, data, dataLen);
737int ProtocolHttp2::sendFrame(
QIODevice *io,
742 qint32 dataLen)
const
746 fr.size2 = quint8(dataLen >> 16);
747 fr.size1 = quint8(dataLen >> 8);
748 fr.size0 = quint8(dataLen);
751 fr.rbit_stream_id3 = quint8(streamId >> 24);
752 fr.rbit_stream_id2 = quint8(streamId >> 16);
753 fr.rbit_stream_id1 = quint8(streamId >> 8);
754 fr.rbit_stream_id0 = quint8(streamId);
764 if (io->
write(
reinterpret_cast<const char *
>(&fr),
sizeof(
struct h2_frame)) !=
769 if (dataLen && io->
write(data, dataLen) != dataLen) {
775void ProtocolHttp2::queueStream(
Socket *socket,
H2Stream *stream)
const
777 ++socket->processing;
784bool ProtocolHttp2::upgradeH2C(
Socket *socket,
791 const auto settings = headers.
header(
"Http2-Settings");
793 io->
write(
"HTTP/1.1 101 Switching Protocols\r\n"
794 "Connection: Upgrade\r\n"
795 "Upgrade: h2c\r\n\r\n");
796 socket->proto =
this;
798 protoRequest->upgradedFrom = socket->protoData;
799 socket->protoData = protoRequest;
801 protoRequest->hpack =
new HPack(m_headerTableSize);
802 protoRequest->maxStreamId = 1;
804 auto stream =
new H2Stream(1, 65535, protoRequest);
814 stream->state = H2Stream::HalfClosed;
815 protoRequest->streams.insert(1, stream);
816 protoRequest->maxStreamId = 1;
820 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
821 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
825 queueStream(socket, stream);
826 qCDebug(C_SERVER_H2) <<
"upgraded";
838ProtoRequestHttp2::~ProtoRequestHttp2()
842void ProtoRequestHttp2::setupNewConnection(
Socket *sock)
847H2Stream::H2Stream(quint32 _streamId, qint32 _initialWindowSize,
ProtoRequestHttp2 *protoRequestH2)
848 : protoRequest(protoRequestH2)
849 , streamId(_streamId)
850 , windowSize(_initialWindowSize)
852 protocol =
"HTTP/2"_qba;
853 serverAddress = protoRequestH2->sock->serverAddress;
854 remoteAddress = protoRequestH2->sock->remoteAddress;
855 remotePort = protoRequestH2->sock->remotePort;
856 isSecure = protoRequestH2->sock->isSecure;
870 auto parser =
dynamic_cast<ProtocolHttp2 *
>(protoRequest->sock->proto);
872 qint64 remainingData = len;
874 while (remainingData > 0 && state != H2Stream::Closed) {
875 qint64 availableWindowSize = qMin(windowSize, protoRequest->windowSize);
880 availableWindowSize =
881 qMin(availableWindowSize, (qint64) protoRequest->settingsMaxFrameSize);
882 if (availableWindowSize == 0) {
886 if (loop->
exec() == 0) {
896 if (availableWindowSize > remainingData) {
897 ret = parser->sendFrame(protoRequest->io,
902 qint32(remainingData));
904 protoRequest->windowSize -= remainingData;
905 windowSize -= remainingData;
906 sent += remainingData;
908 ret = parser->sendFrame(protoRequest->io,
913 qint32(availableWindowSize));
914 remainingData -= availableWindowSize;
915 protoRequest->windowSize -= availableWindowSize;
916 windowSize -= availableWindowSize;
917 sent += availableWindowSize;
923 return ret == 0 ? len : -1;
929 protoRequest->hpack->encodeHeaders(
932 auto parser =
dynamic_cast<ProtocolHttp2 *
>(protoRequest->sock->proto);
934 int ret = parser->sendFrame(protoRequest->io,
936 FlagHeadersEndHeaders,
947 protoRequest->streams.remove(streamId);
948 protoRequest->sock->requestFinished();
952void H2Stream::windowUpdated()
957 if (protoRequest->windowSize > 0 && windowSize > 0 && loop && loop->
isRunning()) {
962#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)