Android低于KitKat版本时HttpUrlConnection忽略代理问题
First off, the short answer: AndroidClientHandler does NOT have proper proxy support for JellyBean (API 16 to 18). This is a well-documented limitation in the Xamarin.Android implementation prior to Lollipop (API 21), and it’s exactly why your requests aren’t routing through the proxy on older devices.
Why This Happens
The core issue lies in how AndroidClientHandler manages socket connections pre-API 21. On KitKat (API 19) and above, a partial fix added support for passing proxy configuration to the underlying socket logic, but JellyBean builds completely lack this handling. When you send an HTTP/HTTPS request, the handler ignores your proxy settings entirely and connects directly to the target server.
Since you’re already subclassing AndroidClientHandler to bypass SSL validation (which makes perfect sense here, since your proxy handles SSL termination), you can extend this subclass to manually add proxy support for JellyBean devices.
Fix: Manually Implement Proxy Logic in Your Custom Handler
Here’s how you can modify your subclass to force proxy routing on JellyBean devices:
using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using Android.OS; using Xamarin.Android.Net; public class ProxyEnabledAndroidHandler : AndroidClientHandler { private readonly IWebProxy _targetProxy; public ProxyEnabledAndroidHandler(IWebProxy proxy) { _targetProxy = proxy; // Your existing SSL bypass logic (as required by your library) ServerCertificateCustomValidationCallback = (sender, cert, chain, errors) => true; } protected override async Task<Stream> GetStreamAsync(HttpWebRequest request, CancellationToken cancellationToken) { // Ensure the request uses our proxy if (_targetProxy != null && request.Proxy == null) { request.Proxy = _targetProxy; } // Special handling for JellyBean and older if (Build.VERSION.SdkInt <= BuildVersionCodes.JellyBeanMr2) { var proxyUri = _targetProxy.GetProxy(request.RequestUri); if (proxyUri != null && proxyUri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase)) { // Connect to the proxy server directly using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); await socket.ConnectAsync(proxyUri.Host, proxyUri.Port).ConfigureAwait(false); // Send CONNECT request to establish tunnel to target server var connectCommand = $"CONNECT {request.RequestUri.Host}:{request.RequestUri.Port} HTTP/1.1\r\nHost: {request.RequestUri.Host}:{request.RequestUri.Port}\r\n\r\n"; var connectBytes = Encoding.ASCII.GetBytes(connectCommand); await socket.SendAsync(connectBytes, SocketFlags.None).ConfigureAwait(false); // Verify proxy accepted the connection var responseBuffer = new byte[1024]; var bytesRead = await socket.ReceiveAsync(responseBuffer, SocketFlags.None).ConfigureAwait(false); var proxyResponse = Encoding.ASCII.GetString(responseBuffer, 0, bytesRead); if (!proxyResponse.StartsWith("HTTP/1.1 200", StringComparison.OrdinalIgnoreCase)) { throw new IOException($"Proxy connection failed: {proxyResponse.Trim()}"); } // Return the tunneled stream for the request to use return new NetworkStream(socket, true); } } // For newer versions, use the base handler's native logic return await base.GetStreamAsync(request, cancellationToken).ConfigureAwait(false); } }
Key Notes
- This code manually creates a tunnel through the proxy using the HTTP
CONNECTmethod, which works for both HTTPS and HTTP requests. - Since your proxy handles SSL termination, bypassing client-side SSL validation is appropriate here—just ensure you fully trust the proxy you’re working with.
- If you only need to handle HTTP (not HTTPS) requests, you can simplify the logic by sending the full request directly to the proxy instead of using the
CONNECTmethod.
内容的提问来源于stack exchange,提问作者fmaccaroni

