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