diff --git a/src/network/access/http2/hpack.cpp b/src/network/access/http2/hpack.cpp index d5f541ed28c..7dae0ee65cd 100644 --- a/src/network/access/http2/hpack.cpp +++ b/src/network/access/http2/hpack.cpp @@ -110,6 +110,16 @@ quint32 Encoder::dynamicTableSize() const return lookupTable.dynamicDataSize(); } +quint32 Encoder::dynamicTableCapacity() const +{ + return lookupTable.dynamicDataCapacity(); +} + +quint32 Encoder::maxDynamicTableCapacity() const +{ + return lookupTable.maxDynamicDataCapacity(); +} + bool Encoder::encodeRequest(BitOStream &outputStream, const HttpHeader &header) { if (!header.size()) { @@ -407,6 +417,16 @@ quint32 Decoder::dynamicTableSize() const return lookupTable.dynamicDataSize(); } +quint32 Decoder::dynamicTableCapacity() const +{ + return lookupTable.dynamicDataCapacity(); +} + +quint32 Decoder::maxDynamicTableCapacity() const +{ + return lookupTable.maxDynamicDataCapacity(); +} + void Decoder::setMaxDynamicTableSize(quint32 size) { // Up to a caller (HTTP2 protocol handler) @@ -490,6 +510,11 @@ bool Decoder::processDecodedField(BitPattern fieldType, return false; } + if (lookupTable.maxDynamicDataCapacity() < lookupTable.dynamicDataCapacity()) { + qDebug("about to add a new field, but expected a Dynamic Table Size Update"); + return false; // We expected a dynamic table size update. + } + header.push_back(HeaderField(name, value)); return true; } diff --git a/src/network/access/http2/hpack_p.h b/src/network/access/http2/hpack_p.h index 9cac521977e..b158f5ed376 100644 --- a/src/network/access/http2/hpack_p.h +++ b/src/network/access/http2/hpack_p.h @@ -39,6 +39,8 @@ public: Encoder(quint32 maxTableSize, bool compressStrings); quint32 dynamicTableSize() const; + quint32 dynamicTableCapacity() const; + quint32 maxDynamicTableCapacity() const; bool encodeRequest(class BitOStream &outputStream, const HttpHeader &header); @@ -94,6 +96,8 @@ public: } quint32 dynamicTableSize() const; + quint32 dynamicTableCapacity() const; + quint32 maxDynamicTableCapacity() const; void setMaxDynamicTableSize(quint32 size); diff --git a/src/network/access/http2/hpacktable.cpp b/src/network/access/http2/hpacktable.cpp index 2c728b37e3b..43466e689fe 100644 --- a/src/network/access/http2/hpacktable.cpp +++ b/src/network/access/http2/hpacktable.cpp @@ -207,6 +207,16 @@ quint32 FieldLookupTable::dynamicDataSize() const return dataSize; } +quint32 FieldLookupTable::dynamicDataCapacity() const +{ + return tableCapacity; +} + +quint32 FieldLookupTable::maxDynamicDataCapacity() const +{ + return maxTableSize; +} + void FieldLookupTable::clearDynamicTable() { searchIndex.clear(); @@ -385,6 +395,7 @@ bool FieldLookupTable::updateDynamicTableSize(quint32 size) { if (!size) { clearDynamicTable(); + tableCapacity = 0; return true; } @@ -403,10 +414,10 @@ void FieldLookupTable::setMaxDynamicTableSize(quint32 size) // This is for an external user, for example, HTTP2 protocol // layer that can receive SETTINGS frame from its peer. // No validity checks here, up to this external user. - // We update max size and capacity (this can also result in - // items evicted or even dynamic table completely cleared). + // We update max size only, the capacity will be updated + // later through the Dynamic Table Size Update mechanism + // in HPack. maxTableSize = size; - updateDynamicTableSize(size); } // This data is from the HPACK's specs and it's quite conveniently sorted, diff --git a/src/network/access/http2/hpacktable_p.h b/src/network/access/http2/hpacktable_p.h index d57013150b2..c9277131d2c 100644 --- a/src/network/access/http2/hpacktable_p.h +++ b/src/network/access/http2/hpacktable_p.h @@ -125,6 +125,8 @@ public: quint32 numberOfStaticEntries() const; quint32 numberOfDynamicEntries() const; quint32 dynamicDataSize() const; + quint32 dynamicDataCapacity() const; + quint32 maxDynamicDataCapacity() const; void clearDynamicTable(); bool indexIsValid(quint32 index) const; diff --git a/tests/auto/network/access/hpack/tst_hpack.cpp b/tests/auto/network/access/hpack/tst_hpack.cpp index e6b43eaed45..0092d586803 100644 --- a/tests/auto/network/access/hpack/tst_hpack.cpp +++ b/tests/auto/network/access/hpack/tst_hpack.cpp @@ -36,6 +36,8 @@ private Q_SLOTS: void lookupTableStatic(); void lookupTableDynamic(); + void dynamicTableSizeUpdate(); + void hpackEncodeRequest_data(); void hpackEncodeRequest(); void hpackDecodeRequest_data(); @@ -469,6 +471,30 @@ void tst_Hpack::lookupTableDynamic() QVERIFY(table.indexOf("name1") == 0); } +void tst_Hpack::dynamicTableSizeUpdate() +{ + std::vector buffer; + buffer.reserve(4096); + + BitOStream out(buffer); + + HPack::Encoder encoder(4096u, true); + + QVERIFY(encoder.encodeSizeUpdate(out, 2048u)); + QVERIFY(encoder.encodeRequest(out, {{":method", "GET"}, {":path", "/"}, {":scheme", "https"}})); + + BitIStream in(buffer.data(), buffer.data() + buffer.size()); + HPack::Decoder decoder(4096u); + QCOMPARE(decoder.dynamicTableCapacity(), 4096u); + QCOMPARE(decoder.maxDynamicTableCapacity(), 4096u); + + QVERIFY(decoder.decodeHeaderFields(in)); + QCOMPARE(decoder.dynamicTableCapacity(), 2048u); + // The max is adjusted out-of-band, so it's not changed here: + QCOMPARE(decoder.maxDynamicTableCapacity(), 4096u); +} + + void tst_Hpack::hpackEncodeRequest_data() { QTest::addColumn("compression");