Skip to content

okhttp: optimize HPACK to index :path and fix dynamic table eviction#12907

Open
AgraVator wants to merge 2 commits into
grpc:masterfrom
AgraVator:fix-okhttp-hpack-eviction-12819
Open

okhttp: optimize HPACK to index :path and fix dynamic table eviction#12907
AgraVator wants to merge 2 commits into
grpc:masterfrom
AgraVator:fix-okhttp-hpack-eviction-12819

Conversation

@AgraVator

Copy link
Copy Markdown
Contributor

Background & Problem Statement

In gRPC over HTTP/2, the :path pseudo-header represents static RPC service/method names (e.g., /package.Service/Method), which remain constant across calls on long-lived channels.

Indexing :path in HPACK's dynamic table reduces header transmission overhead from ~40+ bytes per RPC down to a single 1-byte indexed header field (0x80 | index).

PR #12799 (commit 397d3e7214) originally enabled :path indexing, but was reverted in PR #12820 (commit 52f2cd5982) because continuous dynamic table insertions and evictions uncovered latent bugs in OkHttp's legacy Hpack.java implementation during internal Google testing (b/514688016).

This PR resolves tracking issue #12819 by fixing the underlying Hpack.java dynamic table bugs and safely re-enabling :path indexing.


Changes Included in This PR

  1. Fix Off-by-One NPE Loop Bound in evictToRecoverBytes:

    • In both Hpack.Reader and Hpack.Writer, when bytesToRecover exceeded total table byte count, j >= nextDynamicTableIndex evaluated slot nextDynamicTableIndex (which contains null), throwing a NullPointerException.
    • Updated loop condition to j > nextDynamicTableIndex.
  2. Garbage Collection Memory Leak Cleanup:

    • System.arraycopy shifted active elements rightward during eviction, but left stale Header/ByteString references in vacated array slots.
    • Added Arrays.fill(dynamicTable, oldNext + 1, oldNext + 1 + entriesToEvict, null); post-arraycopy to sever hard references and enable GC.
  3. Normalized Header Casing in Dynamic Table:

    • Header entries inserted into dynamicTable are now guaranteed to store lowercased name keys (new Header(name, value)), preventing dynamic table key lookup mismatches.
  4. Re-enabled :path Pseudo-Header Indexing:

    • Updated Writer.writeHeaders to allow both :authority and :path to be incrementally indexed in the HPACK dynamic table.
  5. Unit Tests:

    • Updated HpackTest.java to test :path indexing reuse (pseudoHeaderIndexingForPathAndAuthority) and added evictToRecoverBytesDoesNotNpeWhenBytesToRecoverExceedsTable.

Issue References

…bugs (grpc#12819)

Allow the :path pseudo-header to be added to the HPACK dynamic table in
the gRPC-Java OkHttp transport, resolving the original goal of grpc#12819
and redoing reverted commit 397d3e7 (grpc#12799, grpc#12820).

Background & Root Cause:
In gRPC over HTTP/2, :path headers represent static service/method names
(e.g., /package.Service/Method), which are constant across RPC calls.
Previously, indexing :path was reverted (grpc#12820) because high dynamic
table churn uncovered latent bugs in legacy OkHttp Hpack.java:

1. Off-by-one NPE loop bound in evictToRecoverBytes:
   When bytesToRecover exceeded total dynamic table size, the loop condition
   (j >= nextDynamicTableIndex) inspected the unallocated nextDynamicTableIndex
   slot containing null, throwing a NullPointerException. Fixed by changing
   the loop bound to (j > nextDynamicTableIndex).

2. Reference leak post-eviction:
   System.arraycopy shifted active entries rightward, but left stale
   Header/ByteString references in vacated array slots. Fixed by setting
   vacated array slots to null via Arrays.fill().

3. Case sensitivity normalization:
   Header entries inserted into dynamicTable are now guaranteed to store
   lowercased name keys, preventing failed dynamic table header lookups.

4. Table Size Setting Handling:
   Works in conjunction with grpc#12818 (SETTINGS_HEADER_TABLE_SIZE handling).

Fixes grpc#12819
Related: grpc#12799, grpc#12818, grpc#12820, b/514688016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant