Skip to content

[tests] Use local servers for remaining networking tests - #12098

Closed
simonrozsival wants to merge 2 commits into
dotnet:mainfrom
simonrozsival:local-test-servers-network
Closed

[tests] Use local servers for remaining networking tests#12098
simonrozsival wants to merge 2 commits into
dotnet:mainfrom
simonrozsival:local-test-servers-network

Conversation

@simonrozsival

Copy link
Copy Markdown
Member

Context

Following up on #12022 (which migrated most on-device networking tests off live external URLs to the loopback LocalHttpServer/LocalHttpsServer) and #12058 (which handles SslTest.HttpsShouldWork), a few on-device tests were still reaching out to the public internet. Those requests made the tests flaky whenever the external endpoints were slow, rate-limited, or unavailable.

This migrates the remaining tests to local loopback servers.

Tests migrated

TestWas hitting
SslTest.SslWithinTasksShouldWorkdotnet.microsoft.com
ProxyTest.QuoteInvalidQuoteUrlsShouldWorkmsftconnecttest.com
AndroidMessageHandlerTests.ServerCertificateCustomValidationCallback_ApproveRequestwww.microsoft.com
AndroidMessageHandlerTests.ServerCertificateCustomValidationCallback_RejectRequestwww.microsoft.com
AndroidMessageHandlerTests.AndroidMessageHandlerSendsClientCertificatecorefx-net-tls.azurewebsites.net
AndroidMessageHandlerIntegrationTests.GetString_Manygoogle.com
HttpClientHandlerTestBase.Disposedgoogle.com
WebSocketTests.TestSocketConnectionecho.websocket.org (was [Ignore]d)

ProxyTest keeps its original point — an unescaped | in the query string (?query&foo|bar) — now exercised against the local /ok endpoint. WebSocketTests was previously disabled because the public echo server no longer exists; it is re-enabled against a local server.

Test infrastructure (LocalTestServers.cs)

  • Client certificates (mutual TLS): LocalHttpsServer.Start (requestClientCertificate: true) now requests a client certificate during the TLS handshake, and a new /echo-client-certificate endpoint returns the certificate the server received (base64 DER). This replaces the external EchoClientCertificate.ashx endpoint.
  • LocalWebSocketServer: a loopback WebSocket echo server that performs the upgrade handshake and echoes messages back (uses WebSocket.CreateFromStream after a manual 101 Switching Protocols response).

Notes

  • HttpsShouldWork is intentionally left to Use a local HTTPS server in SslTest.HttpsShouldWork #12058.
  • The http://10.255.255.1 cancellation/timeout tests deliberately use an unroutable address (not the public internet) and are unchanged.
  • Cosmetic external hostnames that were never actually contacted (CancelRequestViaProxy's base address behind a dead proxy, and DisallowAutoRedirect's redirect target) were pointed at localhost.

The new server logic (HTTP, HTTPS, mutual-TLS echo, and WebSocket echo) was verified end-to-end against HttpClient and ClientWebSocket on desktop .NET.

PR #12022 migrated most on-device networking tests off live external
URLs to the loopback `LocalHttpServer`/`LocalHttpsServer`, and #12058
handles `SslTest.HttpsShouldWork`. This migrates the remaining tests
that still reached out to the public internet, which made them flaky
when those endpoints were slow or unavailable:
- `SslTest.SslWithinTasksShouldWork` (dotnet.microsoft.com)
- `ProxyTest.QuoteInvalidQuoteUrlsShouldWork` (msftconnecttest.com) -
keeps the `?query&foo|bar` unescaped-pipe scenario against `/ok`.
- `AndroidMessageHandlerTests.ServerCertificateCustomValidationCallback_ApproveRequest`
and `_RejectRequest` (www.microsoft.com)
- `AndroidMessageHandlerTests.AndroidMessageHandlerSendsClientCertificate`
(corefx-net-tls.azurewebsites.net)
- `AndroidMessageHandlerIntegrationTests.GetString_Many` (google.com)
- `HttpClientHandlerTestBase.Disposed` (google.com)
- `WebSocketTests.TestSocketConnection` (echo.websocket.org) - was
`[Ignore]`d because the echo server no longer exists; re-enabled
against a local server.
To support these, `LocalTestServers.cs` gains:
- Opt-in client-certificate (mutual TLS) support on `LocalHttpsServer`
plus an `/echo-client-certificate` endpoint that returns the client
certificate the server received.
- A new loopback `LocalWebSocketServer` that performs the WebSocket
upgrade handshake and echoes messages back.
Also cleaned up cosmetic external hostnames in `CancelRequestViaProxy`
(request never connects; goes through a dead proxy) and the
`DisallowAutoRedirect` redirect target (never followed).
The `http://10.255.255.1` cancellation/timeout tests intentionally use
an unroutable address and are left unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 289a099a-278f-432b-b2b4-aac036e6e2dc
CopilotAI review requested due to automatic review settings July 14, 2026 06:12
@simonrozsival
simonrozsival marked this pull request as draft July 14, 2026 06:12

CopilotAI 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.

Pull request overview

This PR completes the migration of remaining on-device networking tests away from public internet dependencies by extending the loopback test-server infrastructure (HTTP/HTTPS, mutual-TLS, and WebSocket) and updating tests to use those local endpoints. This reduces flakiness from external endpoint availability, latency, and rate limiting.

Changes:

  • Extend LocalHttpsServer to optionally request a client certificate and add an /echo-client-certificate endpoint.
  • Add a loopback LocalWebSocketServer echo implementation for ClientWebSocket-based tests.
  • Update remaining tests to target local loopback servers instead of public hostnames, re-enabling the WebSocket test.
Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.Net/LocalTestServers.csAdds mutual-TLS client-certificate echo endpoint and introduces a local WebSocket echo server.
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerTests.csSwitches cert-validation and client-cert tests to use LocalHttpsServer loopback endpoints.
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerIntegrationTests.csReplaces remaining external google.com usage with LocalHttpServer in concurrent request test and adjusts cosmetic host strings.
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidHandlerTestBase.csUpdates disposed-handler test to use LocalHttpServer instead of an external URL.
tests/Mono.Android-Tests/Mono.Android-Tests/System.Net/WebSocketTests.csRe-enables WebSocket coverage by using the new loopback LocalWebSocketServer.
tests/Mono.Android-Tests/Mono.Android-Tests/System.Net/SslTest.csUpdates SslWithinTasksShouldWork to use LocalHttpsServer loopback endpoint.
tests/Mono.Android-Tests/Mono.Android-Tests/System.Net/ProxyTest.csUpdates the proxy quoting test to use LocalHttpServer loopback endpoint.

Copilot's findings

Comments suppressed due to low confidence (1)

tests/Mono.Android-Tests/Mono.Android-Tests/System.Net/SslTest.cs:39

  • ServicePointManager.ServerCertificateValidationCallback is a process-wide/static setting. If anything throws between setting it and resetting it, subsequent tests can be affected. Wrapping the body in a try/finally ensures the original callback is always restored.
 var cb = ServicePointManager.ServerCertificateValidationCallback;
ServicePointManager.ServerCertificateValidationCallback = (s, cert, chain, policy) => {
Console.WriteLine ("# ServerCertificateValidationCallback");
return true;
};
  • Files reviewed: 7/7 changed files
  • Comments generated: 3

Comment on lines +81 to 83
var t = ConnectIgnoreFailure (() => c.GetAsync (server.OkUri), out bool connectionFailed);
if (connectionFailed)
return;
Comment on lines 34 to 38
} catch (WebException ex) when (
ex.Status == WebExceptionStatus.ConnectFailure ||
ex.Status == WebExceptionStatus.NameResolutionFailure ||
ex.Status == WebExceptionStatus.Timeout) {
Assert.Ignore ($"Ignoring network failure: {ex.Message}");
Comment on lines 152 to 153
Assert.IsTrue (callbackHasBeenCalled, "custom validation callback hasn't been called");
}
The local WebSocket test server computes the Sec-WebSocket-Accept value
using SHA-1, as mandated by the WebSocket handshake (RFC 6455 §1.3). This
is a protocol requirement, not a security-sensitive choice, but CA5350
(treated as an error in the test build) flagged it and broke the
Mono.Android.NET-Tests APK build. Suppress CA5350 around the SHA-1 usage
with a justification.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 528dc903-4f7c-4a75-b74b-da0fbfe23f01
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

Superseded by #12099, re-opened as a direct PR (branch pushed to dotnet/android) so the dotnet-android pipeline runs automatically. Also includes the CA5350 fix for the RFC 6455 WebSocket handshake SHA-1 that broke the test-APK build here.

simonrozsival added a commit that referenced this pull request Jul 14, 2026
> Re-opened as a direct PR (supersedes #12098) so the `dotnet-android` pipeline runs automatically without `/azp run` approval.
### Context
Following up on #12022 (which migrated most on-device networking tests off live external URLs to the loopback `LocalHttpServer`/`LocalHttpsServer`) and #12058 (which handles `SslTest.HttpsShouldWork`), a few on-device tests were still reaching out to the public internet. Those requests made the tests flaky whenever the external endpoints were slow, rate-limited, or unavailable.
This migrates the remaining tests to local loopback servers.
### Tests migrated
| Test | Was hitting |
| ---- | ----------- |
| `SslTest.SslWithinTasksShouldWork` | `dotnet.microsoft.com` |
| `ProxyTest.QuoteInvalidQuoteUrlsShouldWork` | `msftconnecttest.com` |
| `AndroidMessageHandlerTests.ServerCertificateCustomValidationCallback_ApproveRequest` | `www.microsoft.com` |
| `AndroidMessageHandlerTests.ServerCertificateCustomValidationCallback_RejectRequest` | `www.microsoft.com` |
| `AndroidMessageHandlerTests.AndroidMessageHandlerSendsClientCertificate` | `corefx-net-tls.azurewebsites.net` |
| `AndroidMessageHandlerIntegrationTests.GetString_Many` | `google.com` |
| `HttpClientHandlerTestBase.Disposed` | `google.com` |
| `WebSocketTests.TestSocketConnection` | `echo.websocket.org` (was `[Ignore]`d) |
`ProxyTest` keeps its original point — an unescaped `|` in the query string (`?query&foo|bar`) — now exercised against the local `/ok` endpoint. `WebSocketTests` was previously disabled because the public echo server no longer exists; it is re-enabled against a local server.
### Test infrastructure (`LocalTestServers.cs`)
- **Client certificates (mutual TLS)**: `LocalHttpsServer.Start (requestClientCertificate: true)` now requests a client certificate during the TLS handshake, and a new `/echo-client-certificate` endpoint returns the certificate the server received (base64 DER). This replaces the external `EchoClientCertificate.ashx` endpoint.
- **`LocalWebSocketServer`**: a loopback WebSocket echo server that performs the upgrade handshake and echoes messages back (uses `WebSocket.CreateFromStream` after a manual `101 Switching Protocols` response).
### Notes
- `HttpsShouldWork` is intentionally left to #12058.
- The `http://10.255.255.1` cancellation/timeout tests deliberately use an unroutable address (not the public internet) and are unchanged.
- Cosmetic external hostnames that were never actually contacted (`CancelRequestViaProxy`'s base address behind a dead proxy, and `DisallowAutoRedirect`'s redirect target) were pointed at `localhost`.
The new server logic (HTTP, HTTPS, mutual-TLS echo, and WebSocket echo) was verified end-to-end against `HttpClient` and `ClientWebSocket` on desktop .NET.
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 14, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@simonrozsival