Skip to content

test(android): send Proxy-Authorization via OkHttp in auth tests - #728

Merged
dcalhoun merged 4 commits into
trunkfrom
test/fix-local-android-failures
Sep 25, 2026
Merged

dcalhoun merged 4 commits into
trunkfrom
test/fix-local-android-failures

Conversation

@dcalhoun

@dcalhoun dcalhoun commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

What?

Ensure Proxy-Authorization header is sent with auth test requests.

Why?

The unexpected absence led to test failures in newer, patched JDK versions. Currently only occurring locally.

How?

Rely upon a raw socket server in the auth tests.

Testing Instructions

Run test/android-robolectric-sdk-properties locally when using a patched JDK version.

Accessibility Testing Instructions

N/A, no user-facing changes.

Screenshots or screencast

N/A, no user-facing changes.


AI-generated details

Problem: On a patched JDK (e.g. 21.0.12), make test-android-library-unit fails six HttpServerAuthenticationTests with expected:<200> but was:<407>. CI passes only because its JVM predates the fix. It will fail the same way once CI picks up a patched JDK.

Cause: The August 2026 JDK security update JDK-8384708 (also backported to JDK 17) makes HttpURLConnection remove any user-set Proxy-Authorization header when the connection doesn't use a proxy. The token never reaches HttpServer, which correctly answers 407. Production code is unaffected: it never sends Proxy-Authorization through HttpURLConnection.

Fix: Every test that sends Proxy-Authorization now uses a raw-socket helper, so the test controls the exact bytes sent. This also fixes "request with wrong token returns 407", which passed on patched JDKs without ever sending its token. OkHttp was tried first but throws on a 407 from a direct connection. Each test was checked by breaking the matching server behavior (accepting any token, ignoring Proxy-Authorization, case-sensitive scheme, reversed header precedence, reading an oversized body before auth, and so on) and confirming it fails.

Testing:

  1. make test-android-library-unit (on JDK 21.0.12+ or an equally patched 17.x)
  2. Confirm all HttpServerAuthenticationTests pass.

🤖 Generated with Claude Code

JDK-8384708 (August 2026 security update) makes HttpURLConnection strip a
user-set Proxy-Authorization header from non-proxied connections, so six
tests reached HttpServer without a token and got 407 on patched JDKs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the [Type] Automated Testing Testing infrastructure changes impacting the execution of end-to-end (E2E) and/or unit tests. label Sep 24, 2026
@wpmobilebot

wpmobilebot commented Sep 24, 2026 •

Copy link
Copy Markdown

XCFramework Build

This PR's XCFramework is available for testing. Add the following to your Package.swift:

.package(url: "https://github.com/wordpress-mobile/GutenbergKit", branch: "pr-build/728")

Built from 4026103

@dcalhoun
dcalhoun marked this pull request as ready for review September 24, 2026 16:47
@dcalhoun
dcalhoun requested a review from nbradbury September 24, 2026 16:48
@nbradbury

Copy link
Copy Markdown
Contributor

@dcalhoun Claude had these findings, the first of which sounds valid.

Severity Location Issue Impact
Medium HttpServerAuthenticationTests.kt:49 request with wrong token returns 407 still sends Proxy-Authorization through HttpURLConnection, and patched JDKs (21.0.12+) strip that header, so the test no longer sends a wrong token. On a patched JDK the server sees no token and returns 407 from the missing-header branch, so a regression that accepts any non-empty Bearer token would still pass and the wrong-token path goes untested.
Low HttpServerAuthenticationTests.kt:258 oversized request with valid token is answered 413 still sends Proxy-Authorization through HttpURLConnection, and it only works because streaming mode writes headers before the JDK's getInputStream0 removes them. If a later JDK also strips the header on the output path, the server returns 407 and the client throws HttpRetryException, breaking the same way the six converted tests did (it also contradicts the new helper's KDoc).
Low HttpServerAuthenticationTests.kt:288 OkHttp's RetryAndFollowUpInterceptor throws ProtocolException on a 407 from a direct connection, so statusCode() can never return 407. If server auth regresses, the converted tests fail with a ProtocolException stack trace instead of expected:<200> but was:<407>, and the helper can't be used for any 407 test.
Low HttpServerAuthenticationTests.kt:278 The file already sends raw-socket requests at lines 140, 183, and 207, so a raw-request helper would fix this without adding a second HTTP client that has its own 407 handling. Moving to OkHttp swaps JDK header stripping for OkHttp's ProtocolException, which is why the wrong-token and oversized tests can't use the new helper, while a raw-socket helper would let every Proxy-Authorization test share one path.
Low HttpServerAuthenticationTests.kt:285 headers.toMap().toHeaders() silently collapses duplicate header names, even though the vararg Pair API looks like it accepts repeated headers. A future test that passes two Proxy-Authorization pairs would send only the last one and pass without testing duplicates, so use Headers.Builder or headersOf(...) instead.

