8#include "protocolhttp2.h"
9#include "serverengine.h"
15#define INT_MASK(bits) ((1 << (bits)) - 1)
21 hpackDecodeString(
unsigned char *src,
unsigned char *src_end,
QByteArray &value,
int len)
24 const HPackPrivate::HuffDecode *entry =
nullptr;
25 value.reserve(len * 2);
31 entry = HPackPrivate::huff_decode_table[state] + (*src >> 4);
33 if (entry->flags & HPackPrivate::HUFF_FAIL) {
38 if (entry->flags & HPackPrivate::HUFF_SYM) {
39 value.append(
char(entry->sym));
42 entry = HPackPrivate::huff_decode_table[entry->state] + (*src & 0x0f);
44 if (entry->flags & HPackPrivate::HUFF_FAIL) {
49 if ((entry->flags & HPackPrivate::HUFF_SYM) != 0) {
50 value.append(
char(entry->sym));
53 }
while (++src < src_end);
58 if ((entry->flags & HPackPrivate::HUFF_ACCEPTED) == 0) {
72 decodeUInt16(
unsigned char *src,
const unsigned char *src_end, quint16 &dst, quint8 mask)
80 if (++src >= src_end) {
85 dst += (*src & 0x7f) << M;
87 }
while (*src & 0x80);
93void encodeUInt16(
QByteArray &buf,
int I, quint8 mask)
103 buf.
append(
char((I & 0x7f) | 0x80));
112 encodeUInt16(buf, key.
length(), INT_MASK(7));
113 for (
auto keyIt : key) {
114 if (keyIt.isLetter()) {
115 buf.
append(keyIt.toLower().toLatin1());
116 }
else if (keyIt == u
'_') {
119 buf.
append(keyIt.toLatin1());
124unsigned char *parse_string(
QByteArray &dst,
unsigned char *buf,
const quint8 *itEnd)
128 bool huffmanDecode = *buf & 0x80;
130 buf = decodeUInt16(buf, itEnd, str_len, INT_MASK(7));
136 buf = hpackDecodeString(buf, buf + str_len, dst, str_len);
141 if (buf + str_len <= itEnd) {
142 dst =
QByteArray(
reinterpret_cast<const char *
>(buf), str_len);
151unsigned char *parse_string_key(
QByteArray &dst, quint8 *buf,
const quint8 *itEnd)
154 bool huffmanDecode = *buf & 0x80;
156 buf = decodeUInt16(buf, itEnd, str_len, INT_MASK(7));
162 buf = hpackDecodeString(buf, buf + str_len, dst, str_len);
167 if (buf + str_len <= itEnd) {
168 itEnd = buf + str_len;
170 while (buf < itEnd) {
175 dst += char(*(buf++));
189 if (!stream->gotPath && !v.
isEmpty()) {
190 int leadingSlash = 0;
191 while (leadingSlash < v.
size() && v.
at(leadingSlash) == u
'/') {
204 stream->gotPath =
true;
207 }
else if (k.
compare(
":method") == 0) {
212 }
else if (k.
compare(
":authority") == 0) {
215 }
else if (k.
compare(
":scheme") == 0) {
216 if (stream->scheme.
isEmpty()) {
232 if (k.
compare(
"content-length") == 0) {
239HPack::HPack(
int maxTableSize)
240 : m_currentMaxDynamicTableSize(maxTableSize)
241 , m_maxTableSize(maxTableSize)
253 }
else if (status == 204) {
255 }
else if (status == 206) {
257 }
else if (status == 304) {
259 }
else if (status == 400) {
261 }
else if (status == 404) {
263 }
else if (status == 500) {
269 encodeUInt16(buf, statusStr.
length(), INT_MASK(4));
273 bool hasDate =
false;
274 const auto headersData = headers.
data();
275 for (
const auto &[key, value] : headersData) {
280 auto staticIt = HPackPrivate::hpackStaticHeadersCode.constFind(key);
281 if (staticIt != HPackPrivate::hpackStaticHeadersCode.constEnd()) {
282 buf.
append(staticIt.value(), 2);
284 encodeUInt16(buf, value.length(), INT_MASK(7));
290 encodeUInt16(buf, value.length(), INT_MASK(7));
297 if (date.
length() != 29) {
304 buf.
append(
"\x0f\x12\x1d", 3);
311 ErrorProtocolError = 0x1,
312 ErrorInternalError = 0x2,
313 ErrorFlowControlError = 0x3,
314 ErrorSettingsTimeout = 0x4,
315 ErrorStreamClosed = 0x5,
316 ErrorFrameSizeError = 0x6,
317 ErrorRefusedStream = 0x7,
319 ErrorCompressionError = 0x9,
320 ErrorConnectError = 0xA,
321 ErrorEnhanceYourCalm = 0xB,
322 ErrorInadequateSecurity = 0xC,
323 ErrorHttp11Required = 0xD
326int HPack::decode(
unsigned char *it,
const unsigned char *itEnd,
H2Stream *stream)
328 bool pseudoHeadersAllowed =
true;
329 bool allowedToUpdate =
true;
333 it = decodeUInt16(it, itEnd, intValue, INT_MASK(7));
336 if (!it || intValue == 0) {
337 return ErrorCompressionError;
346 if (intValue < qint64(m_dynamicTable.size())) {
347 const auto h = m_dynamicTable[intValue];
351 return ErrorCompressionError;
354 const auto h = HPackPrivate::hpackStaticHeaders[intValue];
361 if (!pseudoHeadersAllowed || !validPseudoHeader(key, value, stream)) {
362 return ErrorProtocolError;
365 if (!validHeader(key, value)) {
366 return ErrorProtocolError;
368 pseudoHeadersAllowed =
false;
369 consumeHeader(key, value, stream);
373 bool addToDynamicTable =
false;
376 it = decodeUInt16(it, itEnd, intValue, INT_MASK(6));
378 return ErrorCompressionError;
380 addToDynamicTable =
true;
383 }
else if (*it & 0x20) {
384 it = decodeUInt16(it, itEnd, intValue, INT_MASK(5));
388 if (!it || intValue > m_maxTableSize || !allowedToUpdate) {
389 return ErrorCompressionError;
392 m_currentMaxDynamicTableSize = intValue;
393 while (m_dynamicTableSize > m_currentMaxDynamicTableSize &&
394 !m_dynamicTable.empty()) {
395 auto header = m_dynamicTable.takeLast();
396 m_dynamicTableSize -= header.key.length() + header.value.length() + 32;
403 it = decodeUInt16(it, itEnd, intValue, INT_MASK(4));
405 return ErrorCompressionError;
411 if (addToDynamicTable) {
414 if (intValue - 62 < qint64(m_dynamicTable.size())) {
415 const auto h = m_dynamicTable[intValue - 62];
418 return ErrorCompressionError;
421 return ErrorCompressionError;
423 }
else if (intValue != 0) {
424 const auto h = HPackPrivate::hpackStaticHeaders[intValue];
427 it = parse_string_key(key, it, itEnd);
429 return ErrorProtocolError;
434 it = parse_string(value, it, itEnd);
436 return ErrorCompressionError;
440 if (!pseudoHeadersAllowed || !validPseudoHeader(key, value, stream)) {
441 return ErrorProtocolError;
444 if (!validHeader(key, value)) {
445 return ErrorProtocolError;
447 pseudoHeadersAllowed =
false;
448 consumeHeader(key, value, stream);
452 if (addToDynamicTable) {
453 const int size = key.
length() + value.length() + 32;
454 while (size + m_dynamicTableSize > m_currentMaxDynamicTableSize &&
455 !m_dynamicTable.empty()) {
457 m_dynamicTableSize -= entry.key.
length() + entry.value.
length() + 32;
460 if (size + m_dynamicTableSize <= m_currentMaxDynamicTableSize) {
461 m_dynamicTable.prepend({key, value});
462 m_dynamicTableSize += size;
469 allowedToUpdate =
false;
473 return ErrorProtocolError;
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)
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
QString fromLatin1(QByteArrayView str)
qsizetype length() const const