5 #include "protocolhttp2.h" 12 #include <QLoggingCategory> 17 Q_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;
31 enum SettingsFlags { FlagSettingsAck = 0x1 };
33 enum PingFlags { FlagPingAck = 0x1 };
36 FlagHeadersEndStream = 0x1,
37 FlagHeadersEndHeaders = 0x4,
38 FlagHeadersPadded = 0x8,
39 FlagHeadersPriority = 0x20,
42 enum 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 94 ProtocolHttp2::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);
110 ProtocolHttp2::~ProtocolHttp2()
114 Protocol::Type ProtocolHttp2::type()
const 116 return Protocol::Type::Http2;
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);
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)) {
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();
649 int 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);
668 int 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);
683 int 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());
699 int ProtocolHttp2::sendSettingsAck(
QIODevice *io)
const 701 return sendFrame(io, FrameSettings, FlagSettingsAck);
704 int ProtocolHttp2::sendPing(
QIODevice *io, quint8 flags,
const char *data, qint32 dataLen)
const 706 return sendFrame(io, FramePing, flags, 0, data, dataLen);
709 int 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);
738 int 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)) !=
770 if (dataLen && io->
write(data, dataLen) != dataLen) {
776 void ProtocolHttp2::queueStream(
Socket *socket,
H2Stream *stream)
const 778 ++socket->processing;
785 bool ProtocolHttp2::upgradeH2C(
Socket *socket,
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;
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";
834 ProtoRequestHttp2::ProtoRequestHttp2(
Cutelyst::Socket *sock,
int bufferSize)
839 ProtoRequestHttp2::~ProtoRequestHttp2()
843 void ProtoRequestHttp2::setupNewConnection(
Socket *sock)
848 H2Stream::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;
860 H2Stream::~H2Stream()
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(
931 status,
headers, buf, static_cast<ServerEngine *>(protoRequest->sock->engine));
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();
953 void H2Stream::windowUpdated()
958 if (protoRequest->windowSize > 0 && windowSize > 0 && loop && loop->
isRunning()) {
963 #include "moc_protocolhttp2.cpp"
void push_back(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(ProcessEventsFlags flags)
void exit(int returnCode)
const char * constData() const const
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