dcalhoun and others added 2 commits September 25, 2026 08:31
OkHttp throws on a 407 from a direct connection, so it couldn't carry the
wrong-token or oversized tests, which still used HttpURLConnection. The
wrong-token test passed without sending its token on patched JDKs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A null status line otherwise surfaced as a bare NPE from split().

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@dcalhoun

Copy link
Copy Markdown
Member Author

Thanks for sharing these, @nbradbury. I believe I addressed each in ee32019. I also had Claude check each test for false positives. Ready for another review.

@nbradbury

Copy link
Copy Markdown
Contributor

@dcalhoun As with #729, Claude found some issues but again I feel it sometimes just looks for trouble. I'll approve this and leave Claude's findings for you to peruse.

Severity Location Issue Impact
Medium HttpServerAuthenticationTests.kt:231 The unauthenticated oversized-request test now sends Content-Length: 2048 but never sends the body, so it no longer covers a real client that sends the body right after the headers. When a client sends the 2 KB body immediately, the server's 407-and-close with unread bytes can trigger an RST that turns the 407 into ECONNRESET, and a regression on that path would now pass CI.
Low HttpServerAuthenticationTests.kt:355 SOCKET_TIMEOUT_MS (5000) equals HttpServer.DEFAULT_IDLE_TIMEOUT_MS (5000), and the comment at lines 229–230 says a drain-before-auth server "would never answer," but it would answer 408 after the idle timeout. If auth regresses to run after the drain, the test fails with either a 407-vs-408 assertion or a SocketTimeoutException, depending on which 5 s timer fires first, which makes the failure confusing to diagnose.
Low HttpServerAuthenticationTests.kt:146 receivedAuth (and its counterpart at line 104) is a plain captured var, written on the server's IO thread and read on the test thread with no volatile or synchronization. The JMM gives no happens-before edge between the handler's write and the assertion at line 168, so it can see a stale null and fail intermittently (unlikely in practice, since socket I/O usually acts as a barrier).
Low HttpServerAuthenticationTests.kt:69 The Relay-Authorization tests, the two no-token 407 tests and the auth-disabled test still use HttpURLConnection, so the class keeps two HTTP clients side by side. These tests stay exposed to the next JDK header-filtering change, the same kind of failure this branch fixes, even though send() already handles all of them.
Low HttpServerAuthenticationTests.kt:271 send()/RawResponse duplicate the raw-socket request and response parsing already in MediaUploadServerTest.sendRawRequest/RawHttpResponse, which HttpServerTimeoutTests and HttpServerCancellationTests also hand-roll inline. Two private helpers and about 10 inline socket blocks parse status lines and set timeouts slightly differently, so a parsing fix in one place won't reach the others.
Low HttpServerAuthenticationTests.kt:296 Response parsing takes the status code from split(" ")[1], destructures header lines without checking for a colon, and associate silently keeps only the last duplicate header. A colon-less header line throws IndexOutOfBoundsException instead of a readable assertion failure, and duplicate headers collapse so an assertion could pass against the wrong value.

@nbradbury nbradbury 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.

:shipit:

The socket timeout matched the server's 5s idle timeout, so a drain-before-auth
regression failed as either a 408 or a client timeout depending on which fired
first. Waiting longer makes it fail as 407 vs 408.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@dcalhoun

Copy link
Copy Markdown
Member Author

Addressed the timeout overlap in 4026103. The rest appear pre-existing or quite low impact. Sharing the raw-socket helper with MediaUploadServerTest is a good candidate for a follow-up cleanup.

@dcalhoun
dcalhoun merged commit 9213b53 into trunk Sep 25, 2026
24 checks passed
@dcalhoun
dcalhoun deleted the test/fix-local-android-failures branch September 25, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Type] Automated Testing Testing infrastructure changes impacting the execution of end-to-end (E2E) and/or unit tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants