Skip to content

oauthex: parse WWW-Authenticate in linear time - #1456

Merged
guglielmo-san merged 1 commit into
modelcontextprotocol:mainfrom
jyotir-aditya:fix-noissue-www-authenticate-quadratic
Oct 26, 2026
Merged

guglielmo-san merged 1 commit into
modelcontextprotocol:mainfrom
jyotir-aditya:fix-noissue-www-authenticate-quadratic

Conversation

@jyotir-aditya

@jyotir-aditya jyotir-aditya commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

splitChallenges rescanned the rest of the header after every comma, so parsing took quadratic time (100 KB of commas: 5.7 s on main). The lookahead is now computed once per run of commas and blanks, and startsWithAuthParam reads only the next token.

Output is unchanged except for one case: a comma inside the name before the = (for example x, a,b=c) now ends the challenge. A token cannot contain a comma (RFC 9110, section 5.6.2); before, a,b was taken as a parameter name.

Tests

  • TestSplitChallenges: cases for params-less challenges, a blank before =, whitespace inside the name, a non-blank space before =, and the comma case above. All but the comma case pass on the old code, so they pin existing behavior.
  • TestParseWWWAuthenticateLinearTime: a 100 KB run of commas must parse within 2 s. The old code takes about 5.7 s and fails; the new code takes well under 100 ms. The input is deliberately small so the test adds little load to the parallel mcp tests.
  • I also compared the new splitChallenges with the old one on 3,000,000 random headers (scratch test, not included): they differ only by the comma rule.
  • gofmt -l ., go vet ./..., go test ./... and go test -race ./oauthex are clean. I did not run the conformance scripts; the change does not touch protocol behavior.

Fixes #1455

splitChallenges looked ahead from every comma by trimming and scanning
the rest of the header, so parsing took time quadratic in the header
length. A header made of 100 KB of commas took about 5.7s to parse, and
the work does not observe context cancellation.

Compute the lookahead once per run of commas and blanks, and read only
the next token to decide whether a comma starts an auth-param
(startsWithAuthParam). The result is unchanged except for a comma inside
the name before the '=', such as `x, a,b=c`: a token cannot contain a
comma (RFC 9110, section 5.6.2), so that comma now ends the challenge.

Add cases to TestSplitChallenges and TestParseWWWAuthenticateLinearTime,
which checks that a 100 KB run of commas parses within a deadline.

Fixes modelcontextprotocol#1455
@jyotir-aditya
jyotir-aditya force-pushed the fix-noissue-www-authenticate-quadratic branch from 78e9a8e to 4ab9b58 Compare October 26, 2026 03:00
@jyotir-aditya

Copy link
Copy Markdown
Contributor Author

The race-test job failed on TestResourceSubscriptions_InMemory (mcp_test.go:3074: timed out waiting for resource update), in a package this PR doesn't touch. That test is flaky on its own: on unmodified main (2e00768), go test -race -cpu=2 ./mcp -run '^TestResourceSubscriptions_InMemory$' -count=300 failed 2 times in 300 runs.

My original timing test did make it fail more often, though. It ran five 200 KB inputs in parallel with the mcp tests, and in a controlled A/B (2 cores, -race, 900 runs each) the flake fired 39 times on this branch against 1-3 times on main. I've replaced it with a single 100 KB input that takes well under 100 ms. With that version the same setup gives 4 against 3 on main, which is no difference. The new commit only changes that test; the fix itself is unchanged.

Could a maintainer re-run the failed job? I can't.

@guglielmo-san
guglielmo-san merged commit b42403f into modelcontextprotocol:main Oct 26, 2026
9 checks passed
@guglielmo-san

Copy link
Copy Markdown
Contributor

@jyotir-aditya thank you for the contribution!

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.

oauthex: ParseWWWAuthenticate takes quadratic time on long headers

2 participants