cutelyst 5.0.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
protocolhttp2.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2018 Daniel Nicoletti <dantti12@gmail.com>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#include "protocolhttp2.h"
6
7#include "hpack.h"
8#include "server.h"
9#include "socket.h"
10
11#include <QEventLoop>
12#include <QLoggingCategory>
13
14using namespace Cutelyst;
15using namespace Qt::Literals::StringLiterals;
16
17Q_LOGGING_CATEGORY(C_SERVER_H2, "cutelyst.server.http2", QtWarningMsg)
18
19namespace {
20struct h2_frame {
21 quint8 size2;
22 quint8 size1;
23 quint8 size0;
24 quint8 type;
25 quint8 flags;
26 quint8 rbit_stream_id3;
27 quint8 rbit_stream_id2;
28 quint8 rbit_stream_id1;
29 quint8 rbit_stream_id0;
30};
31
32enum SettingsFlags { FlagSettingsAck = 0x1 };
33
34enum PingFlags { FlagPingAck = 0x1 };
35
36enum HeaderFlags {
37 FlagHeadersEndStream = 0x1,
38 FlagHeadersEndHeaders = 0x4,
39 FlagHeadersPadded = 0x8,
40 FlagHeadersPriority = 0x20,
41};
42
43enum PushPromiseFlags {
44 FlagPushPromiseEndHeaders = 0x4,
45 FlagPushPromisePadded = 0x8,
46};
47
48enum DataFlags {
49 FlagDataEndStream = 0x1,
50 FlagDataPadded = 0x8,
51};
52
53enum FrameType {
54 FrameData = 0x0,
55 FrameHeaders = 0x1,
56 FramePriority = 0x2,
57 FrameRstStream = 0x3,
58 FrameSettings = 0x4,
59 FramePushPromise = 0x5,
60 FramePing = 0x6,
61 FrameGoaway = 0x7,
62 FrameWindowUpdate = 0x8,
63 FrameContinuation = 0x9
64};
65
66enum ErrorCodes {
67 ErrorNoError = 0x0,
68 ErrorProtocolError = 0x1,
69 ErrorInternalError = 0x2,
70 ErrorFlowControlError = 0x3,
71 ErrorSettingsTimeout = 0x4,
72 ErrorStreamClosed = 0x5,
73 ErrorFrameSizeError = 0x6,
74 ErrorRefusedStream = 0x7,
75 ErrorCancel = 0x8,
76 ErrorCompressionError = 0x9,
77 ErrorConnectError = 0xA,
78 ErrorEnhanceYourCalm = 0xB,
79 ErrorInadequateSecurity = 0xC,
80 ErrorHttp11Required = 0xD
81};
82
83enum Settings {
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,
91};
92
93constexpr int PREFACE_SIZE = 24;
94} // namespace
95
96ProtocolHttp2::ProtocolHttp2(Server *wsgi)
97 : Protocol(wsgi)
98 , m_headerTableSize(qint32(wsgi->http2HeaderTableSize()))
99{
100 m_bufferSize = qMin(m_bufferSize, 2147483647);
101
102 // 2^14 + 9 (octects)
103 if (m_bufferSize < 16393) {
104 qFatal("HTTP/2 Protocol requires that buffer-size to be at least '16393' in size, current "
105 "value is '%s'",
106 QByteArray::number(m_bufferSize).constData());
107 }
108
109 m_maxFrameSize = quint32(m_bufferSize - 9);
110}
111
112ProtocolHttp2::~ProtocolHttp2()
113{
114}
115
116Protocol::Type ProtocolHttp2::type() const
117{
118 return Protocol::Type::Http2;
119}
120
121void ProtocolHttp2::parse(Socket *sock, QIODevice *io) const
122{
123 auto request = static_cast<ProtoRequestHttp2 *>(sock->protoData);
124
125 qint64 bytesAvailable = io->bytesAvailable();
126 // qCDebug(C_SERVER_H2) << sock << "READ available" << bytesAvailable << "buffer size" <<
127 // request->buf_size << "default buffer size" << m_bufferSize ;
128
129 do {
130 const qint64 len =
131 io->read(request->buffer + request->buf_size, m_bufferSize - request->buf_size);
132 bytesAvailable -= len;
133
134 if (len > 0) {
135 request->buf_size += len;
136 int ret = 0;
137 while (request->buf_size && ret == 0) {
138 // qDebug() << "Current buffer size" << request->connState <<
139 // request->buf_size;//QByteArray(request->buffer,
140 // request->buf_size);
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) {
144 // qCDebug(C_SERVER_H2) << "Got MAGIC" <<
145 // sizeof(struct h2_frame);
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;
151
152 sendSettings(io,
153 {
154 {SETTINGS_ENABLE_CONNECT_PROTOCOL, 0},
155 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
156 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
157 });
158 } else {
159 qCDebug(C_SERVER_H2) << "Protocol Error: Invalid connection preface"
160 << sock->remoteAddress.toString();
161 // RFC 7540 says this MAY be omitted, so let's reduce further processing
162 // ret = sendGoAway(io, request->maxStreamId,
163 // ErrorProtocolError);
164 sock->connectionClose();
165 return;
166 }
167 } else {
168 // qDebug() << "MAGIC needs more data" <<
169 // bytesAvailable;
170 break;
171 }
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);
175 H2Frame frame;
176 frame.len = quint32(fr->size0 | (fr->size1 << 8) | (fr->size2 << 16));
177 frame.streamId =
178 quint32(fr->rbit_stream_id0 | (fr->rbit_stream_id1 << 8) |
179 (fr->rbit_stream_id2 << 16) |
180 ((fr->rbit_stream_id3 & ~0x80) << 24)); // Ignore first bit
181 frame.type = fr->type;
182 frame.flags = fr->flags;
183 request->pktsize =
184 quint32(fr->size0 | (fr->size1 << 8) | (fr->size2 << 16));
185 request->stream_id =
186 quint32(fr->rbit_stream_id0 | (fr->rbit_stream_id1 << 8) |
187 (fr->rbit_stream_id2 << 16) |
188 ((fr->rbit_stream_id3 & ~0x80) << 24)); // Ignore first bit
189
190 // qDebug() << "Frame type" << fr->type
191 // << "flags" << fr->flags
192 // << "stream-id" << request->stream_id
193 // << "required size" << request->pktsize
194 // << "available" << (request->buf_size -
195 // sizeof(struct h2_frame));
196
197 if (frame.streamId && !(frame.streamId & 1)) {
198 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
199 break;
200 }
201
202 if (request->pktsize > m_maxFrameSize) {
203 // qDebug() << "Frame too big" <<
204 // request->pktsize << m_bufferSize;
205 ret = sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
206 break;
207 }
208
209 if (request->pktsize >
210 (quint32(request->buf_size) - sizeof(struct h2_frame))) {
211 // qDebug() << "need more data" <<
212 // bytesAvailable;
213 break;
214 }
215
216 if (request->streamForContinuation) {
217 if (fr->type == FrameContinuation &&
218 request->streamForContinuation == frame.streamId) {
219 fr->type = FrameHeaders;
220 } else {
221 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
222 break;
223 }
224 }
225
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) {
237 // Client can not PUSH
238 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
239 break;
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();
246 return;
247 } else if (fr->type == FrameContinuation) {
248 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
249 break;
250 } else {
251 qCDebug(C_SERVER_H2) << "Unknown frame type" << fr->type;
252 // Implementations MUST ignore and discard any frame that has a type
253 // that is unknown.
254 }
255
256 request->buf_size -= 9 + request->pktsize;
257 memmove(request->buffer,
258 request->buffer + 9 + request->pktsize,
259 size_t(request->buf_size));
260 }
261 }
262 }
263
264 if (ret) {
265 // qDebug() << "Got error closing" << ret;
266 sock->connectionClose();
267 }
268 } else {
269 qCWarning(C_SERVER_H2) << "Failed to read from socket" << io->errorString();
270 break;
271 }
272 } while (bytesAvailable);
273}
274
275ProtocolData *ProtocolHttp2::createData(Socket *sock) const
276{
277 return new ProtoRequestHttp2(sock, m_bufferSize);
278}
279
280int ProtocolHttp2::parseSettings(ProtoRequestHttp2 *request, QIODevice *io, const H2Frame &fr) const
281{
282 // qDebug() << "Consumming SETTINGS";
283 if ((fr.flags & FlagSettingsAck && fr.len) || fr.len % 6) {
284 sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
285 return 1;
286 } else if (fr.streamId) {
287 sendGoAway(io, request->maxStreamId, ErrorProtocolError);
288 return 1;
289 }
290
291 if (!(fr.flags & FlagSettingsAck)) {
293 uint pos = 0;
294 while (request->pktsize > pos) {
295 quint16 identifier = net_be16(request->buffer + 9 + pos);
296 quint32 value = net_be32(request->buffer + 9 + 2 + pos);
297 settings.push_back({identifier, value});
298 pos += 6;
299 // qDebug() << "SETTINGS" << identifier << value;
300 if (identifier == SETTINGS_ENABLE_PUSH) {
301 if (value > 1) {
302 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
303 }
304
305 request->canPush = value;
306 } else if (identifier == SETTINGS_INITIAL_WINDOW_SIZE) {
307 if (value > 2147483647) {
308 return sendGoAway(io, request->maxStreamId, ErrorFlowControlError);
309 }
310
311 const qint32 difference = qint32(value) - request->settingsInitialWindowSize;
312 request->settingsInitialWindowSize = qint32(value);
313
314 for (const auto &stream : std::as_const(request->streams)) {
315 stream->windowSize += difference;
316 stream->windowUpdated();
317 // qCDebug(C_SERVER_H2) << "updating stream" << it.key() <<
318 // "to window" << stream->windowSize;
319 }
320 } else if (identifier == SETTINGS_MAX_FRAME_SIZE) {
321 if (value < 16384 || value > 16777215) {
322 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
323 }
324 request->settingsMaxFrameSize = value;
325 }
326 }
327 sendSettingsAck(io);
328 }
329
330 return ErrorNoError;
331}
332
333int ProtocolHttp2::parseData(ProtoRequestHttp2 *request, QIODevice *io, const H2Frame &fr) const
334{
335 // qCDebug(C_SERVER_H2) << "Consuming DATA" << fr.len;
336 if (fr.streamId == 0) {
337 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
338 }
339
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);
345 }
346 }
347
348 H2Stream *stream;
349 auto streamIt = request->streams.constFind(fr.streamId);
350 if (streamIt != request->streams.constEnd()) {
351 stream = streamIt.value();
352
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);
357 }
358 } else {
359 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
360 }
361
362 // qCDebug(C_SERVER_H2) << "Frame data" << padLength << "state" << stream->state <<
363 // "content-length" << stream->contentLength;
364
365 if (!stream->body) {
366 stream->body = createBody(request->contentLength);
367 if (!stream->body) {
368 // Failed to create body to store data
369 return sendGoAway(io, request->maxStreamId, ErrorInternalError);
370 }
371 }
372 stream->body->write(request->buffer + 9, fr.len - padLength);
373
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);
379 }
380
381 if (fr.flags & FlagDataEndStream) {
382 queueStream(request->sock, stream);
383 }
384
385 return ErrorNoError;
386}
387
388int ProtocolHttp2::parseHeaders(ProtoRequestHttp2 *request, QIODevice *io, const H2Frame &fr) const
389{
390 // qCDebug(C_SERVER_H2) << "Consumming HEADERS" << bool(fr.flags & FlagHeadersEndStream);
391 if (fr.streamId == 0) {
392 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
393 }
394 if (fr.len > request->settingsMaxFrameSize) {
395 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
396 }
397 int pos = 0;
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) {
403 // qCDebug(C_SERVER_H2) << "header pad length";
404 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
405 }
406
407 pos += 1;
408 }
409
410 quint32 streamDependency = 0;
411 // quint8 weight = 0;
412 if (fr.flags & FlagHeadersPriority) {
413 // TODO disable exclusive bit
414 streamDependency = net_be32(ptr + pos);
415 if (fr.streamId == streamDependency) {
416 // qCDebug(C_SERVER_H2) << "header stream dep";
417 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
418 }
419
420 pos += 4;
421 // weight = quint8(*(ptr + pos)) + 1;
422 pos += 1;
423 }
424 ptr += pos;
425
426 H2Stream *stream;
427 auto streamIt = request->streams.constFind(fr.streamId);
428 if (streamIt != request->streams.constEnd()) {
429 stream = streamIt.value();
430
431 // qCDebug(C_SERVER_H2) << "------------" << !(fr.flags & FlagHeadersEndStream) <<
432 // stream->state << request->streamForContinuation ;
433
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);
438 }
439 if (stream->state == H2Stream::HalfClosed && request->streamForContinuation == 0) {
440 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
441 }
442 if (stream->state == H2Stream::Closed) {
443 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
444 }
445 } else {
446 if (request->maxStreamId >= fr.streamId) {
447 // qCDebug(C_SERVER_H2) << "header maxStreamId ";
448 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
449 }
450 request->maxStreamId = fr.streamId;
451
452 stream = new H2Stream(fr.streamId, request->settingsInitialWindowSize, request);
453 if (useStats) {
454 stream->startOfRequest = std::chrono::steady_clock::now();
455 }
456 request->streams.insert(fr.streamId, stream);
457 }
458
459 if (stream->state == H2Stream::Idle) {
460 stream->state = H2Stream::Open;
461 }
462
463 if (fr.flags & FlagHeadersEndStream) {
464 stream->state = H2Stream::HalfClosed;
465 }
466
467 if (!request->hpack) {
468 request->hpack = new HPack(m_headerTableSize);
469 }
470
471 if (fr.flags & FlagHeadersEndHeaders) {
472 request->streamForContinuation = 0;
473 if (!request->headersBuffer.isEmpty()) {
474 request->headersBuffer.append(ptr, qint32(fr.len) - pos - padLength);
475 }
476 } else {
477 // qCDebug(C_SERVER_H2) << "Setting HEADERS for continuation on stream" <<
478 // fr.streamId;
479 request->streamForContinuation = fr.streamId;
480 request->headersBuffer.append(ptr, qint32(fr.len) - pos - padLength);
481 return 0;
482 }
483
484 quint8 *it;
485 quint8 *itEnd;
486 if (request->headersBuffer.size()) {
487 it = reinterpret_cast<quint8 *>(request->headersBuffer.begin());
488 itEnd = reinterpret_cast<quint8 *>(request->headersBuffer.end());
489 } else {
490 it = reinterpret_cast<quint8 *>(ptr);
491 itEnd = it + fr.len - pos - padLength;
492 }
493
494 int ret = request->hpack->decode(it, itEnd, stream);
495 if (ret) {
496 // qDebug() << "Headers parser error" << ret << QByteArray(ptr + pos, fr.len - pos -
497 // padLength).toHex();
498 return sendGoAway(io, request->maxStreamId, quint32(ret));
499 }
500
501 // qDebug() << "Headers" << padLength << streamDependency << weight << "stream headers size"
502 // << stream->headers /*<< QByteArray(ptr + pos, fr.len - pos - padLength).toHex()*/ << ret;
503
504 if ((stream->state == H2Stream::HalfClosed || fr.flags & FlagHeadersEndStream) &&
505 request->streamForContinuation == 0) {
506
507 // Process request
508 queueStream(request->sock, stream);
509 }
510
511 return 0;
512}
513
514int ProtocolHttp2::parsePriority(ProtoRequestHttp2 *sock, QIODevice *io, const H2Frame &fr) const
515{
516 // qDebug() << "Consumming PRIORITY";
517 if (fr.len != 5) {
518 return sendGoAway(io, sock->maxStreamId, ErrorFrameSizeError);
519 } else if (fr.streamId == 0) {
520 return sendGoAway(io, sock->maxStreamId, ErrorProtocolError);
521 }
522
523 uint pos = 0;
524 while (fr.len > pos) {
525 // TODO store/disable EXCLUSIVE bit
526 quint32 exclusiveAndStreamDep = net_be32(sock->buffer + 9 + pos);
527 // quint8 weigth = *reinterpret_cast<quint8 *>(sock->buffer + 9 + 4 + pos) + 1;
528 // settings.push_back({ identifier, value });
529 // sock->pktsize -= 6;
530
531 if (fr.streamId == exclusiveAndStreamDep) {
532 // qDebug() << "PRIO error2" << exclusiveAndStreamDep << fr.streamId;
533
534 return sendGoAway(io, sock->maxStreamId, ErrorProtocolError);
535 }
536
537 pos += 6;
538 // qDebug() << "PRIO" << exclusiveAndStreamDep << weigth;
539 }
540
541 return 0;
542}
543
544int ProtocolHttp2::parsePing(ProtoRequestHttp2 *request, QIODevice *io, const H2Frame &fr) const
545{
546 // qCDebug(C_SERVER_H2) << "Got PING" << fr.flags;
547 if (fr.len != 8) {
548 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
549 } else if (fr.streamId) {
550 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
551 }
552
553 if (!(fr.flags & FlagPingAck)) {
554 sendPing(io, FlagPingAck, request->buffer + 9, 8);
555 }
556 return 0;
557}
558
559int ProtocolHttp2::parseRstStream(ProtoRequestHttp2 *request,
560 QIODevice *io,
561 const H2Frame &fr) const
562{
563 // qCDebug(C_SERVER_H2) << "Consuming RST_STREAM";
564
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);
569 }
570
571 H2Stream *stream;
572 auto streamIt = request->streams.constFind(fr.streamId);
573 if (streamIt != request->streams.constEnd()) {
574 stream = streamIt.value();
575
576 // qCDebug(C_SERVER_H2) << "Consuming RST_STREAM state" << stream->state;
577 if (stream->state == H2Stream::Idle) {
578 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
579 }
580
581 } else {
582 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
583 }
584
585 stream->state = H2Stream::Closed;
586
587 // quint32 errorCode = h2_be32(request->buffer + 9);
588 // qCDebug(C_SERVER_H2) << "RST frame" << errorCode;
589
590 return 0;
591}
592
593int ProtocolHttp2::parseWindowUpdate(ProtoRequestHttp2 *request,
594 QIODevice *io,
595 const H2Frame &fr) const
596{
597 if (fr.len != 4) {
598 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
599 }
600
601 quint32 windowSizeIncrement = net_be32(request->buffer + 9);
602 if (windowSizeIncrement == 0) {
603 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
604 }
605
606 // qDebug() << "Consuming WINDOW_UPDATE" << fr.streamId << "increment" << windowSizeIncrement
607 // << request;
608
609 if (fr.streamId) {
610 H2Stream *stream;
611 auto streamIt = request->streams.constFind(fr.streamId);
612 if (streamIt != request->streams.constEnd()) {
613 stream = streamIt.value();
614
615 if (stream->state == H2Stream::Idle) {
616 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
617 }
618 } else {
619 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
620 }
621
622 const qint64 result = qint64(stream->windowSize) + windowSizeIncrement;
623 if (result > 2147483647) {
624 stream->state = H2Stream::Closed;
625 return sendRstStream(io, fr.streamId, ErrorFlowControlError);
626 }
627 stream->windowSize = qint32(result);
628 stream->windowUpdated();
629
630 } else {
631 const qint64 result = qint64(request->windowSize) + windowSizeIncrement;
632 if (result > 2147483647) {
633 return sendGoAway(io, request->maxStreamId, ErrorFlowControlError);
634 }
635 request->windowSize = qint32(result);
636
637 if (result > 0) {
638 for (const auto &stream : std::as_const(request->streams)) {
639 stream->windowUpdated();
640 }
641 }
642 }
643
644 return 0;
645}
646
647int ProtocolHttp2::sendGoAway(QIODevice *io, quint32 lastStreamId, quint32 error) const
648{
649 // qDebug() << "GOAWAY" << error;
650 QByteArray data;
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));
658 data.append(char(error));
659 // quint64 data = error;
660 // sendFrame(io, FrameGoaway, 0, 0, reinterpret_cast<const char *>(&data), 4);
661 int ret = sendFrame(io, FrameGoaway, 0, 0, data.constData(), 8);
662 // qDebug() << ret << int(error);
663 return error || ret;
664}
665
666int ProtocolHttp2::sendRstStream(QIODevice *io, quint32 streamId, quint32 error) const
667{
668 // qDebug() << "RST_STREAM" << streamId << error;
669 QByteArray data;
670 data.append(char(error >> 24));
671 data.append(char(error >> 16));
672 data.append(char(error >> 8));
673 data.append(char(error));
674 // quint64 data = error;
675 // sendFrame(io, FrameGoaway, 0, 0, reinterpret_cast<const char *>(&data), 4);
676 int ret = sendFrame(io, FrameRstStream, 0, streamId, data.constData(), 4);
677 // qDebug() << ret << int(error);
678 return error || ret;
679}
680
681int ProtocolHttp2::sendSettings(QIODevice *io,
682 const std::vector<std::pair<quint16, quint32>> &settings) const
683{
684 QByteArray data;
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));
692 }
693 // qDebug() << "Send settings" << data.toHex();
694 return sendFrame(io, FrameSettings, 0, 0, data.constData(), data.length());
695}
696
697int ProtocolHttp2::sendSettingsAck(QIODevice *io) const
698{
699 return sendFrame(io, FrameSettings, FlagSettingsAck);
700}
701
702int ProtocolHttp2::sendPing(QIODevice *io, quint8 flags, const char *data, qint32 dataLen) const
703{
704 return sendFrame(io, FramePing, flags, 0, data, dataLen);
705}
706
707int ProtocolHttp2::sendData(QIODevice *io,
708 quint32 streamId,
709 qint32 windowSize,
710 const char *data,
711 qint32 dataLen) const
712{
713 if (windowSize < 1) {
714 // qDebug() << "Window size too small, holding";
715 return 0;
716 }
717 if (windowSize < dataLen) {
718 qint32 i = 0;
719 quint8 flags = 0;
720 while (i < dataLen) {
721 int ret = sendFrame(io, FrameData, flags, streamId, data + i, windowSize);
722 if (ret) {
723 return ret;
724 }
725 i += windowSize;
726 if ((i + 1) == dataLen) {
727 flags = FlagDataEndStream;
728 }
729 }
730 return 0;
731 } else {
732 return sendFrame(io, FrameData, FlagDataEndStream, streamId, data, dataLen);
733 }
734}
735
736int ProtocolHttp2::sendFrame(QIODevice *io,
737 quint8 type,
738 quint8 flags,
739 quint32 streamId,
740 const char *data,
741 qint32 dataLen) const
742{
743 h2_frame fr;
744
745 fr.size2 = quint8(dataLen >> 16);
746 fr.size1 = quint8(dataLen >> 8);
747 fr.size0 = quint8(dataLen);
748 fr.type = type;
749 fr.flags = flags;
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);
754
755 // qCDebug(C_SERVER_H2) << "Sending frame"
756 // << type
757 // << flags
758 // << streamId
759 // << dataLen;
760
761 // qCDebug(C_SERVER_H2) << "Frame" << QByteArray(reinterpret_cast<const char *>(&fr),
762 // sizeof(struct h2_frame)).toHex();
763 if (io->write(reinterpret_cast<const char *>(&fr), sizeof(struct h2_frame)) !=
764 sizeof(struct h2_frame)) {
765 return -1;
766 }
767 // qCDebug(C_SERVER_H2) << "Frame data" << QByteArray(data, dataLen).toHex();
768 if (dataLen && io->write(data, dataLen) != dataLen) {
769 return -1;
770 }
771 return 0;
772}
773
774void ProtocolHttp2::queueStream(Socket *socket, H2Stream *stream) const
775{
776 ++socket->processing;
777 if (stream->body) {
778 stream->body->seek(0);
779 }
780 Q_EMIT socket->engine->processRequestAsync(stream);
781}
782
783bool ProtocolHttp2::upgradeH2C(Socket *socket,
784 QIODevice *io,
785 const Cutelyst::EngineRequest &request)
786{
787 const Cutelyst::Headers &headers = request.headers;
788 if (headers.header("Upgrade").compare("h2c") == 0 &&
789 headers.connection().compare("Upgrade, HTTP2-Settings") == 0) {
790 const auto settings = headers.header("Http2-Settings");
791 if (!settings.isEmpty()) {
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;
796 auto protoRequest = new ProtoRequestHttp2(socket, m_bufferSize);
797 protoRequest->upgradedFrom = socket->protoData;
798 socket->protoData = protoRequest;
799
800 protoRequest->hpack = new HPack(m_headerTableSize);
801 protoRequest->maxStreamId = 1;
802
803 auto stream = new H2Stream(1, 65535, protoRequest);
804 stream->method = request.method;
805 stream->path = request.path;
806 stream->query = request.query;
807 stream->remoteUser = request.remoteUser;
808 stream->headers = request.headers;
809 stream->startOfRequest = std::chrono::steady_clock::now();
810 stream->status = request.status;
811 stream->body = request.body;
812
813 stream->state = H2Stream::HalfClosed;
814 protoRequest->streams.insert(1, stream);
815 protoRequest->maxStreamId = 1;
816
817 sendSettings(io,
818 {
819 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
820 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
821 });
822
823 // Process request
824 queueStream(socket, stream);
825 qCDebug(C_SERVER_H2) << "upgraded";
826 return true;
827 }
828 }
829 return false;
830}
831
832ProtoRequestHttp2::ProtoRequestHttp2(Cutelyst::Socket *sock, int bufferSize)
833 : ProtocolData(sock, bufferSize)
834{
835}
836
837ProtoRequestHttp2::~ProtoRequestHttp2()
838{
839}
840
841void ProtoRequestHttp2::setupNewConnection(Socket *sock)
842{
843 Q_UNUSED(sock)
844}
845
846H2Stream::H2Stream(quint32 _streamId, qint32 _initialWindowSize, ProtoRequestHttp2 *protoRequestH2)
847 : protoRequest(protoRequestH2)
848 , streamId(_streamId)
849 , windowSize(_initialWindowSize)
850{
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;
856}
857
858H2Stream::~H2Stream()
859{
860 if (loop) {
861 loop->exit(-1);
862 delete loop;
863 }
864}
865
866qint64 H2Stream::doWrite(const char *data, qint64 len)
867{
868 int ret = -1;
869 auto parser = dynamic_cast<ProtocolHttp2 *>(protoRequest->sock->proto);
870
871 qint64 remainingData = len;
872 qint64 sent = 0;
873 while (remainingData > 0 && state != H2Stream::Closed) {
874 qint64 availableWindowSize = qMin(windowSize, protoRequest->windowSize);
875 // qCDebug(C_SERVER_H2) << "H2Stream::doWrite" << len << streamId <<
876 // "availableWindowSize" << availableWindowSize
877 // << "remaining data" << remainingData
878 // << "stream" << this << protoRequest;
879 availableWindowSize =
880 qMin(availableWindowSize, (qint64) protoRequest->settingsMaxFrameSize);
881 if (availableWindowSize == 0) {
882 if (!loop) {
883 loop = new QEventLoop;
884 }
885 if (loop->exec() == 0) {
886 continue;
887 }
888 return -1;
889 }
890
891 // qCDebug(C_SERVER_H2) << "H2Stream::doWrite" << len << streamId <<
892 // "availableWindowSize" << availableWindowSize
893 // << "remaining data" << remainingData;
894
895 if (availableWindowSize > remainingData) {
896 ret = parser->sendFrame(protoRequest->io,
897 FrameData,
898 FlagDataEndStream,
899 streamId,
900 data + sent,
901 qint32(remainingData));
902 remainingData = 0;
903 protoRequest->windowSize -= remainingData;
904 windowSize -= remainingData;
905 sent += remainingData;
906 } else {
907 ret = parser->sendFrame(protoRequest->io,
908 FrameData,
909 0x0,
910 streamId,
911 data + sent,
912 qint32(availableWindowSize));
913 remainingData -= availableWindowSize;
914 protoRequest->windowSize -= availableWindowSize;
915 windowSize -= availableWindowSize;
916 sent += availableWindowSize;
917 }
918
919 // qCDebug(C_SERVER_H2) << "H2Stream::doWrite ret" << ret << (ret == 0 ? len : -1);
920 }
921
922 return ret == 0 ? len : -1;
923}
924
925bool H2Stream::writeHeaders(quint16 status, const Cutelyst::Headers &headers)
926{
927 QByteArray buf;
928 protoRequest->hpack->encodeHeaders(
929 status, headers, buf, static_cast<ServerEngine *>(protoRequest->sock->engine));
930
931 auto parser = dynamic_cast<ProtocolHttp2 *>(protoRequest->sock->proto);
932
933 int ret = parser->sendFrame(protoRequest->io,
934 FrameHeaders,
935 FlagHeadersEndHeaders,
936 streamId,
937 buf.constData(),
938 buf.size());
939
940 return ret == 0;
941}
942
944{
945 state = Closed;
946 protoRequest->streams.remove(streamId);
947 protoRequest->sock->requestFinished();
948 delete this;
949}
950
951void H2Stream::windowUpdated()
952{
953 // qDebug() << "WINDOW_UPDATED" << protoRequest->windowSize << windowSize << loop << (loop &&
954 // loop->isRunning()) << this << protoRequest;
955
956 if (protoRequest->windowSize > 0 && windowSize > 0 && loop && loop->isRunning()) {
957 loop->quit();
958 }
959}
960
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
Container for HTTP headers.
Definition headers.h:24
QByteArray connection() const noexcept
Definition headers.cpp:296
QByteArray header(QAnyStringView key) const noexcept
Definition headers.cpp:393
Implements a web server.
Definition server.h:60
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
void quit()
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)