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)
148void HPack::encodeHeaders(
int status,
const Headers &headers, QByteArray &buf,
ServerEngine *engine)
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));
198 const QByteArray date = engine->lastDate().
mid(8);
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
228inline bool validPseudoHeader(
const QByteArray &k,
const QByteArray &v,
H2Stream *stream)
233 if (!stream->gotPath && !v.
isEmpty()) {
234 int leadingSlash = 0;
235 while (leadingSlash < v.
size() && v.
at(leadingSlash) == u
'/') {
241 QByteArray path = v.
mid(leadingSlash);
244 QByteArray path = v.
mid(leadingSlash, pos - leadingSlash);
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()) {
269inline bool validHeader(
const QByteArray &k,
const QByteArray &v)
274inline void consumeHeader(
const QByteArray &k,
const QByteArray &v,
H2Stream *stream)
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()) {
411 const DynamicTableEntry entry = m_dynamicTable.takeLast();
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) &&
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