cutelyst 4.4.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)) {
289 QVector<std::pair<quint16, quint32>> settings;
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.