Skip to content

Fix stale Set-Cookie pointer after HttpHeader mutations - #3606

Merged
chenBright merged 10 commits into
apache:masterfrom
songgaoye:fix/http-header-set-cookie-cache
Oct 25, 2026
Merged

chenBright merged 10 commits into
apache:masterfrom
songgaoye:fix/http-header-set-cookie-cache

Conversation

@songgaoye

@songgaoye songgaoye commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: resolve

Problem Summary:

HttpHeader caches the first Set-Cookie value as a pointer into its
HeaderMap. The pointer becomes stale when the map grows.

Refreshing it with HeaderMap::seek() is not sufficient: HeaderMap is a
multi-map and does not preserve the identity or insertion order of duplicate
Set-Cookie fields. The refreshed pointer may therefore select a different
cookie.

What is changed and the side effects?

Changed:

Store Set-Cookie fields separately in insertion order and keep HeaderMap
synchronized for header iteration and serialization.

Make GetHeader(SET_COOKIE) and GetAllSetCookieHeader() read the stable
ordered storage. This also makes Swap() and Clear() safe without retaining
a stale cached pointer.

Add regression coverage for actual map growth, multiple Set-Cookie fields,
fragmented HTTP parsing, Swap(), copy construction, and assignment.

Side effects:

  • Performance effects:

    The change only adds separate storage and synchronization for Set-Cookie
    fields. Ordinary headers keep the existing path.

  • Breaking backward compatibility:

    None.

@chenBright
chenBright requested a lite review from Copilot October 14, 2026 11:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

4 open findings
What changed in this PR

Fixes a stale cached pointer for the first Set-Cookie header in HttpHeader after HeaderMap mutations (rehash/growth), Swap(), and Clear(), and adds regression tests for these cases.

Changes:

  • Refresh _first_set_cookie after HeaderMap growth and after HttpHeader::Swap()
  • Clear _first_set_cookie in HttpHeader::Clear()
  • Add unit tests covering map growth, Swap(), and Clear()
File Description
test/​brpc_http_message_unittest.cpp Adds regression tests to catch stale _first_set_cookie after growth, Swap(), and Clear().
src/​brpc/​http_header.h Declares RefreshFirstSetCookie() helper to rebuild the cached pointer safely.
src/​brpc/​http_header.cpp Implements pointer refresh logic on rehash/growth and Swap(), and clears cache on Clear().

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread test/brpc_http_message_unittest.cpp Outdated
Comment thread test/brpc_http_message_unittest.cpp
Comment thread src/brpc/http_header.cpp Outdated
Comment thread test/brpc_http_message_unittest.cpp Outdated
@songgaoye

songgaoye commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

The CI failure is unrelated to this PR. It was fixed in #3610.

Comment thread src/brpc/http_header.cpp Outdated
@songgaoye
songgaoye force-pushed the fix/http-header-set-cookie-cache branch from bfe433d to 70608b9 Compare October 15, 2026 05:50
@chenBright
chenBright requested a lite review from Copilot October 16, 2026 03:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/brpc/details/http_message.cpp
Comment thread src/brpc/http_header.cpp
Comment thread test/brpc_http_message_unittest.cpp Outdated
Comment thread test/brpc_http_message_unittest.cpp Outdated
@songgaoye
songgaoye force-pushed the fix/http-header-set-cookie-cache branch from f9c55d5 to b6001dc Compare October 16, 2026 06:02
@chenBright
chenBright requested a lite review from Copilot October 17, 2026 10:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/brpc/http_header.cpp
@songgaoye
songgaoye requested a review from chenBright October 18, 2026 01:13
@wwbmmm

wwbmmm commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

LGTM

Comment thread src/brpc/http_header.cpp
@songgaoye
songgaoye marked this pull request as draft October 21, 2026 15:03
@wwbmmm

wwbmmm commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

[replied by brpc-oncall robot] Agreed that this sequence is unsafe, and thanks for the precise reproducer.

The corruption does not come from the new Set-Cookie storage itself, but from butil::FlatMap's multi-map erase(): when the erased key owns the bucket head and a colliding entry survives, the old implementation destroys the head element and then move-assigns the surviving node into that already-destroyed object. With the default 16 buckets, set-cookie and user-agent both land in bucket 3, so the sequence above hits exactly that path (this was also latently reachable before this PR, but only through RemoveHeader()).

Two follow-ups:

  1. This defect is already fixed on master by Fix use-after-destruction when erasing MultiFlatMap bucket head #3617 (the head element is no longer destroyed when a survivor is moved up). This branch's base predates that fix — the code quoted above (flat_map_inl.h:473 destroying the head unconditionally) is the pre-fix version. Note that CI builds the PR's merge ref against current master, so the current CI run cannot expose it. Please rebase/merge master so the new _headers.erase(SET_COOKIE) calls in AddHeader() and SyncSetCookieHeaders() run on the fixed implementation; no workaround is needed inside http_header.cpp.

  2. Since SetHeader()/SyncSetCookieHeaders() now trigger _headers.erase(SET_COOKIE) on every cookie update (previously only RemoveHeader() did), this collision path becomes far more common. Please add the sequence above as a regression test in test/brpc_http_message_unittest.cpp (Set-Cookie -> colliding header -> Set-Cookie) so the header code keeps exercising it.

@songgaoye
songgaoye marked this pull request as ready for review October 22, 2026 15:10
Comment thread src/brpc/http_header.h Outdated

@chenBright chenBright left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@chenBright
chenBright merged commit cefc091 into apache:master Oct 25, 2026
29 checks passed
@songgaoye
songgaoye deleted the fix/http-header-set-cookie-cache branch October 25, 2026 12:32
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.

4 participants