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)
249void HPack::encodeHeaders(
int status,
const Headers &headers, QByteArray &buf,
ServerEngine *engine)
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));
296 const QByteArray date = engine->lastDate().
mid(8);
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()) {
456 const DynamicTableEntry entry = m_dynamicTable.takeLast();
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, 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) &&
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