8#include "protocolhttp2.h"
9#include "serverengine.h"
15#define INT_MASK(bits) (1 << bits) - 1
20 hpackDecodeString(
unsigned char *src,
unsigned char *src_end,
QByteArray &value,
int len);
26unsigned char *decodeUInt16(
unsigned char *src,
unsigned char *src_end, quint16 &dst, quint8 mask)
34 if (++src >= src_end) {
39 dst += (*src & 0x7f) << M;
41 }
while (*src & 0x80);
47void encodeUInt16(
QByteArray &buf,
int I, quint8 mask)
57 buf.
append(
char((I & 0x7f) | 0x80));
66 encodeUInt16(buf, key.
length(), INT_MASK(7));
67 for (
auto keyIt : key) {
68 if (keyIt.isLetter()) {
69 buf.
append(keyIt.toLower().toLatin1());
70 }
else if (keyIt == u
'_') {
73 buf.
append(keyIt.toLatin1());
78unsigned char *parse_string(
QByteArray &dst,
unsigned char *buf, quint8 *itEnd)
82 bool huffmanDecode = *buf & 0x80;
84 buf = decodeUInt16(buf, itEnd, str_len, INT_MASK(7));
90 buf = hpackDecodeString(buf, buf + str_len, dst, str_len);
95 if (buf + str_len <= itEnd) {
96 dst =
QByteArray(
reinterpret_cast<const char *
>(buf), str_len);
105unsigned char *parse_string_key(
QByteArray &dst, quint8 *buf, quint8 *itEnd)
108 bool huffmanDecode = *buf & 0x80;
110 buf = decodeUInt16(buf, itEnd, str_len, INT_MASK(7));
116 buf = hpackDecodeString(buf, buf + str_len, dst, str_len);
121 if (buf + str_len <= itEnd) {
122 itEnd = buf + str_len;
124 while (buf < itEnd) {
129 dst += char(*(buf++));
138HPack::HPack(
int maxTableSize)
139 : m_currentMaxDynamicTableSize(maxTableSize)
140 , m_maxTableSize(maxTableSize)
152 }
else if (status == 204) {
154 }
else if (status == 206) {
156 }
else if (status == 304) {
158 }
else if (status == 400) {
160 }
else if (status == 404) {
162 }
else if (status == 500) {
168 encodeUInt16(buf, statusStr.
length(), INT_MASK(4));
172 bool hasDate =
false;
173 auto headersData = headers.
data();
174 auto it = headersData.begin();
175 while (it != headersData.end()) {
180 auto staticIt = HPackPrivate::hpackStaticHeadersCode.constFind(it->key);
181 if (staticIt != HPackPrivate::hpackStaticHeadersCode.constEnd()) {
182 buf.
append(staticIt.value(), 2);
184 encodeUInt16(buf, it->value.length(), INT_MASK(7));
190 encodeUInt16(buf, it->value.length(), INT_MASK(7));
199 if (date.
length() != 29) {
206 buf.
append(
"\x0f\x12\x1d", 3);
213 ErrorProtocolError = 0x1,
214 ErrorInternalError = 0x2,
215 ErrorFlowControlError = 0x3,
216 ErrorSettingsTimeout = 0x4,
217 ErrorStreamClosed = 0x5,
218 ErrorFrameSizeError = 0x6,
219 ErrorRefusedStream = 0x7,
221 ErrorCompressionError = 0x9,
222 ErrorConnectError = 0xA,
223 ErrorEnhanceYourCalm = 0xB,
224 ErrorInadequateSecurity = 0xC,
225 ErrorHttp11Required = 0xD
233 if (!stream->gotPath && !v.
isEmpty()) {
234 int leadingSlash = 0;
235 while (leadingSlash < v.
size() && v.
at(leadingSlash) == u
'/') {
248 stream->gotPath =
true;
251 }
else if (k.
compare(
":method") == 0) {
256 }
else if (k.
compare(
":authority") == 0) {
259 }
else if (k.
compare(
":scheme") == 0) {
260 if (stream->scheme.
isEmpty()) {
276 if (k.
compare(
"content-length") == 0) {
281int HPack::decode(
unsigned char *it,
unsigned char *itEnd,
H2Stream *stream)
283 bool pseudoHeadersAllowed =
true;
284 bool allowedToUpdate =
true;
288 it = decodeUInt16(it, itEnd, intValue, INT_MASK(7));
291 if (!it || intValue == 0) {
292 return ErrorCompressionError;
301 if (intValue < qint64(m_dynamicTable.size())) {
302 const auto h = m_dynamicTable[intValue];
306 return ErrorCompressionError;
309 const auto h = HPackPrivate::hpackStaticHeaders[intValue];
316 if (!pseudoHeadersAllowed || !validPseudoHeader(key, value, stream)) {
317 return ErrorProtocolError;
320 if (!validHeader(key, value)) {
321 return ErrorProtocolError;
323 pseudoHeadersAllowed =
false;
324 consumeHeader(key, value, stream);
328 bool addToDynamicTable =
false;
331 it = decodeUInt16(it, itEnd, intValue, INT_MASK(6));
333 return ErrorCompressionError;
335 addToDynamicTable =
true;
338 }
else if (*it & 0x20) {
339 it = decodeUInt16(it, itEnd, intValue, INT_MASK(5));
343 if (!it || intValue > m_maxTableSize || !allowedToUpdate) {
344 return ErrorCompressionError;
347 m_currentMaxDynamicTableSize = intValue;
348 while (m_dynamicTableSize > m_currentMaxDynamicTableSize &&
349 !m_dynamicTable.empty()) {
350 auto header = m_dynamicTable.takeLast();
351 m_dynamicTableSize -= header.key.length() + header.value.length() + 32;
358 it = decodeUInt16(it, itEnd, intValue, INT_MASK(4));
360 return ErrorCompressionError;
366 if (addToDynamicTable) {
369 if (intValue - 62 < qint64(m_dynamicTable.size())) {
370 const auto h = m_dynamicTable[intValue - 62];
373 return ErrorCompressionError;
376 return ErrorCompressionError;
378 }
else if (intValue != 0) {
379 const auto h = HPackPrivate::hpackStaticHeaders[intValue];
382 it = parse_string_key(key, it, itEnd);
384 return ErrorProtocolError;
389 it = parse_string(value, it, itEnd);
391 return ErrorCompressionError;
395 if (!pseudoHeadersAllowed || !validPseudoHeader(key, value, stream)) {
396 return ErrorProtocolError;
399 if (!validHeader(key, value)) {
400 return ErrorProtocolError;
402 pseudoHeadersAllowed =
false;
403 consumeHeader(key, value, stream);
407 if (addToDynamicTable) {
408 const int size = key.
length() + value.length() + 32;
409 while (size + m_dynamicTableSize > m_currentMaxDynamicTableSize &&
410 !m_dynamicTable.empty()) {
412 m_dynamicTableSize -= entry.key.
length() + entry.value.
length() + 32;
415 if (size + m_dynamicTableSize <= m_currentMaxDynamicTableSize) {
416 m_dynamicTable.prepend({key, value});
417 m_dynamicTableSize += size;
424 allowedToUpdate =
false;
428 return ErrorProtocolError;
435 hpackDecodeString(
unsigned char *src,
unsigned char *src_end,
QByteArray &value,
int len)
438 const HPackPrivate::HuffDecode *entry =
nullptr;
439 value.reserve(len * 2);
443 state = entry->state;
445 entry = HPackPrivate::huff_decode_table[state] + (*src >> 4);
447 if (entry->flags & HPackPrivate::HUFF_FAIL) {
452 if (entry->flags & HPackPrivate::HUFF_SYM) {
453 value.append(
char(entry->sym));
456 entry = HPackPrivate::huff_decode_table[entry->state] + (*src & 0x0f);
458 if (entry->flags & HPackPrivate::HUFF_FAIL) {
463 if ((entry->flags & HPackPrivate::HUFF_SYM) != 0) {
464 value.append(
char(entry->sym));
467 }
while (++src < src_end);
472 if ((entry->flags & HPackPrivate::HUFF_ACCEPTED) == 0) {
void setPath(char *rawPath, const int len)
The Cutelyst namespace holds all public Cutelyst API.
QByteArray & append(QByteArrayView data)
char at(qsizetype i) const const
int compare(QByteArrayView bv, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
bool isEmpty() const const
qsizetype length() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
QByteArray number(double n, char format, int precision)
qsizetype size() const const
bool startsWith(QByteArrayView bv) const const
qlonglong toLongLong(bool *ok, int base) const const
bool isUpper(char32_t ucs4)
QString fromLatin1(QByteArrayView str)
qsizetype length() const const