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 
14 using namespace Cutelyst;
15 using namespace Qt::Literals::StringLiterals;
16 
17 Q_LOGGING_CATEGORY(C_SERVER_H2, "cutelyst.server.http2", QtWarningMsg)
18 
19 struct 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 
31 enum SettingsFlags { FlagSettingsAck = 0x1 };
32 
33 enum PingFlags { FlagPingAck = 0x1 };
34 
35 enum HeaderFlags {
36  FlagHeadersEndStream = 0x1,
37  FlagHeadersEndHeaders = 0x4,
38  FlagHeadersPadded = 0x8,
39  FlagHeadersPriority = 0x20,
40 };
41 
42 enum PushPromiseFlags {
43  FlagPushPromiseEndHeaders = 0x4,
44  FlagPushPromisePadded = 0x8,
45 };
46 
47 enum DataFlags {
48  FlagDataEndStream = 0x1,
49  FlagDataPadded = 0x8,
50 };
51 
52 enum 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 
65 enum 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 
82 enum 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 
94 ProtocolHttp2::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 
110 ProtocolHttp2::~ProtocolHttp2()
111 {
112 }
113 
114 Protocol::Type ProtocolHttp2::type() const
115 {
116  return Protocol::Type::Http2;
117 }
118 
119 void 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 
273 ProtocolData *ProtocolHttp2::createData(Socket *sock) const
274 {
275  return new ProtoRequestHttp2(sock, m_bufferSize);
276 }
277 
278 int 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)) {
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 
333 int 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 
388 int 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 
514 int 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 
544 int 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 
559 int 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 
593 int 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 
649 int 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 
668 int 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 
683 int 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 
699 int ProtocolHttp2::sendSettingsAck(QIODevice *io) const
700 {
701  return sendFrame(io, FrameSettings, FlagSettingsAck);
702 }
703 
704 int ProtocolHttp2::sendPing(QIODevice *io, quint8 flags, const char *data, qint32 dataLen) const
705 {
706  return sendFrame(io, FramePing, flags, 0, data, dataLen);
707 }
708 
709 int 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 
738 int 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 
776 void 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 
785 bool 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 
834 ProtoRequestHttp2::ProtoRequestHttp2(Cutelyst::Socket *sock, int bufferSize)
835  : ProtocolData(sock, bufferSize)
836 {
837 }
838 
839 ProtoRequestHttp2::~ProtoRequestHttp2()
840 {
841 }
842 
843 void ProtoRequestHttp2::setupNewConnection(Socket *sock)
844 {
845  Q_UNUSED(sock)
846 }
847 
848 H2Stream::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 
860 H2Stream::~H2Stream()
861 {
862  if (loop) {
863  loop->exit(-1);
864  delete loop;
865  }
866 }
867 
868 qint64 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 
927 bool H2Stream::writeHeaders(quint16 status, const Cutelyst::Headers &headers)
928 {
929  QByteArray buf;
930  protoRequest->hpack->encodeHeaders(
931  status, headers, buf, static_cast<ServerEngine *>(protoRequest->sock->engine));
932 
933  auto parser = dynamic_cast<ProtocolHttp2 *>(protoRequest->sock->proto);
934 
935  int ret = parser->sendFrame(protoRequest->io,
936  FrameHeaders,
937  FlagHeadersEndHeaders,
938  streamId,
939  buf.constData(),
940  buf.size());
941 
942  return ret == 0;
943 }
944 
946 {
947  state = Closed;
948  protoRequest->streams.remove(streamId);
949  protoRequest->sock->requestFinished();
950  delete this;
951 }
952 
953 void 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"
void quit()
void push_back(parameter_type value)
virtual bool seek(qint64 pos)
bool writeHeaders(quint16 status, const Cutelyst::Headers &headers) override final
QString errorString() const const
bool isEmpty() const const
Container for HTTP headers.
Definition: headers.h:23
Implements a web server.
Definition: server.h:59
QString toString() const const
qsizetype length() const const
bool isRunning() const const
QByteArray read(qint64 maxSize)
int compare(QByteArrayView bv, Qt::CaseSensitivity cs) const const
TimePointSteady startOfRequest
QByteArray number(double n, char format, int precision)
int exec(ProcessEventsFlags flags)
void exit(int returnCode)
const char * constData() const const
iterator begin()
void processRequestAsync(Cutelyst::EngineRequest *request)
qint64 write(const QByteArray &data)
void processingFinished() override final
The Cutelyst namespace holds all public Cutelyst API.
virtual qint64 bytesAvailable() const const
QByteArray & append(QByteArrayView data)
QByteArray connection() const noexcept
Definition: headers.cpp:296
qint64 doWrite(const char *data, qint64 len) override final
QByteArray header(QByteArrayView key) const noexcept
Definition: headers.cpp:393
qsizetype size() const const
iterator end()