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 *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)) {
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
928bool H2Stream::writeHeaders(quint16 status, const Cutelyst::Headers &headers)
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.
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)