cutelyst  4.3.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
protocolhttp2.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2018 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "protocolhttp2.h"
6 
7 #include "hpack.h"
8 #include "server.h"
9 #include "socket.h"
10 
11 #include <QEventLoop>
12 #include <QLoggingCategory>
13 
14 using namespace Cutelyst;
15 
16 Q_LOGGING_CATEGORY(C_SERVER_H2, "cutelyst.server.http2", QtWarningMsg)
17 
18 struct 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 
30 enum SettingsFlags { FlagSettingsAck = 0x1 };
31 
32 enum PingFlags { FlagPingAck = 0x1 };
33 
34 enum HeaderFlags {
35  FlagHeadersEndStream = 0x1,
36  FlagHeadersEndHeaders = 0x4,
37  FlagHeadersPadded = 0x8,
38  FlagHeadersPriority = 0x20,
39 };
40 
41 enum PushPromiseFlags {
42  FlagPushPromiseEndHeaders = 0x4,
43  FlagPushPromisePadded = 0x8,
44 };
45 
46 enum DataFlags {
47  FlagDataEndStream = 0x1,
48  FlagDataPadded = 0x8,
49 };
50 
51 enum 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 
64 enum 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 
81 enum 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 
93 ProtocolHttp2::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 
109 ProtocolHttp2::~ProtocolHttp2()
110 {
111 }
112 
113 Protocol::Type ProtocolHttp2::type() const
114 {
115  return Protocol::Type::Http2;
116 }
117 
118 void 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 
272 ProtocolData *ProtocolHttp2::createData(Socket *sock) const
273 {
274  return new ProtoRequestHttp2(sock, m_bufferSize);
275 }
276 
277 int ProtocolHttp2::parseSettings(ProtoRequestHttp2 *request, QIODevice *io, const H2Frame &fr) const
278 {
279  // qDebug() << "Consumming SETTINGS";
280  if ((fr.flags & FlagSettingsAck && fr.len) || fr.len % 6) {
281  sendGoAway(io, request->maxStreamId, ErrorFrameSizeError);
282  return 1;
283  } else if (fr.streamId) {
284  sendGoAway(io, request->maxStreamId, ErrorProtocolError);
285  return 1;
286  }
287 
288  if (!(fr.flags & FlagSettingsAck)) {
290  uint pos = 0;
291  while (request->pktsize > pos) {
292  quint16 identifier = net_be16(request->buffer + 9 + pos);
293  quint32 value = net_be32(request->buffer + 9 + 2 + pos);
294  settings.push_back({identifier, value});
295  pos += 6;
296  // qDebug() << "SETTINGS" << identifier << value;
297  if (identifier == SETTINGS_ENABLE_PUSH) {
298  if (value > 1) {
299  return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
300  }
301 
302  request->canPush = value;
303  } else if (identifier == SETTINGS_INITIAL_WINDOW_SIZE) {
304  if (value > 2147483647) {
305  return sendGoAway(io, request->maxStreamId, ErrorFlowControlError);
306  }
307 
308  const qint32 difference = qint32(value) - request->settingsInitialWindowSize;
309  request->settingsInitialWindowSize = qint32(value);
310 
311  auto it = request->streams.begin();
312  while (it != request->streams.end()) {
313  (*it)->windowSize += difference;
314  (*it)->windowUpdated();
315  // qCDebug(C_SERVER_H2) << "updating stream" << it.key() <<
316  // "to window" << (*it)->windowSize;
317  ++it;
318  }
319  } else if (identifier == SETTINGS_MAX_FRAME_SIZE) {
320  if (value < 16384 || value > 16777215) {
321  return sendGoAway(io, request->maxStreamId, ErrorProtocolError);
322  }
323  request->settingsMaxFrameSize = value;
324  }
325  }
326  sendSettingsAck(io);
327  }
328 
329  return ErrorNoError;
330 }
331 
332 int 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 
387 int 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 
513 int 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 
543 int 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 
558 int 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 
592 int 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 
648 int 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 
667 int 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 
682 int 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 
698 int ProtocolHttp2::sendSettingsAck(QIODevice *io) const
699 {
700  return sendFrame(io, FrameSettings, FlagSettingsAck);
701 }
702 
703 int ProtocolHttp2::sendPing(QIODevice *io, quint8 flags, const char *data, qint32 dataLen) const
704 {
705  return sendFrame(io, FramePing, flags, 0, data, dataLen);
706 }
707 
708 int 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 
737 int 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 
775 void 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 
784 bool 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 
833 ProtoRequestHttp2::ProtoRequestHttp2(Cutelyst::Socket *sock, int bufferSize)
834  : ProtocolData(sock, bufferSize)
835 {
836 }
837 
838 ProtoRequestHttp2::~ProtoRequestHttp2()
839 {
840 }
841 
842 void ProtoRequestHttp2::setupNewConnection(Socket *sock)
843 {
844  Q_UNUSED(sock)
845 }
846 
847 H2Stream::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 
859 H2Stream::~H2Stream()
860 {
861  if (loop) {
862  loop->exit(-1);
863  delete loop;
864  }
865 }
866 
867 qint64 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 
926 bool 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 
952 void 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"
void quit()
void push_back(QList::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
int compare(QByteArrayView bv, Qt::CaseSensitivity cs) const const
TimePointSteady startOfRequest
int exec(QEventLoop::ProcessEventsFlags flags)
void exit(int returnCode)
const char * constData() const const
QByteArray number(int n, int base)
QByteArray::iterator begin()
void processRequestAsync(Cutelyst::EngineRequest *request)
void processingFinished() override final
qint64 read(char *data, qint64 maxSize)
The Cutelyst namespace holds all public Cutelyst API.
virtual qint64 bytesAvailable() const const
QByteArray & append(char ch)
QByteArray connection() const noexcept
Definition: headers.cpp:295
qint64 doWrite(const char *data, qint64 len) override final
QByteArray header(QByteArrayView key) const noexcept
Definition: headers.cpp:392
qint64 write(const char *data, qint64 maxSize)
qsizetype size() const const
QByteArray::iterator end()