1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "bitstreams_p.h"
41#include "hpack_p.h"
42
43#include <QtCore/qbytearray.h>
44#include <QtCore/qdebug.h>
45
46#include <limits>
47
48QT_BEGIN_NAMESPACE
49
50namespace HPack
51{
52
53HeaderSize header_size(const HttpHeader &header)
54{
55 HeaderSize size(true, 0);
56 for (const HeaderField &field : header) {
57 HeaderSize delta = entry_size(field);
58 if (!delta.first)
59 return HeaderSize();
60 if (std::numeric_limits<quint32>::max() - size.second < delta.second)
61 return HeaderSize();
62 size.second += delta.second;
63 }
64
65 return size;
66}
67
68struct BitPattern
69{
70 uchar value;
71 uchar bitLength;
72};
73
74bool operator == (const BitPattern &lhs, const BitPattern &rhs)
75{
76 return lhs.bitLength == rhs.bitLength && lhs.value == rhs.value;
77}
78
79namespace
80{
81
82using StreamError = BitIStream::Error;
83
84// There are several bit patterns to distinguish header fields:
85// 1 - indexed
86// 01 - literal with incremented indexing
87// 0000 - literal without indexing
88// 0001 - literal, never indexing
89// 001 - dynamic table size update.
90
91// It's always 1 or 0 actually, but the number of bits to extract
92// from the input stream - differs.
93const BitPattern Indexed = {1, 1};
94const BitPattern LiteralIncrementalIndexing = {1, 2};
95const BitPattern LiteralNoIndexing = {0, 4};
96const BitPattern LiteralNeverIndexing = {1, 4};
97const BitPattern SizeUpdate = {1, 3};
98
99bool is_literal_field(const BitPattern &pattern)
100{
101 return pattern == LiteralIncrementalIndexing
102 || pattern == LiteralNoIndexing
103 || pattern == LiteralNeverIndexing;
104}
105
106void write_bit_pattern(const BitPattern &pattern, BitOStream &outputStream)
107{
108 outputStream.writeBits(pattern.value, pattern.bitLength);
109}
110
111bool read_bit_pattern(const BitPattern &pattern, BitIStream &inputStream)
112{
113 uchar chunk = 0;
114
115 const quint32 bitsRead = inputStream.peekBits(inputStream.streamOffset(),
116 pattern.bitLength, &chunk);
117 if (bitsRead != pattern.bitLength)
118 return false;
119
120 // Since peekBits packs in the most significant bits, shift it!
121 chunk >>= (8 - bitsRead);
122 if (chunk != pattern.value)
123 return false;
124
125 inputStream.skipBits(pattern.bitLength);
126
127 return true;
128}
129
130bool is_request_pseudo_header(const QByteArray &name)
131{
132 return name == ":method" || name == ":scheme" ||
133 name == ":authority" || name == ":path";
134}
135
136} // unnamed namespace
137
138Encoder::Encoder(quint32 size, bool compress)
139 : lookupTable(size, true /*encoder needs search index*/),
140 compressStrings(compress)
141{
142}
143
144quint32 Encoder::dynamicTableSize() const
145{
146 return lookupTable.dynamicDataSize();
147}
148
149bool Encoder::encodeRequest(BitOStream &outputStream, const HttpHeader &header)
150{
151 if (!header.size()) {
152 qDebug("empty header");
153 return false;
154 }
155
156 if (!encodeRequestPseudoHeaders(outputStream, header))
157 return false;
158
159 for (const auto &field : header) {
160 if (is_request_pseudo_header(field.name))
161 continue;
162
163 if (!encodeHeaderField(outputStream, field))
164 return false;
165 }
166
167 return true;
168}
169
170bool Encoder::encodeResponse(BitOStream &outputStream, const HttpHeader &header)
171{
172 if (!header.size()) {
173 qDebug("empty header");
174 return false;
175 }
176
177 if (!encodeResponsePseudoHeaders(outputStream, header))
178 return false;
179
180 for (const auto &field : header) {
181 if (field.name == ":status")
182 continue;
183
184 if (!encodeHeaderField(outputStream, field))
185 return false;
186 }
187
188 return true;
189}
190
191bool Encoder::encodeSizeUpdate(BitOStream &outputStream, quint32 newSize)
192{
193 if (!lookupTable.updateDynamicTableSize(newSize)) {
194 qDebug("failed to update own table size");
195 return false;
196 }
197
198 write_bit_pattern(SizeUpdate, outputStream);
199 outputStream.write(newSize);
200
201 return true;
202}
203
204void Encoder::setMaxDynamicTableSize(quint32 size)
205{
206 // Up to a caller (HTTP2 protocol handler)
207 // to validate this size first.
208 lookupTable.setMaxDynamicTableSize(size);
209}
210
211void Encoder::setCompressStrings(bool compress)
212{
213 compressStrings = compress;
214}
215
216bool Encoder::encodeRequestPseudoHeaders(BitOStream &outputStream,
217 const HttpHeader &header)
218{
219 // The following pseudo-header fields are defined for HTTP/2 requests:
220 // - The :method pseudo-header field includes the HTTP method
221 // - The :scheme pseudo-header field includes the scheme portion of the target URI
222 // - The :authority pseudo-header field includes the authority portion of the target URI
223 // - The :path pseudo-header field includes the path and query parts of the target URI
224
225 // All HTTP/2 requests MUST include exactly one valid value for the :method,
226 // :scheme, and :path pseudo-header fields, unless it is a CONNECT request
227 // (Section 8.3). An HTTP request that omits mandatory pseudo-header fields
228 // is malformed (Section 8.1.2.6).
229
230 using size_type = decltype(header.size());
231
232 bool methodFound = false;
233 const char *headerName[] = {":authority", ":scheme", ":path"};
234 const size_type nHeaders = sizeof headerName / sizeof headerName[0];
235 bool headerFound[nHeaders] = {};
236
237 for (const auto &field : header) {
238 if (field.name == ":status") {
239 qCritical("invalid pseudo-header (:status) in a request");
240 return false;
241 }
242
243 if (field.name == ":method") {
244 if (methodFound) {
245 qCritical("only one :method pseudo-header is allowed");
246 return false;
247 }
248
249 if (!encodeMethod(outputStream, field))
250 return false;
251 methodFound = true;
252 } else if (field.name == "cookie") {
253 // "crumbs" ...
254 } else {
255 for (size_type j = 0; j < nHeaders; ++j) {
256 if (field.name == headerName[j]) {
257 if (headerFound[j]) {
258 qCritical() << "only one" << headerName[j] << "pseudo-header is allowed";
259 return false;
260 }
261 if (!encodeHeaderField(outputStream, field))
262 return false;
263 headerFound[j] = true;
264 break;
265 }
266 }
267 }
268 }
269
270 if (!methodFound) {
271 qCritical("mandatory :method pseudo-header not found");
272 return false;
273 }
274
275 // 1: don't demand headerFound[0], as :authority isn't mandatory.
276 for (size_type i = 1; i < nHeaders; ++i) {
277 if (!headerFound[i]) {
278 qCritical() << "mandatory" << headerName[i]
279 << "pseudo-header not found";
280 return false;
281 }
282 }
283
284 return true;
285}
286
287bool Encoder::encodeHeaderField(BitOStream &outputStream, const HeaderField &field)
288{
289 // TODO: at the moment we never use LiteralNo/Never Indexing ...
290
291 // Here we try:
292 // 1. indexed
293 // 2. literal indexed with indexed name/literal value
294 // 3. literal indexed with literal name/literal value
295 if (const auto index = lookupTable.indexOf(field.name, field.value))
296 return encodeIndexedField(outputStream, index);
297
298 if (const auto index = lookupTable.indexOf(field.name)) {
299 return encodeLiteralField(outputStream, LiteralIncrementalIndexing,
300 index, field.value, compressStrings);
301 }
302
303 return encodeLiteralField(outputStream, LiteralIncrementalIndexing,
304 field.name, field.value, compressStrings);
305}
306
307bool Encoder::encodeMethod(BitOStream &outputStream, const HeaderField &field)
308{
309 Q_ASSERT(field.name == ":method");
310 quint32 index = lookupTable.indexOf(field.name, field.value);
311 if (index)
312 return encodeIndexedField(outputStream, index);
313
314 index = lookupTable.indexOf(field.name);
315 Q_ASSERT(index); // ":method" is always in the static table ...
316 return encodeLiteralField(outputStream, LiteralIncrementalIndexing,
317 index, field.value, compressStrings);
318}
319
320bool Encoder::encodeResponsePseudoHeaders(BitOStream &outputStream, const HttpHeader &header)
321{
322 bool statusFound = false;
323 for (const auto &field : header) {
324 if (is_request_pseudo_header(field.name)) {
325 qCritical() << "invalid pseudo-header" << field.name << "in http response";
326 return false;
327 }
328
329 if (field.name == ":status") {
330 if (statusFound) {
331 qDebug("only one :status pseudo-header is allowed");
332 return false;
333 }
334 if (!encodeHeaderField(outputStream, field))
335 return false;
336 statusFound = true;
337 } else if (field.name == "cookie") {
338 // "crumbs"..
339 }
340 }
341
342 if (!statusFound)
343 qCritical("mandatory :status pseudo-header not found");
344
345 return statusFound;
346}
347
348bool Encoder::encodeIndexedField(BitOStream &outputStream, quint32 index) const
349{
350 Q_ASSERT(lookupTable.indexIsValid(index));
351
352 write_bit_pattern(Indexed, outputStream);
353 outputStream.write(index);
354
355 return true;
356}
357
358bool Encoder::encodeLiteralField(BitOStream &outputStream, const BitPattern &fieldType,
359 const QByteArray &name, const QByteArray &value,
360 bool withCompression)
361{
362 Q_ASSERT(is_literal_field(fieldType));
363 // According to HPACK, the bit pattern is
364 // 01 | 000000 (integer 0 that fits into 6-bit prefix),
365 // since integers always end on byte boundary,
366 // this also implies that we always start at bit offset == 0.
367 if (outputStream.bitLength() % 8) {
368 qCritical("invalid bit offset");
369 return false;
370 }
371
372 if (fieldType == LiteralIncrementalIndexing) {
373 if (!lookupTable.prependField(name, value))
374 qDebug("failed to prepend a new field");
375 }
376
377 write_bit_pattern(fieldType, outputStream);
378
379 outputStream.write(0);
380 outputStream.write(name, withCompression);
381 outputStream.write(value, withCompression);
382
383 return true;
384}
385
386bool Encoder::encodeLiteralField(BitOStream &outputStream, const BitPattern &fieldType,
387 quint32 nameIndex, const QByteArray &value,
388 bool withCompression)
389{
390 Q_ASSERT(is_literal_field(fieldType));
391
392 QByteArray name;
393 const bool found = lookupTable.fieldName(nameIndex, &name);
394 Q_UNUSED(found);
395 Q_ASSERT(found);
396
397 if (fieldType == LiteralIncrementalIndexing) {
398 if (!lookupTable.prependField(name, value))
399 qDebug("failed to prepend a new field");
400 }
401
402 write_bit_pattern(fieldType, outputStream);
403 outputStream.write(nameIndex);
404 outputStream.write(value, withCompression);
405
406 return true;
407}
408
409Decoder::Decoder(quint32 size)
410 : lookupTable{size, false /* we do not need search index ... */}
411{
412}
413
414bool Decoder::decodeHeaderFields(BitIStream &inputStream)
415{
416 header.clear();
417 while (true) {
418 if (read_bit_pattern(Indexed, inputStream)) {
419 if (!decodeIndexedField(inputStream))
420 return false;
421 } else if (read_bit_pattern(LiteralIncrementalIndexing, inputStream)) {
422 if (!decodeLiteralField(LiteralIncrementalIndexing, inputStream))
423 return false;
424 } else if (read_bit_pattern(LiteralNoIndexing, inputStream)) {
425 if (!decodeLiteralField(LiteralNoIndexing, inputStream))
426 return false;
427 } else if (read_bit_pattern(LiteralNeverIndexing, inputStream)) {
428 if (!decodeLiteralField(LiteralNeverIndexing, inputStream))
429 return false;
430 } else if (read_bit_pattern(SizeUpdate, inputStream)) {
431 if (!decodeSizeUpdate(inputStream))
432 return false;
433 } else {
434 return inputStream.bitLength() == inputStream.streamOffset();
435 }
436 }
437
438 return false;
439}
440
441quint32 Decoder::dynamicTableSize() const
442{
443 return lookupTable.dynamicDataSize();
444}
445
446void Decoder::setMaxDynamicTableSize(quint32 size)
447{
448 // Up to a caller (HTTP2 protocol handler)
449 // to validate this size first.
450 lookupTable.setMaxDynamicTableSize(size);
451}
452
453bool Decoder::decodeIndexedField(BitIStream &inputStream)
454{
455 quint32 index = 0;
456 if (inputStream.read(&index)) {
457 if (!index) {
458 // "The index value of 0 is not used.
459 // It MUST be treated as a decoding
460 // error if found in an indexed header
461 // field representation."
462 return false;
463 }
464
465 QByteArray name, value;
466 if (lookupTable.field(index, &name, &value))
467 return processDecodedField(Indexed, name, value);
468 } else {
469 handleStreamError(inputStream);
470 }
471
472 return false;
473}
474
475bool Decoder::decodeSizeUpdate(BitIStream &inputStream)
476{
477 // For now, just read and skip bits.
478 quint32 maxSize = 0;
479 if (inputStream.read(&maxSize)) {
480 if (!lookupTable.updateDynamicTableSize(maxSize))
481 return false;
482
483 return true;
484 }
485
486 handleStreamError(inputStream);
487 return false;
488}
489
490bool Decoder::decodeLiteralField(const BitPattern &fieldType, BitIStream &inputStream)
491{
492 // https://http2.github.io/http2-spec/compression.html
493 // 6.2.1, 6.2.2, 6.2.3
494 // Format for all 'literal' is similar,
495 // the difference - is how we update/not our lookup table.
496 quint32 index = 0;
497 if (inputStream.read(&index)) {
498 QByteArray name;
499 if (!index) {
500 // Read a string.
501 if (!inputStream.read(&name)) {
502 handleStreamError(inputStream);
503 return false;
504 }
505 } else {
506 if (!lookupTable.fieldName(index, &name))
507 return false;
508 }
509
510 QByteArray value;
511 if (inputStream.read(&value))
512 return processDecodedField(fieldType, name, value);
513 }
514
515 handleStreamError(inputStream);
516
517 return false;
518}
519
520bool Decoder::processDecodedField(const BitPattern &fieldType,
521 const QByteArray &name,
522 const QByteArray &value)
523{
524 if (fieldType == LiteralIncrementalIndexing) {
525 if (!lookupTable.prependField(name, value))
526 return false;
527 }
528
529 header.push_back(HeaderField(name, value));
530 return true;
531}
532
533void Decoder::handleStreamError(BitIStream &inputStream)
534{
535 const auto errorCode(inputStream.error());
536 if (errorCode == StreamError::NoError)
537 return;
538
539 // For now error handling not needed here,
540 // HTTP2 layer will end with session error/COMPRESSION_ERROR.
541}
542
543}
544
545QT_END_NAMESPACE
546