cutelyst 5.0.1
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 *server)
97 : Protocol(server)
98 , m_headerTableSize(qint32(server->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)) {
292 QVector<std::pair<quint16, quint32>> settings;
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 // quint8 weight = 0;
411 if (fr.flags & FlagHeadersPriority) {
412 // TODO disable exclusive bit
413 quint32 streamDependency = net_be32(ptr + pos);
414 if (fr.streamId == streamDependency) {
415 // qCDebug(C_SERVER_H2) << "header stream dep";
416 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
417 }
418
419 pos += 4;
420 // weight = quint8(*(ptr + pos)) + 1;
421 pos += 1;
422 }
423 ptr += pos;
424
425 H2Stream *stream;
426 auto streamIt = request->streams.constFind(fr.streamId);
427 if (streamIt != request->streams.constEnd()) {
428 stream = streamIt.value();
429
430 // qCDebug(C_SERVER_H2) << "------------" << !(fr.flags & FlagHeadersEndStream) <<
431 // stream->state << request->streamForContinuation ;
432
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);
437 }
438 if (stream->state == H2Stream::HalfClosed && request->streamForContinuation == 0) {
439 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
440 }
441 if (stream->state == H2Stream::Closed) {
442 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
443 }
444 } else {
445 if (request->maxStreamId >= fr.streamId) {
446 // qCDebug(C_SERVER_H2) << "header maxStreamId ";
447 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
448 }
449 request->maxStreamId = fr.streamId;
450
451 stream = new H2Stream(fr.streamId, request->settingsInitialWindowSize, request);
452 if (useStats) {
453 stream->startOfRequest = std::chrono::steady_clock::now();
454 }
455 request->streams.insert(fr.streamId, stream);
456 }
457
458 if (stream->state == H2Stream::Idle) {
459 stream->state = H2Stream::Open;
460 }
461
462 if (fr.flags & FlagHeadersEndStream) {
463 stream->state = H2Stream::HalfClosed;
464 }
465
466 if (!request->hpack) {
467 request->hpack = new HPack(m_headerTableSize);
468 }
469
470 if (fr.flags & FlagHeadersEndHeaders) {
471 request->streamForContinuation = 0;
472 if (!request->headersBuffer.isEmpty()) {
473 request->headersBuffer.append(ptr, qint32(fr.len) - pos - padLength);
474 }
475 } else {
476 // qCDebug(C_SERVER_H2) << "Setting HEADERS for continuation on stream" <<
477 // fr.streamId;
478 request->streamForContinuation = fr.streamId;
479 request->headersBuffer.append(ptr, qint32(fr.len) - pos - padLength);
480 return 0;
481 }
482
483 quint8 *it;
484 const quint8 *itEnd;
485 if (request->headersBuffer.size()) {
486 it = reinterpret_cast<quint8 *>(request->headersBuffer.begin());
487 itEnd = reinterpret_cast<quint8 *>(request->headersBuffer.end());
488 } else {
489 it = reinterpret_cast<quint8 *>(ptr);
490 itEnd = it + fr.len - pos - padLength;
491 }
492
493 int ret = request->hpack->decode(it, itEnd, stream);
494 if (ret) {
495 // qDebug() << "Headers parser error" << ret << QByteArray(ptr + pos, fr.len - pos -
496 // padLength).toHex();
497 return sendGoAway(io, request->maxStreamId, quint32(ret));
498 }
499
500 // qDebug() << "Headers" << padLength << streamDependency << weight << "stream headers size"
501 // << stream->headers /*<< QByteArray(ptr + pos, fr.len - pos - padLength).toHex()*/ << ret;
502
503 if ((stream->state == H2Stream::HalfClosed || fr.flags & FlagHeadersEndStream) &&
504 request->streamForContinuation == 0) {
505
506 // Process request
507 queueStream(request->sock, stream);
508 }
509
510 return 0;
511}
512
513int ProtocolHttp2::parsePriority(const ProtoRequestHttp2 *sock,
514 QIODevice *io,
515 const H2Frame &fr) const
516{
517 // qDebug() << "Consumming PRIORITY";
518 if (fr.len != 5) {
519 return sendGoAway(io, sock->maxStreamId, ErrorFrameSizeError);
520 } else if (fr.streamId == 0) {
521 return sendGoAway(io, sock->maxStreamId, ErrorProtocolError);
522 }
523
524 uint pos = 0;
525 while (fr.len > pos) {
526 // TODO store/disable EXCLUSIVE bit
527 quint32 exclusiveAndStreamDep = net_be32(sock->buffer + 9 + pos);
528 // quint8 weigth = *reinterpret_cast<quint8 *>(sock->buffer + 9 + 4 + pos) + 1;
529 // settings.push_back({ identifier, value });
530 // sock->pktsize -= 6;
531
532 if (fr.streamId == exclusiveAndStreamDep) {
533 // qDebug() << "PRIO error2" << exclusiveAndStreamDep << fr.streamId;
534
535 return sendGoAway(io, sock->maxStreamId, ErrorProtocolError);
536 }
537
538 pos += 6;
539 // qDebug() << "PRIO" << exclusiveAndStreamDep << weigth;
540 }
541
542 return 0;
543}
544
545int ProtocolHttp2::parsePing(const ProtoRequestHttp2 *request,
546 QIODevice *io,
547 const H2Frame &fr) const
548{
549 // qCDebug(C_SERVER_H2) << "Got PING" << fr.flags;
550 if (fr.len != 8) {
551 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
552 } else if (fr.streamId) {
553 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
554 }
555
556 if (!(fr.flags & FlagPingAck)) {
557 sendPing(io, FlagPingAck, request->buffer + 9, 8);
558 }
559 return 0;
560}
561
562int ProtocolHttp2::parseRstStream(ProtoRequestHttp2 *request,
563 QIODevice *io,
564 const H2Frame &fr) const
565{
566 // qCDebug(C_SERVER_H2) << "Consuming RST_STREAM";
567
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);
572 }
573
574 H2Stream *stream;
575 auto streamIt = request->streams.constFind(fr.streamId);
576 if (streamIt != request->streams.constEnd()) {
577 stream = streamIt.value();
578
579 // qCDebug(C_SERVER_H2) << "Consuming RST_STREAM state" << stream->state;
580 if (stream->state == H2Stream::Idle) {
581 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
582 }
583
584 } else {
585 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
586 }
587
588 stream->state = H2Stream::Closed;
589
590 // quint32 errorCode = h2_be32(request->buffer + 9);
591 // qCDebug(C_SERVER_H2) << "RST frame" << errorCode;
592
593 return 0;
594}
595
596int ProtocolHttp2::parseWindowUpdate(ProtoRequestHttp2 *request,
597 QIODevice *io,
598 const H2Frame &fr) const
599{
600 if (fr.len != 4) {
601 return sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
602 }
603
604 quint32 windowSizeIncrement = net_be32(request->buffer + 9);
605 if (windowSizeIncrement == 0) {
606 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
607 }
608
609 // qDebug() << "Consuming WINDOW_UPDATE" << fr.streamId << "increment" << windowSizeIncrement
610 // << request;
611
612 if (fr.streamId) {
613 H2Stream *stream;
614 auto streamIt = request->streams.constFind(fr.streamId);
615 if (streamIt != request->streams.constEnd()) {
616 stream = streamIt.value();
617
618 if (stream->state == H2Stream::Idle) {
619 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
620 }
621 } else {
622 return sendGoAway(io, request->maxStreamId, ErrorStreamClosed);
623 }
624
625 const qint64 result = qint64(stream->windowSize) + windowSizeIncrement;
626 if (result > 2147483647) {
627 stream->state = H2Stream::Closed;
628 return sendRstStream(io, fr.streamId, ErrorFlowControlError);
629 }
630 stream->windowSize = qint32(result);
631 stream->windowUpdated();
632
633 } else {
634 const qint64 result = qint64(request->windowSize) + windowSizeIncrement;
635 if (result > 2147483647) {
636 return sendGoAway(io, request->maxStreamId, ErrorFlowControlError);
637 }
638 request->windowSize = qint32(result);
639
640 if (result > 0) {
641 for (const auto &stream : std::as_const(request->streams)) {
642 stream->windowUpdated();
643 }
644 }
645 }
646
647 return 0;
648}
649
650int ProtocolHttp2::sendGoAway(QIODevice *io, quint32 lastStreamId, quint32 error) const
651{
652 // qDebug() << "GOAWAY" << error;
653 QByteArray data;
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));
661 data.append(char(error));
662 // quint64 data = error;
663 // sendFrame(io, FrameGoaway, 0, 0, reinterpret_cast<const char *>(&data), 4);
664 int ret = sendFrame(io, FrameGoaway, 0, 0, data.constData(), 8);
665 // qDebug() << ret << int(error);
666 return error || ret;
667}
668
669int ProtocolHttp2::sendRstStream(QIODevice *io, quint32 streamId, quint32 error) const
670{
671 // qDebug() << "RST_STREAM" << streamId << error;
672 QByteArray data;
673 data.append(char(error >> 24));
674 data.append(char(error >> 16));
675 data.append(char(error >> 8));
676 data.append(char(error));
677 // quint64 data = error;
678 // sendFrame(io, FrameGoaway, 0, 0, reinterpret_cast<const char *>(&data), 4);
679 int ret = sendFrame(io, FrameRstStream, 0, streamId, data.constData(), 4);
680 // qDebug() << ret << int(error);
681 return error || ret;
682}
683
684int ProtocolHttp2::sendSettings(QIODevice *io,
685 const std::vector<std::pair<quint16, quint32>> &settings) const
686{
687 QByteArray data;
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));
695 }
696 // qDebug() << "Send settings" << data.toHex();
697 return sendFrame(io, FrameSettings, 0, 0, data.constData(), data.length());
698}
699
700int ProtocolHttp2::sendSettingsAck(QIODevice *io) const
701{
702 return sendFrame(io, FrameSettings, FlagSettingsAck);
703}
704
705int ProtocolHttp2::sendPing(QIODevice *io, quint8 flags, const char *data, qint32 dataLen) const
706{
707 return sendFrame(io, FramePing, flags, 0, data, dataLen);
708}
709
710int ProtocolHttp2::sendData(QIODevice *io,
711 quint32 streamId,
712 qint32 windowSize,
713 const char *data,
714 qint32 dataLen) const
715{
716 if (windowSize < 1) {
717 // qDebug() << "Window size too small, holding";
718 return 0;
719 }
720 if (windowSize < dataLen) {
721 qint32 i = 0;
722 quint8 flags = 0;
723 while (i < dataLen) {
724 int ret = sendFrame(io, FrameData, flags, streamId, data + i, windowSize);
725 if (ret) {
726 return ret;
727 }
728 i += windowSize;
729 if ((i + 1) == dataLen) {
730 flags = FlagDataEndStream;
731 }
732 }
733 return 0;
734 } else {
735 return sendFrame(io, FrameData, FlagDataEndStream, streamId, data, dataLen);
736 }
737}
738
739int ProtocolHttp2::sendFrame(QIODevice *io,
740 quint8 type,
741 quint8 flags,
742 quint32 streamId,
743 const char *data,
744 qint32 dataLen) const
745{
746 h2_frame fr;
747
748 fr.size2 = quint8(dataLen >> 16);
749 fr.size1 = quint8(dataLen >> 8);
750 fr.size0 = quint8(dataLen);
751 fr.type = type;
752 fr.flags = flags;
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);
757
758 // qCDebug(C_SERVER_H2) << "Sending frame"
759 // << type
760 // << flags
761 // << streamId
762 // << dataLen;
763
764 // qCDebug(C_SERVER_H2) << "Frame" << QByteArray(reinterpret_cast<const char *>(&fr),
765 // sizeof(struct h2_frame)).toHex();
766 if (io->write(reinterpret_cast<const char *>(&fr), sizeof(struct h2_frame)) !=
767 sizeof(struct h2_frame)) {
768 return -1;
769 }
770 // qCDebug(C_SERVER_H2) << "Frame data" << QByteArray(data, dataLen).toHex();
771 if (dataLen && io->write(data, dataLen) != dataLen) {
772 return -1;
773 }
774 return 0;
775}
776
777void ProtocolHttp2::queueStream(Socket *socket, H2Stream *stream) const
778{
779 ++socket->processing;
780 if (stream->body) {
781 stream->body->seek(0);
782 }
783 Q_EMIT socket->engine->processRequestAsync(stream);
784}
785
786bool ProtocolHttp2::upgradeH2C(Socket *socket,
787 QIODevice *io,
788 const Cutelyst::EngineRequest &request)
789{
790 const Cutelyst::Headers &headers = request.headers;
791 if (headers.header("Upgrade").compare("h2c") == 0 &&
792 headers.connection().compare("Upgrade, HTTP2-Settings") == 0) {
793 const auto settings = headers.header("Http2-Settings");
794 if (!settings.isEmpty()) {
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;
799 auto protoRequest = new ProtoRequestHttp2(socket, m_bufferSize);
800 protoRequest->upgradedFrom = socket->protoData;
801 socket->protoData = protoRequest;
802
803 protoRequest->hpack = new HPack(m_headerTableSize);
804 protoRequest->maxStreamId = 1;
805
806 auto stream = new H2Stream(1, 65535, protoRequest);
807 stream->method = request.method;
808 stream->path = request.path;
809 stream->query = request.query;
810 stream->remoteUser = request.remoteUser;
811 stream->headers = request.headers;
812 stream->startOfRequest = std::chrono::steady_clock::now();
813 stream->status = request.status;
814 stream->body = request.body;
815
816 stream->state = H2Stream::HalfClosed;
817 protoRequest->streams.insert(1, stream);
818 protoRequest->maxStreamId = 1;
819
820 sendSettings(io,
821 {
822 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
823 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
824 });
825
826 // Process request
827 queueStream(socket, stream);
828 qCDebug(C_SERVER_H2) << "upgraded";
829 return true;
830 }
831 }
832 return false;
833}
834
835ProtoRequestHttp2::ProtoRequestHttp2(Cutelyst::Socket *sock, int bufferSize)
836 : ProtocolData(sock, bufferSize)
837{
838}
839
840ProtoRequestHttp2::~ProtoRequestHttp2()
841{
842}
843
844void ProtoRequestHttp2::setupNewConnection(Socket *sock)
845{
846 Q_UNUSED(sock)
847}
848
849H2Stream::H2Stream(quint32 _streamId, qint32 _initialWindowSize, ProtoRequestHttp2 *protoRequestH2)
850 : protoRequest(protoRequestH2)
851 , streamId(_streamId)
852 , windowSize(_initialWindowSize)
853{
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;
859}
860
861H2Stream::~H2Stream()
862{
863 if (loop) {
864 loop->exit(-1);
865 delete loop;
866 }
867}
868
869qint64 H2Stream::doWrite(const char *data, qint64 len)
870{
871 int ret = -1;
872 auto parser = dynamic_cast<ProtocolHttp2 *>(protoRequest->sock->proto);
873
874 qint64 remainingData = len;
875 qint64 sent = 0;
876 while (remainingData > 0 && state != H2Stream::Closed) {
877 qint64 availableWindowSize = qMin(windowSize, protoRequest->windowSize);
878 // qCDebug(C_SERVER_H2) << "H2Stream::doWrite" << len << streamId <<
879 // "availableWindowSize" << availableWindowSize
880 // << "remaining data" << remainingData
881 // << "stream" << this << protoRequest;
882 availableWindowSize =
883 qMin(availableWindowSize, (qint64) protoRequest->settingsMaxFrameSize);
884 if (availableWindowSize == 0) {
885 if (!loop) {
886 loop = new QEventLoop;
887 }
888 if (loop->exec() == 0) {
889 continue;
890 }
891 return -1;
892 }
893
894 // qCDebug(C_SERVER_H2) << "H2Stream::doWrite" << len << streamId <<
895 // "availableWindowSize" << availableWindowSize
896 // << "remaining data" << remainingData;
897
898 if (availableWindowSize > remainingData) {
899 ret = parser->sendFrame(protoRequest->io,
900 FrameData,
901 FlagDataEndStream,
902 streamId,
903 data + sent,
904 qint32(remainingData));
905 remainingData = 0;
906 protoRequest->windowSize -= remainingData;
907 windowSize -= remainingData;
908 sent += remainingData;
909 } else {
910 ret = parser->sendFrame(protoRequest->io,
911 FrameData,
912 0x0,
913 streamId,
914 data + sent,
915 qint32(availableWindowSize));
916 remainingData -= availableWindowSize;
917 protoRequest->windowSize -= availableWindowSize;
918 windowSize -= availableWindowSize;
919 sent += availableWindowSize;
920 }
921
922 // qCDebug(C_SERVER_H2) << "H2Stream::doWrite ret" << ret << (ret == 0 ? len : -1);
923 }
924
925 return ret == 0 ? len : -1;
926}
927
929{
930 QByteArray buf;
931 protoRequest->hpack->encodeHeaders(
932 status, headers, buf, static_cast<ServerEngine *>(protoRequest->sock->engine));
933
934 auto parser = dynamic_cast<ProtocolHttp2 *>(protoRequest->sock->proto);
935
936 int ret = parser->sendFrame(protoRequest->io,
937 FrameHeaders,
938 FlagHeadersEndHeaders,
939 streamId,
940 buf.constData(),
941 buf.size());
942
943 return ret == 0;
944}
945
947{
948 state = Closed;
949 protoRequest->streams.remove(streamId);
950 protoRequest->sock->requestFinished();
951 delete this;
952}
953
954void H2Stream::windowUpdated()
955{
956 // qDebug() << "WINDOW_UPDATED" << protoRequest->windowSize << windowSize << loop << (loop &&
957 // loop->isRunning()) << this << protoRequest;
958
959 if (protoRequest->windowSize > 0 && windowSize > 0 && loop && loop->isRunning()) {
960 loop->quit();
961 }
962}
963
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
Container for HTTP headers.
Definition headers.h:24
QByteArray connection() const noexcept
Definition headers.cpp:322
QByteArray header(QAnyStringView key) const noexcept
Definition headers.cpp:419
Implements a web server.
Definition server.h:60
The Cutelyst namespace holds all public Cutelyst API.