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