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
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.
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
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)
void push_back(QList< T >::parameter_type value)