cutelyst  4.4.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
protocolhttp2.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2018 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "protocolhttp2.h"
6 
7 #include "hpack.h"
8 #include "server.h"
9 #include "socket.h"
10 
11 #include <QEventLoop>
12 #include <QLoggingCategory>
13 
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"
TimePointSteady startOfRequest
void processRequestAsync(Cutelyst::EngineRequest *request)
qint64 doWrite(const char *data, qint64 len) override final
void processingFinished() override final
bool writeHeaders(quint16 status, const Cutelyst::Headers &headers) override final
Container for HTTP headers.
Definition: headers.h:24
QByteArray connection() const noexcept
Definition: headers.cpp:295
QByteArray header(QByteArrayView key) const noexcept
Definition: headers.cpp:392
Implements a web server.
Definition: server.h:60
The Cutelyst namespace holds all public Cutelyst API.
QByteArray & append(QByteArrayView data)
QByteArray::iterator begin()
int compare(QByteArrayView bv, Qt::CaseSensitivity cs) const const
const char * constData() const const
QByteArray::iterator end()
bool isEmpty() const const
qsizetype length() const const
QByteArray number(double n, char format, int precision)
qsizetype size() const const
int exec(QEventLoop::ProcessEventsFlags flags)
void exit(int returnCode)
bool isRunning() const const
void quit()
QString toString() const const
virtual qint64 bytesAvailable() const const
QString errorString() const const
QByteArray read(qint64 maxSize)
virtual bool seek(qint64 pos)
qint64 write(const QByteArray &data)
void push_back(QList::parameter_type value)