5 #include "protocolhttp2.h" 12 #include <QLoggingCategory> 16 Q_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;
30 enum SettingsFlags { FlagSettingsAck = 0x1 };
32 enum PingFlags { FlagPingAck = 0x1 };
35 FlagHeadersEndStream = 0x1,
36 FlagHeadersEndHeaders = 0x4,
37 FlagHeadersPadded = 0x8,
38 FlagHeadersPriority = 0x20,
41 enum 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 93 ProtocolHttp2::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);
109 ProtocolHttp2::~ProtocolHttp2()
113 Protocol::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();
648 int 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);
667 int 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);
682 int 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());
698 int ProtocolHttp2::sendSettingsAck(
QIODevice *io)
const 700 return sendFrame(io, FrameSettings, FlagSettingsAck);
703 int ProtocolHttp2::sendPing(
QIODevice *io, quint8 flags,
const char *data, qint32 dataLen)
const 705 return sendFrame(io, FramePing, flags, 0, data, dataLen);
708 int 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);
737 int 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) {
775 void ProtocolHttp2::queueStream(
Socket *socket,
H2Stream *stream)
const 777 ++socket->processing;
784 bool ProtocolHttp2::upgradeH2C(
Socket *socket,
791 const auto settings = headers.
header(
"Http2-Settings");
792 if (!settings.isEmpty()) {
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";
833 ProtoRequestHttp2::ProtoRequestHttp2(
Cutelyst::Socket *sock,
int bufferSize)
838 ProtoRequestHttp2::~ProtoRequestHttp2()
842 void ProtoRequestHttp2::setupNewConnection(
Socket *sock)
847 H2Stream::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;
859 H2Stream::~H2Stream()
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(
930 status,
headers, buf, static_cast<ServerEngine *>(protoRequest->sock->engine));
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();
952 void H2Stream::windowUpdated()
957 if (protoRequest->windowSize > 0 && windowSize > 0 && loop && loop->
isRunning()) {
962 #include "moc_protocolhttp2.cpp"
void push_back(QList::parameter_type value)
virtual bool seek(qint64 pos)
bool writeHeaders(quint16 status, const Cutelyst::Headers &headers) override final
QString errorString() const const
bool isEmpty() const const
QString toString() const const
qsizetype length() const const
bool isRunning() const const
QByteArray read(qint64 maxSize)
int compare(QByteArrayView bv, Qt::CaseSensitivity cs) const const
TimePointSteady startOfRequest
QByteArray number(double n, char format, int precision)
int exec(QEventLoop::ProcessEventsFlags flags)
void exit(int returnCode)
const char * constData() const const
QByteArray::iterator begin()
void processRequestAsync(Cutelyst::EngineRequest *request)
qint64 write(const QByteArray &data)
void processingFinished() override final
The Cutelyst namespace holds all public Cutelyst API.
virtual qint64 bytesAvailable() const const
QByteArray & append(QByteArrayView data)
qint64 doWrite(const char *data, qint64 len) override final
qsizetype size() const const
QByteArray::iterator end()