From 9d1f4e2b3d90096f3d08183edded71612ac0568f Mon Sep 17 00:00:00 2001 From: Mingyu Kang Date: Wed, 20 May 2026 23:58:33 -0700 Subject: [PATCH] Fix SETTINGS_HEADER_TABLE_SIZE handling in OkHttp transport In the OkHttp transport, the client and server were not applying the SETTINGS_HEADER_TABLE_SIZE setting received from the peer to their HPACK encoder (Hpack.Writer). This could lead to protocol errors if the peer requested a smaller table size than the default (4096 octets). This change modifies Http2.Writer.ackSettings to resize the HPACK writer table when this setting is received, ensuring compliance with peer limits and triggering the required Dynamic Table Size Update in the next HEADERS frame. --- .../io/grpc/okhttp/internal/framed/Http2.java | 4 ++ .../okhttp/internal/framed/Http2Test.java | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Http2.java b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Http2.java index 0eb49b9f076..f6408ae61d1 100644 --- a/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Http2.java +++ b/okhttp/third_party/okhttp/main/java/io/grpc/okhttp/internal/framed/Http2.java @@ -397,6 +397,10 @@ static final class Writer implements io.grpc.okhttp.internal.framed.FrameWriter @Override public synchronized void ackSettings(io.grpc.okhttp.internal.framed.Settings peerSettings) throws IOException { if (closed) throw new IOException("closed"); this.maxFrameSize = peerSettings.getMaxFrameSize(maxFrameSize); + int headerTableSize = peerSettings.getHeaderTableSize(); + if (headerTableSize >= 0) { + hpackWriter.resizeHeaderTable(headerTableSize); + } int length = 0; byte type = TYPE_SETTINGS; byte flags = FLAG_ACK; diff --git a/okhttp/third_party/okhttp/test/java/io/grpc/okhttp/internal/framed/Http2Test.java b/okhttp/third_party/okhttp/test/java/io/grpc/okhttp/internal/framed/Http2Test.java index 5631a18515d..ab608d0dfcf 100644 --- a/okhttp/third_party/okhttp/test/java/io/grpc/okhttp/internal/framed/Http2Test.java +++ b/okhttp/third_party/okhttp/test/java/io/grpc/okhttp/internal/framed/Http2Test.java @@ -26,8 +26,10 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.List; import okio.Buffer; import okio.BufferedSink; +import okio.ByteString; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -75,6 +77,46 @@ public void dataFramePadding() throws IOException { assertEquals(2037 - 125, bufferIn.size()); } + @Test + public void writerAckSettingsResizesHpackWriter() throws IOException { + Buffer sink = new Buffer(); + Http2.Writer writer = new Http2.Writer(sink, true); + + // Peer sends SETTINGS_HEADER_TABLE_SIZE = 2048 + Settings peerSettings = new Settings(); + peerSettings.set(Settings.HEADER_TABLE_SIZE, 0, 2048); + + writer.ackSettings(peerSettings); + + // Verify ACK frame is written (9 bytes) + ByteString expectedAck = ByteString.decodeHex("000000040100000000"); + assertEquals(expectedAck, sink.readByteString()); + + // Write headers to trigger dynamic table size update + List
headers = Arrays.asList(new Header("foo", "bar")); + writer.headers(STREAM_ID, headers); + + // Read back the HEADERS frame and verify it has the dynamic table size update + Buffer buffer = sink; + int length = ((buffer.readByte() & 0xff) << 16) + | ((buffer.readByte() & 0xff) << 8) + | (buffer.readByte() & 0xff); + int type = buffer.readByte() & 0xff; + int flags = buffer.readByte() & 0xff; + int streamId = buffer.readInt() & 0x7fffffff; + + assertEquals(Http2.TYPE_HEADERS, type); + assertEquals(12, length); + assertEquals(Http2.FLAG_END_HEADERS, flags); + assertEquals(STREAM_ID, streamId); + + // Verify first bytes of payload are the dynamic table size update (2048) + // In HPACK, table size 2048 update is 0x3F, 0xE1, 0x0F + assertEquals(0x3F, buffer.readByte() & 0xff); + assertEquals(0xE1, buffer.readByte() & 0xff); + assertEquals(0x0F, buffer.readByte() & 0xff); + } + private Buffer createData(int flag, int length, int paddingLength) throws IOException { Buffer sink = new Buffer(); writeLength(sink, length);