cutelyst 4.8.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
19struct h2_frame {
20 quint8 size2;
21 quint8 size1;
22 quint8 size0;
23 quint8 type;
24 quint8 flags;
25 quint8 rbit_stream_id3;
26 quint8 rbit_stream_id2;
27 quint8 rbit_stream_id1;
28 quint8 rbit_stream_id0;
29};
30
31enum SettingsFlags { FlagSettingsAck = 0x1 };
32
33enum PingFlags { FlagPingAck = 0x1 };
34
35enum HeaderFlags {
36 FlagHeadersEndStream = 0x1,
37 FlagHeadersEndHeaders = 0x4,
38 FlagHeadersPadded = 0x8,
39 FlagHeadersPriority = 0x20,
40};
41
42enum PushPromiseFlags {
43 FlagPushPromiseEndHeaders = 0x4,
44 FlagPushPromisePadded = 0x8,
45};
46
47enum DataFlags {
48 FlagDataEndStream = 0x1,
49 FlagDataPadded = 0x8,
50};
51
52enum FrameType {
53 FrameData = 0x0,
54 FrameHeaders = 0x1,
55 FramePriority = 0x2,
56 FrameRstStream = 0x3,
57 FrameSettings = 0x4,
58 FramePushPromise = 0x5,
59 FramePing = 0x6,
60 FrameGoaway = 0x7,
61 FrameWindowUpdate = 0x8,
62 FrameContinuation = 0x9
63};
64
65enum ErrorCodes {
66 ErrorNoError = 0x0,
67 ErrorProtocolError = 0x1,
68 ErrorInternalError = 0x2,
69 ErrorFlowControlError = 0x3,
70 ErrorSettingsTimeout = 0x4,
71 ErrorStreamClosed = 0x5,
72 ErrorFrameSizeError = 0x6,
73 ErrorRefusedStream = 0x7,
74 ErrorCancel = 0x8,
75 ErrorCompressionError = 0x9,
76 ErrorConnectError = 0xA,
77 ErrorEnhanceYourCalm = 0xB,
78 ErrorInadequateSecurity = 0xC,
79 ErrorHttp11Required = 0xD
80};
81
82enum Settings {
83 SETTINGS_HEADER_TABLE_SIZE = 0x1,
84 SETTINGS_ENABLE_PUSH = 0x2,
85 SETTINGS_MAX_CONCURRENT_STREAMS = 0x3,
86 SETTINGS_INITIAL_WINDOW_SIZE = 0x4,
87 SETTINGS_MAX_FRAME_SIZE = 0x5,
88 SETTINGS_MAX_HEADER_LIST_SIZE = 0x6,
89 SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x8,
90};
91
92#define PREFACE_SIZE 24
93
94ProtocolHttp2::ProtocolHttp2(Server *wsgi)
95 : Protocol(wsgi)
96 , m_headerTableSize(qint32(wsgi->http2HeaderTableSize()))
97{
98 m_bufferSize = qMin(m_bufferSize, 2147483647);
99
100 // 2^14 + 9 (octects)
101 if (m_bufferSize < 16393) {
102 qFatal("HTTP/2 Protocol requires that buffer-size to be at least '16393' in size, current "
103 "value is '%s'",
104 QByteArray::number(m_bufferSize).constData());
105 }
106
107 m_maxFrameSize = quint32(m_bufferSize - 9);
108}
109
110ProtocolHttp2::~ProtocolHttp2()
111{
112}
113
114Protocol::Type ProtocolHttp2::type() const
115{
116 return Protocol::Type::Http2;
117}
118
119void ProtocolHttp2::parse(Socket *sock, QIODevice *io) const
120{
121 auto request = static_cast<ProtoRequestHttp2 *>(sock->protoData);
122
123 qint64 bytesAvailable = io->bytesAvailable();
124 // qCDebug(C_SERVER_H2) << sock << "READ available" << bytesAvailable << "buffer size" <<
125 // request->buf_size << "default buffer size" << m_bufferSize ;
126
127 do {
128 const qint64 len =
129 io->read(request->buffer + request->buf_size, m_bufferSize - request->buf_size);
130 bytesAvailable -= len;
131
132 if (len > 0) {
133 request->buf_size += len;
134 int ret = 0;
135 while (request->buf_size && ret == 0) {
136 // qDebug() << "Current buffer size" << request->connState <<
137 // request->buf_size;//QByteArray(request->buffer,
138 // request->buf_size);
139 if (request->connState == ProtoRequestHttp2::MethodLine) {
140 if (request->buf_size >= PREFACE_SIZE) {
141 if (memcmp(request->buffer, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24) == 0) {
142 // qCDebug(C_SERVER_H2) << "Got MAGIC" <<
143 // sizeof(struct h2_frame);
144 request->buf_size -= PREFACE_SIZE;
145 memmove(request->buffer,
146 request->buffer + PREFACE_SIZE,
147 size_t(request->buf_size));
148 request->connState = ProtoRequestHttp2::H2Frames;
149
150 sendSettings(io,
151 {
152 {SETTINGS_ENABLE_CONNECT_PROTOCOL, 0},
153 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
154 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
155 });
156 } else {
157 qCDebug(C_SERVER_H2) << "Protocol Error: Invalid connection preface"
158 << sock->remoteAddress.toString();
159 // RFC 7540 says this MAY be omitted, so let's reduce further processing
160 // ret = sendGoAway(io, request->maxStreamId,
161 // ErrorProtocolError);
162 sock->connectionClose();
163 return;
164 }
165 } else {
166 // qDebug() << "MAGIC needs more data" <<
167 // bytesAvailable;
168 break;
169 }
170 } else if (request->connState == ProtoRequestHttp2::H2Frames) {
171 if (request->buf_size >= int(sizeof(struct h2_frame))) {
172 auto fr = reinterpret_cast<struct h2_frame *>(request->buffer);
173 H2Frame frame;
174 frame.len = quint32(fr->size0 | (fr->size1 << 8) | (fr->size2 << 16));
175 frame.streamId =
176 quint32(fr->rbit_stream_id0 | (fr->rbit_stream_id1 << 8) |
177 (fr->rbit_stream_id2 << 16) |
178 ((fr->rbit_stream_id3 & ~0x80) << 24)); // Ignore first bit
179 frame.type = fr->type;
180 frame.flags = fr->flags;
181 request->pktsize =
182 quint32(fr->size0 | (fr->size1 << 8) | (fr->size2 << 16));
183 request->stream_id =
184 quint32(fr->rbit_stream_id0 | (fr->rbit_stream_id1 << 8) |
185 (fr->rbit_stream_id2 << 16) |
186 ((fr->rbit_stream_id3 & ~0x80) << 24)); // Ignore first bit
187
188 // qDebug() << "Frame type" << fr->type
189 // << "flags" << fr->flags
190 // << "stream-id" << request->stream_id
191 // << "required size" << request->pktsize
192 // << "available" << (request->buf_size -
193 // sizeof(struct h2_frame));
194
195 if (frame.streamId && !(frame.streamId & 1)) {
196 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
197 break;
198 }
199
200 if (request->pktsize > m_maxFrameSize) {
201 // qDebug() << "Frame too big" <<
202 // request->pktsize << m_bufferSize;
203 ret = sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
204 break;
205 }
206
207 if (request->pktsize >
208 (quint32(request->buf_size) - sizeof(struct h2_frame))) {
209 // qDebug() << "need more data" <<
210 // bytesAvailable;
211 break;
212 }
213
214 if (request->streamForContinuation) {
215 if (fr->type == FrameContinuation &&
216 request->streamForContinuation == frame.streamId) {
217 fr->type = FrameHeaders;
218 } else {
219 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
220 break;
221 }
222 }
223
224 if (fr->type == FrameSettings) {
225 ret = parseSettings(request, io, frame);
226 } else if (fr->type == FramePriority) {
227 ret = parsePriority(request, io, frame);
228 } else if (fr->type == FrameHeaders) {
229 ret = parseHeaders(request, io, frame);
230 } else if (fr->type == FramePing) {
231 ret = parsePing(request, io, frame);
232 } else if (fr->type == FrameData) {
233 ret = parseData(request, io, frame);
234 } else if (fr->type == FramePushPromise) {
235 // Client can not PUSH
236 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
237 break;
238 } else if (fr->type == FrameRstStream) {
239 ret = parseRstStream(request, io, frame);
240 } else if (fr->type == FrameWindowUpdate) {
241 ret = parseWindowUpdate(request, io, frame);
242 } else if (fr->type == FrameGoaway) {
243 sock->connectionClose();
244 return;
245 } else if (fr->type == FrameContinuation) {
246 ret = sendGoAway(io, request->maxStreamId, ErrorProtocolError);
247 break;
248 } else {
249 qCDebug(C_SERVER_H2) << "Unknown frame type" << fr->type;
250 // Implementations MUST ignore and discard any frame that has a type
251 // that is unknown.
252 }
253
254 request->buf_size -= 9 + request->pktsize;
255 memmove(request->buffer,
256 request->buffer + 9 + request->pktsize,
257 size_t(request->buf_size));
258 }
259 }
260 }
261
262 if (ret) {
263 // qDebug() << "Got error closing" << ret;
264 sock->connectionClose();
265 }
266 } else {
267 qCWarning(C_SERVER_H2) << "Failed to read from socket" << io->errorString();
268 break;
269 }
270 } while (bytesAvailable);
271}
272
273ProtocolData *ProtocolHttp2::createData(Socket *sock) const
274{
275 return new ProtoRequestHttp2(sock, m_bufferSize);
276}
277
278int ProtocolHttp2::parseSettings(ProtoRequestHttp2 *request, QIODevice *io, const H2Frame &fr) const
279{
280 // qDebug() << "Consumming SETTINGS";
281 if ((fr.flags & FlagSettingsAck && fr.len) || fr.len % 6) {
282 sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
283 return 1;
284 } else if (fr.streamId) {
285 sendGoAway(io, request->maxStreamId, ErrorProtocolError);
286 return 1;
287 }
288
289 if (!(fr.flags & FlagSettingsAck)) {
290 QVector<std::pair<quint16, quint32>> settings;
291 uint pos = 0;
292 while (request->pktsize > pos) {
293 quint16 identifier = net_be16(request->buffer + 9 + pos);
294 quint32 value = net_be32(request->buffer + 9 + 2 + pos);
295 settings.push_back({identifier, value});
296 pos += 6;
297 // qDebug() << "SETTINGS" << identifier << value;
298 if (identifier == SETTINGS_ENABLE_PUSH) {
299 if (value > 1) {
300 return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
301 }
302
303 request->canPush = value;
304 } else if (identifier == SETTINGS_INITIAL_WINDOW_SIZE) {
305 if (value > 2147483647) {
306 return sendGoAway(io, request->maxStreamId, ErrorFlowControlError);
307 }
308
309 const qint32 difference = qint32(value) - request->settingsInitialWindowSize;
310 request->settingsInitialWindowSize = qint32(value);
311
312 auto it = request->streams.begin();
313 while (it != request->streams.end()) {
314 (*it)->windowSize += difference;
315 (*it)->windowUpdated();
316 // qCDebug(C_SERVER_H2) << "updating stream" << it.key() <<
317 // "to window" << (*it)->windowSize;
318 ++it;
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 auto streamIt = request->streams.constBegin();
639 if (streamIt != request->streams.constEnd()) {
640 (*streamIt)->windowUpdated();
641 ++streamIt;
642 }
643 }
644 }
645
646 return 0;
647}
648
649int ProtocolHttp2::sendGoAway(QIODevice *io, quint32 lastStreamId, quint32 error) const
650{
651 // qDebug() << "GOAWAY" << error;
652 QByteArray data;
653 data.append(char(lastStreamId >> 24));
654 data.append(char(lastStreamId >> 16));
655 data.append(char(lastStreamId >> 8));
656 data.append(char(lastStreamId));
657 data.append(char(error >> 24));
658 data.append(char(error >> 16));
659 data.append(char(error >> 8));
660 data.append(char(error));
661 // quint64 data = error;
662 // sendFrame(io, FrameGoaway, 0, 0, reinterpret_cast<const char *>(&data), 4);
663 int ret = sendFrame(io, FrameGoaway, 0, 0, data.constData(), 8);
664 // qDebug() << ret << int(error);
665 return error || ret;
666}
667
668int ProtocolHttp2::sendRstStream(QIODevice *io, quint32 streamId, quint32 error) const
669{
670 // qDebug() << "RST_STREAM" << streamId << error;
671 QByteArray data;
672 data.append(char(error >> 24));
673 data.append(char(error >> 16));
674 data.append(char(error >> 8));
675 data.append(char(error));
676 // quint64 data = error;
677 // sendFrame(io, FrameGoaway, 0, 0, reinterpret_cast<const char *>(&data), 4);
678 int ret = sendFrame(io, FrameRstStream, 0, streamId, data.constData(), 4);
679 // qDebug() << ret << int(error);
680 return error || ret;
681}
682
683int ProtocolHttp2::sendSettings(QIODevice *io,
684 const std::vector<std::pair<quint16, quint32>> &settings) const
685{
686 QByteArray data;
687 for (const std::pair<quint16, quint32> &pair : settings) {
688 data.append(char(pair.first >> 8));
689 data.append(char(pair.first));
690 data.append(char(pair.second >> 24));
691 data.append(char(pair.second >> 16));
692 data.append(char(pair.second >> 8));
693 data.append(char(pair.second));
694 }
695 // qDebug() << "Send settings" << data.toHex();
696 return sendFrame(io, FrameSettings, 0, 0, data.constData(), data.length());
697}
698
699int ProtocolHttp2::sendSettingsAck(QIODevice *io) const
700{
701 return sendFrame(io, FrameSettings, FlagSettingsAck);
702}
703
704int ProtocolHttp2::sendPing(QIODevice *io, quint8 flags, const char *data, qint32 dataLen) const
705{
706 return sendFrame(io, FramePing, flags, 0, data, dataLen);
707}
708
709int ProtocolHttp2::sendData(QIODevice *io,
710 quint32 streamId,
711 qint32 windowSize,
712 const char *data,
713 qint32 dataLen) const
714{
715 if (windowSize < 1) {
716 // qDebug() << "Window size too small, holding";
717 return 0;
718 }
719 if (windowSize < dataLen) {
720 qint32 i = 0;
721 quint8 flags = 0;
722 while (i < dataLen) {
723 int ret = sendFrame(io, FrameData, flags, streamId, data + i, windowSize);
724 if (ret) {
725 return ret;
726 }
727 i += windowSize;
728 if ((i + 1) == dataLen) {
729 flags = FlagDataEndStream;
730 }
731 }
732 return 0;
733 } else {
734 return sendFrame(io, FrameData, FlagDataEndStream, streamId, data, dataLen);
735 }
736}
737
738int ProtocolHttp2::sendFrame(QIODevice *io,
739 quint8 type,
740 quint8 flags,
741 quint32 streamId,
742 const char *data,
743 qint32 dataLen) const
744{
745 h2_frame fr;
746
747 fr.size2 = quint8(dataLen >> 16);
748 fr.size1 = quint8(dataLen >> 8);
749 fr.size0 = quint8(dataLen);
750 fr.type = type;
751 fr.flags = flags;
752 fr.rbit_stream_id3 = quint8(streamId >> 24);
753 fr.rbit_stream_id2 = quint8(streamId >> 16);
754 fr.rbit_stream_id1 = quint8(streamId >> 8);
755 fr.rbit_stream_id0 = quint8(streamId);
756
757 // qCDebug(C_SERVER_H2) << "Sending frame"
758 // << type
759 // << flags
760 // << streamId
761 // << dataLen;
762
763 // qCDebug(C_SERVER_H2) << "Frame" << QByteArray(reinterpret_cast<const char *>(&fr),
764 // sizeof(struct h2_frame)).toHex();
765 if (io->write(reinterpret_cast<const char *>(&fr), sizeof(struct h2_frame)) !=
766 sizeof(struct h2_frame)) {
767 return -1;
768 }
769 // qCDebug(C_SERVER_H2) << "Frame data" << QByteArray(data, dataLen).toHex();
770 if (dataLen && io->write(data, dataLen) != dataLen) {
771 return -1;
772 }
773 return 0;
774}
775
776void ProtocolHttp2::queueStream(Socket *socket, H2Stream *stream) const
777{
778 ++socket->processing;
779 if (stream->body) {
780 stream->body->seek(0);
781 }
782 Q_EMIT socket->engine->processRequestAsync(stream);
783}
784
785bool ProtocolHttp2::upgradeH2C(Socket *socket,
786 QIODevice *io,
787 const Cutelyst::EngineRequest &request)
788{
789 const Cutelyst::Headers &headers = request.headers;
790 if (headers.header("Upgrade").compare("h2c") == 0 &&
791 headers.connection().compare("Upgrade, HTTP2-Settings") == 0) {
792 const auto settings = headers.header("Http2-Settings");
793 if (!settings.isEmpty()) {
794 io->write("HTTP/1.1 101 Switching Protocols\r\n"
795 "Connection: Upgrade\r\n"
796 "Upgrade: h2c\r\n\r\n");
797 socket->proto = this;
798 auto protoRequest = new ProtoRequestHttp2(socket, m_bufferSize);
799 protoRequest->upgradedFrom = socket->protoData;
800 socket->protoData = protoRequest;
801
802 protoRequest->hpack = new HPack(m_headerTableSize);
803 protoRequest->maxStreamId = 1;
804
805 auto stream = new H2Stream(1, 65535, protoRequest);
806 stream->method = request.method;
807 stream->path = request.path;
808 stream->query = request.query;
809 stream->remoteUser = request.remoteUser;
810 stream->headers = request.headers;
811 stream->startOfRequest = std::chrono::steady_clock::now();
812 stream->status = request.status;
813 stream->body = request.body;
814
815 stream->state = H2Stream::HalfClosed;
816 protoRequest->streams.insert(1, stream);
817 protoRequest->maxStreamId = 1;
818
819 sendSettings(io,
820 {
821 {SETTINGS_MAX_FRAME_SIZE, m_maxFrameSize},
822 {SETTINGS_HEADER_TABLE_SIZE, m_headerTableSize},
823 });
824
825 // Process request
826 queueStream(socket, stream);
827 qCDebug(C_SERVER_H2) << "upgraded";
828 return true;
829 }
830 }
831 return false;
832}
833
834ProtoRequestHttp2::ProtoRequestHttp2(Cutelyst::Socket *sock, int bufferSize)
835 : ProtocolData(sock, bufferSize)
836{
837}
838
839ProtoRequestHttp2::~ProtoRequestHttp2()
840{
841}
842
843void ProtoRequestHttp2::setupNewConnection(Socket *sock)
844{
845 Q_UNUSED(sock)
846}
847
848H2Stream::H2Stream(quint32 _streamId, qint32 _initialWindowSize, ProtoRequestHttp2 *protoRequestH2)
849 : protoRequest(protoRequestH2)
850 , streamId(_streamId)
851 , windowSize(_initialWindowSize)
852{
853 protocol = "HTTP/2"_ba;
854 serverAddress = protoRequestH2->sock->serverAddress;
855 remoteAddress = protoRequestH2->sock->remoteAddress;
856 remotePort = protoRequestH2->sock->remotePort;
857 isSecure = protoRequestH2->sock->isSecure;
858}
859
860H2Stream::~H2Stream()
861{
862 if (loop) {
863 loop->exit(-1);
864 delete loop;
865 }
866}
867
868qint64 H2Stream::doWrite(const char *data, qint64 len)
869{
870 int ret = -1;
871 auto parser = dynamic_cast<ProtocolHttp2 *>(protoRequest->sock->proto);
872
873 qint64 remainingData = len;
874 qint64 sent = 0;
875 while (remainingData > 0 && state != H2Stream::Closed) {
876 qint64 availableWindowSize = qMin(windowSize, protoRequest->windowSize);
877 // qCDebug(C_SERVER_H2) << "H2Stream::doWrite" << len << streamId <<
878 // "availableWindowSize" << availableWindowSize
879 // << "remaining data" << remainingData
880 // << "stream" << this << protoRequest;
881 availableWindowSize =
882 qMin(availableWindowSize, (qint64) protoRequest->settingsMaxFrameSize);
883 if (availableWindowSize == 0) {
884 if (!loop) {
885 loop = new QEventLoop;
886 }
887 if (loop->exec() == 0) {
888 continue;
889 }
890 return -1;
891 }
892
893 // qCDebug(C_SERVER_H2) << "H2Stream::doWrite" << len << streamId <<
894 // "availableWindowSize" << availableWindowSize
895 // << "remaining data" << remainingData;
896
897 if (availableWindowSize > remainingData) {
898 ret = parser->sendFrame(protoRequest->io,
899 FrameData,
900 FlagDataEndStream,
901 streamId,
902 data + sent,
903 qint32(remainingData));
904 remainingData = 0;
905 protoRequest->windowSize -= remainingData;
906 windowSize -= remainingData;
907 sent += remainingData;
908 } else {
909 ret = parser->sendFrame(protoRequest->io,
910 FrameData,
911 0x0,
912 streamId,
913 data + sent,
914 qint32(availableWindowSize));
915 remainingData -= availableWindowSize;
916 protoRequest->windowSize -= availableWindowSize;
917 windowSize -= availableWindowSize;
918 sent += availableWindowSize;
919 }
920
921 // qCDebug(C_SERVER_H2) << "H2Stream::doWrite ret" << ret << (ret == 0 ? len : -1);
922 }
923
924 return ret == 0 ? len : -1;
925}
926
927bool H2Stream::writeHeaders(quint16 status, const Cutelyst::Headers &headers)
928{
929 QByteArray buf;
930 protoRequest->hpack->encodeHeaders(
931 status, headers, buf, static_cast<ServerEngine *>(protoRequest->sock->engine));
932
933 auto parser = dynamic_cast<ProtocolHttp2 *>(protoRequest->sock->proto);
934
935 int ret = parser->sendFrame(protoRequest->io,
936 FrameHeaders,
937 FlagHeadersEndHeaders,
938 streamId,
939 buf.constData(),
940 buf.size());
941
942 return ret == 0;
943}
944
946{
947 state = Closed;
948 protoRequest->streams.remove(streamId);
949 protoRequest->sock->requestFinished();
950 delete this;
951}
952
953void H2Stream::windowUpdated()
954{
955 // qDebug() << "WINDOW_UPDATED" << protoRequest->windowSize << windowSize << loop << (loop &&
956 // loop->isRunning()) << this << protoRequest;
957
958 if (protoRequest->windowSize > 0 && windowSize > 0 && loop && loop->isRunning()) {
959 loop->quit();
960 }
961}
962
963#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(QByteArrayView key) const noexcept
Definition headers.cpp:393
Implements a web server.
Definition server.h:60
The Cutelyst namespace holds all public Cutelyst API.