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 
48 QT_BEGIN_NAMESPACE
49 
50 namespace HPack
51 {
52 
header_size(const HttpHeader & header)53 HeaderSize 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 
68 struct BitPattern
69 {
70     uchar value;
71     uchar bitLength;
72 };
73 
operator ==(const BitPattern & lhs,const BitPattern & rhs)74 bool operator == (const BitPattern &lhs, const BitPattern &rhs)
75 {
76     return lhs.bitLength == rhs.bitLength && lhs.value == rhs.value;
77 }
78 
79 namespace
80 {
81 
82 using 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.
93 const BitPattern Indexed = {1, 1};
94 const BitPattern LiteralIncrementalIndexing = {1, 2};
95 const BitPattern LiteralNoIndexing = {0, 4};
96 const BitPattern LiteralNeverIndexing = {1, 4};
97 const BitPattern SizeUpdate = {1, 3};
98 
is_literal_field(const BitPattern & pattern)99 bool is_literal_field(const BitPattern &pattern)
100 {
101     return pattern == LiteralIncrementalIndexing
102            || pattern == LiteralNoIndexing
103            || pattern == LiteralNeverIndexing;
104 }
105 
write_bit_pattern(const BitPattern & pattern,BitOStream & outputStream)106 void write_bit_pattern(const BitPattern &pattern, BitOStream &outputStream)
107 {
108     outputStream.writeBits(pattern.value, pattern.bitLength);
109 }
110 
read_bit_pattern(const BitPattern & pattern,BitIStream & inputStream)111 bool 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 
is_request_pseudo_header(const QByteArray & name)130 bool 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 
Encoder(quint32 size,bool compress)138 Encoder::Encoder(quint32 size, bool compress)
139     : lookupTable(size, true /*encoder needs search index*/),
140       compressStrings(compress)
141 {
142 }
143 
dynamicTableSize() const144 quint32 Encoder::dynamicTableSize() const
145 {
146     return lookupTable.dynamicDataSize();
147 }
148 
encodeRequest(BitOStream & outputStream,const HttpHeader & header)149 bool 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 
encodeResponse(BitOStream & outputStream,const HttpHeader & header)170 bool 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 
encodeSizeUpdate(BitOStream & outputStream,quint32 newSize)191 bool 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 
setMaxDynamicTableSize(quint32 size)204 void 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 
setCompressStrings(bool compress)211 void Encoder::setCompressStrings(bool compress)
212 {
213     compressStrings = compress;
214 }
215 
encodeRequestPseudoHeaders(BitOStream & outputStream,const HttpHeader & header)216 bool 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 
encodeHeaderField(BitOStream & outputStream,const HeaderField & field)287 bool 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 
encodeMethod(BitOStream & outputStream,const HeaderField & field)307 bool 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 
encodeResponsePseudoHeaders(BitOStream & outputStream,const HttpHeader & header)320 bool 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 
encodeIndexedField(BitOStream & outputStream,quint32 index) const348 bool 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 
encodeLiteralField(BitOStream & outputStream,const BitPattern & fieldType,const QByteArray & name,const QByteArray & value,bool withCompression)358 bool 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 
encodeLiteralField(BitOStream & outputStream,const BitPattern & fieldType,quint32 nameIndex,const QByteArray & value,bool withCompression)386 bool 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) Q_ASSERT(found);
395 
396     if (fieldType == LiteralIncrementalIndexing) {
397         if (!lookupTable.prependField(name, value))
398             qDebug("failed to prepend a new field");
399     }
400 
401     write_bit_pattern(fieldType, outputStream);
402     outputStream.write(nameIndex);
403     outputStream.write(value, withCompression);
404 
405     return true;
406 }
407 
Decoder(quint32 size)408 Decoder::Decoder(quint32 size)
409     : lookupTable{size, false /* we do not need search index ... */}
410 {
411 }
412 
decodeHeaderFields(BitIStream & inputStream)413 bool Decoder::decodeHeaderFields(BitIStream &inputStream)
414 {
415     header.clear();
416     while (true) {
417         if (read_bit_pattern(Indexed, inputStream)) {
418             if (!decodeIndexedField(inputStream))
419                 return false;
420         } else if (read_bit_pattern(LiteralIncrementalIndexing, inputStream)) {
421             if (!decodeLiteralField(LiteralIncrementalIndexing, inputStream))
422                 return false;
423         } else if (read_bit_pattern(LiteralNoIndexing, inputStream)) {
424             if (!decodeLiteralField(LiteralNoIndexing, inputStream))
425                 return false;
426         } else if (read_bit_pattern(LiteralNeverIndexing, inputStream)) {
427             if (!decodeLiteralField(LiteralNeverIndexing, inputStream))
428                 return false;
429         } else if (read_bit_pattern(SizeUpdate, inputStream)) {
430             if (!decodeSizeUpdate(inputStream))
431                 return false;
432         } else {
433             return inputStream.bitLength() == inputStream.streamOffset();
434         }
435     }
436 
437     return false;
438 }
439 
dynamicTableSize() const440 quint32 Decoder::dynamicTableSize() const
441 {
442     return lookupTable.dynamicDataSize();
443 }
444 
setMaxDynamicTableSize(quint32 size)445 void Decoder::setMaxDynamicTableSize(quint32 size)
446 {
447     // Up to a caller (HTTP2 protocol handler)
448     // to validate this size first.
449     lookupTable.setMaxDynamicTableSize(size);
450 }
451 
decodeIndexedField(BitIStream & inputStream)452 bool Decoder::decodeIndexedField(BitIStream &inputStream)
453 {
454     quint32 index = 0;
455     if (inputStream.read(&index)) {
456         if (!index) {
457             // "The index value of 0 is not used.
458             //  It MUST be treated as a decoding
459             //  error if found in an indexed header
460             //  field representation."
461             return false;
462         }
463 
464         QByteArray name, value;
465         if (lookupTable.field(index, &name, &value))
466             return processDecodedField(Indexed, name, value);
467     } else {
468         handleStreamError(inputStream);
469     }
470 
471     return false;
472 }
473 
decodeSizeUpdate(BitIStream & inputStream)474 bool Decoder::decodeSizeUpdate(BitIStream &inputStream)
475 {
476     // For now, just read and skip bits.
477     quint32 maxSize = 0;
478     if (inputStream.read(&maxSize)) {
479         if (!lookupTable.updateDynamicTableSize(maxSize))
480             return false;
481 
482         return true;
483     }
484 
485     handleStreamError(inputStream);
486     return false;
487 }
488 
decodeLiteralField(const BitPattern & fieldType,BitIStream & inputStream)489 bool Decoder::decodeLiteralField(const BitPattern &fieldType, BitIStream &inputStream)
490 {
491     // https://http2.github.io/http2-spec/compression.html
492     // 6.2.1, 6.2.2, 6.2.3
493     // Format for all 'literal' is similar,
494     // the difference - is how we update/not our lookup table.
495     quint32 index = 0;
496     if (inputStream.read(&index)) {
497         QByteArray name;
498         if (!index) {
499             // Read a string.
500             if (!inputStream.read(&name)) {
501                 handleStreamError(inputStream);
502                 return false;
503             }
504         } else {
505             if (!lookupTable.fieldName(index, &name))
506                 return false;
507         }
508 
509         QByteArray value;
510         if (inputStream.read(&value))
511             return processDecodedField(fieldType, name, value);
512     }
513 
514     handleStreamError(inputStream);
515 
516     return false;
517 }
518 
processDecodedField(const BitPattern & fieldType,const QByteArray & name,const QByteArray & value)519 bool Decoder::processDecodedField(const BitPattern &fieldType,
520                                  const QByteArray &name,
521                                  const QByteArray &value)
522 {
523     if (fieldType == LiteralIncrementalIndexing) {
524         if (!lookupTable.prependField(name, value))
525             return false;
526     }
527 
528     header.push_back(HeaderField(name, value));
529     return true;
530 }
531 
handleStreamError(BitIStream & inputStream)532 void Decoder::handleStreamError(BitIStream &inputStream)
533 {
534     const auto errorCode(inputStream.error());
535     if (errorCode == StreamError::NoError)
536         return;
537 
538     // For now error handling not needed here,
539     // HTTP2 layer will end with session error/COMPRESSION_ERROR.
540 }
541 
542 }
543 
544 QT_END_NAMESPACE
545