diff --git a/src/Android/Android.csproj b/src/Android/Android.csproj
index 49b285392..f9f9dbd06 100644
--- a/src/Android/Android.csproj
+++ b/src/Android/Android.csproj
@@ -316,7 +316,6 @@
-
diff --git a/src/Android/CustomAndroidClientHandler.cs b/src/Android/CustomAndroidClientHandler.cs
deleted file mode 100644
index 654d85bfa..000000000
--- a/src/Android/CustomAndroidClientHandler.cs
+++ /dev/null
@@ -1,774 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Compression;
-using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Net.Http.Headers;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-using Android.Runtime;
-using Java.IO;
-using Java.Net;
-using Java.Security;
-using Java.Security.Cert;
-using Javax.Net.Ssl;
-
-namespace Xamarin.Android.Net
-{
- ///
- /// A custom implementation of which internally uses
- /// (or its HTTPS incarnation) to send HTTP requests.
- ///
- ///
- /// Instance of this class is used to configure instance
- /// in the following way:
- ///
- ///
- /// var handler = new AndroidClientHandler {
- /// UseCookies = true,
- /// AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
- /// };
- ///
- /// var httpClient = new HttpClient (handler);
- /// var response = httpClient.GetAsync ("http://example.com")?.Result as AndroidHttpResponseMessage;
- ///
- ///
- /// The class supports pre-authentication of requests albeit in a slightly "manual" way. Namely, whenever a request to a server requiring authentication
- /// is made and no authentication credentials are provided in the property (which is usually the case on the first
- /// request), the property will return true and the property will
- /// contain all the authentication information gathered from the server. The application must then fill in the blanks (i.e. the credentials) and re-send
- /// the request configured to perform pre-authentication. The reason for this manual process is that the underlying Java HTTP client API supports only a
- /// single, VM-wide, authentication handler which cannot be configured to handle credentials for several requests. AndroidClientHandler, therefore, implements
- /// the authentication in managed .NET code. Message handler supports both Basic and Digest authentication. If an authentication scheme that's not supported
- /// by AndroidClientHandler is requested by the server, the application can provide its own authentication module (,
- /// ) to handle the protocol authorization.
- /// AndroidClientHandler also supports requests to servers with "invalid" (e.g. self-signed) SSL certificates. Since this process is a bit convoluted using
- /// the Java APIs, AndroidClientHandler defines two ways to handle the situation. First, easier, is to store the necessary certificates (either CA or server certificates)
- /// in the collection or, after deriving a custom class from AndroidClientHandler, by overriding one or more methods provided for this purpose
- /// (, and ). The former method should be sufficient
- /// for most use cases, the latter allows the application to provide fully customized key store, trust manager and key manager, if needed. Note that the instance of
- /// AndroidClientHandler configured to accept an "invalid" certificate from the particular server will most likely fail to validate certificates from other servers (even
- /// if they use a certificate with a fully validated trust chain) unless you store the CA certificates from your Android system in along with
- /// the self-signed certificate(s).
- ///
- public class CustomAndroidClientHandler : HttpClientHandler
- {
- sealed class RequestRedirectionState
- {
- public Uri NewUrl;
- public int RedirectCounter;
- public HttpMethod Method;
- }
-
- internal const string LOG_APP = "monodroid-net";
-
- const string GZIP_ENCODING = "gzip";
- const string DEFLATE_ENCODING = "deflate";
- const string IDENTITY_ENCODING = "identity";
-
- static readonly HashSet known_content_headers = new HashSet(StringComparer.OrdinalIgnoreCase) {
- "Allow",
- "Content-Disposition",
- "Content-Encoding",
- "Content-Language",
- "Content-Length",
- "Content-Location",
- "Content-MD5",
- "Content-Range",
- "Content-Type",
- "Expires",
- "Last-Modified"
- };
-
- static readonly List authModules = new List {
- new AuthModuleBasic (),
- // COMMENTED OUT: Kyle
- //new AuthModuleDigest ()
- };
-
- bool disposed;
-
- // Now all hail Java developers! Get this... HttpURLClient defaults to accepting AND
- // uncompressing the gzip content encoding UNLESS you set the Accept-Encoding header to ANY
- // value. So if we set it to 'gzip' below we WILL get gzipped stream but HttpURLClient will NOT
- // uncompress it any longer, doh. And they don't support 'deflate' so we need to handle it ourselves.
- bool decompress_here;
-
- ///
- ///
- /// Gets or sets the pre authentication data for the request. This property must be set by the application
- /// before the request is made. Generally the value can be taken from
- /// after the initial request, without any authentication data, receives the authorization request from the
- /// server. The application must then store credentials in instance of and
- /// assign the instance to this propery before retrying the request.
- ///
- ///
- /// The property is never set by AndroidClientHandler.
- ///
- ///
- /// The pre authentication data.
- public AuthenticationData PreAuthenticationData { get; set; }
-
- ///
- /// If the website requires authentication, this property will contain data about each scheme supported
- /// by the server after the response. Note that unauthorized request will return a valid response - you
- /// need to check the status code and and (re)configure AndroidClientHandler instance accordingly by providing
- /// both the credentials and the authentication scheme by setting the
- /// property. If AndroidClientHandler is not able to detect the kind of authentication scheme it will store an
- /// instance of with its property
- /// set to AuthenticationScheme.Unsupported and the application will be responsible for providing an
- /// instance of which handles this kind of authorization scheme
- /// (
- ///
- public IList RequestedAuthentication { get; private set; }
-
- ///
- /// Server authentication response indicates that the request to authorize comes from a proxy if this property is true.
- /// All the instances of stored in the property will
- /// have their preset to the same value as this property.
- ///
- public bool ProxyAuthenticationRequested { get; private set; }
-
- ///
- /// If true then the server requested authorization and the application must use information
- /// found in to set the value of
- ///
- public bool RequestNeedsAuthorization
- {
- get { return RequestedAuthentication?.Count > 0; }
- }
-
- ///
- ///
- /// If the request is to the server protected with a self-signed (or otherwise untrusted) SSL certificate, the request will
- /// fail security chain verification unless the application provides either the CA certificate of the entity which issued the
- /// server's certificate or, alternatively, provides the server public key. Whichever the case, the certificate(s) must be stored
- /// in this property in order for AndroidClientHandler to configure the request to accept the server certificate.
- /// AndroidClientHandler uses a custom and to configure the connection.
- /// If, however, the application requires finer control over the SSL configuration (e.g. it implements its own TrustManager) then
- /// it should leave this property empty and instead derive a custom class from AndroidClientHandler and override, as needed, the
- /// , and methods
- /// instead
- ///
- /// The trusted certs.
- public IList TrustedCerts { get; set; }
-
- protected override void Dispose(bool disposing)
- {
- disposed = true;
-
- base.Dispose(disposing);
- }
-
- protected void AssertSelf()
- {
- if(!disposed)
- return;
- throw new ObjectDisposedException(nameof(AndroidClientHandler));
- }
-
- string EncodeUrl(Uri url)
- {
- if(url == null)
- return String.Empty;
-
- if(String.IsNullOrEmpty(url.Query))
- return Uri.EscapeUriString(url.ToString());
-
- // UriBuilder takes care of encoding everything properly
- var bldr = new UriBuilder(url);
- if(url.IsDefaultPort)
- bldr.Port = -1; // Avoids adding :80 or :443 to the host name in the result
-
- // bldr.Uri.ToString () would ruin the good job UriBuilder did
- return bldr.ToString();
- }
-
- ///
- /// Creates, configures and processes an asynchronous request to the indicated resource.
- ///
- /// Task in which the request is executed
- /// Request provided by
- /// Cancellation token.
- protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- AssertSelf();
- if(request == null)
- throw new ArgumentNullException(nameof(request));
-
- if(!request.RequestUri.IsAbsoluteUri)
- throw new ArgumentException("Must represent an absolute URI", "request");
-
- var redirectState = new RequestRedirectionState
- {
- NewUrl = request.RequestUri,
- RedirectCounter = 0,
- Method = request.Method
- };
- while(true)
- {
- URL java_url = new URL(EncodeUrl(redirectState.NewUrl));
- URLConnection java_connection = java_url.OpenConnection();
- HttpURLConnection httpConnection = await SetupRequestInternal(request, java_connection);
- HttpResponseMessage response = await ProcessRequest(request, java_url, httpConnection, cancellationToken, redirectState);
- if(response != null)
- return response;
-
- if(redirectState.NewUrl == null)
- throw new InvalidOperationException("Request redirected but no new URI specified");
- request.Method = redirectState.Method;
- }
- }
-
- Task ProcessRequest(HttpRequestMessage request, URL javaUrl, HttpURLConnection httpConnection, CancellationToken cancellationToken, RequestRedirectionState redirectState)
- {
- cancellationToken.ThrowIfCancellationRequested();
- httpConnection.InstanceFollowRedirects = false; // We handle it ourselves
- RequestedAuthentication = null;
- ProxyAuthenticationRequested = false;
-
- return DoProcessRequest(request, javaUrl, httpConnection, cancellationToken, redirectState);
- }
-
- async Task DoProcessRequest(HttpRequestMessage request, URL javaUrl, HttpURLConnection httpConnection, CancellationToken cancellationToken, RequestRedirectionState redirectState)
- {
- if(cancellationToken.IsCancellationRequested)
- {
- cancellationToken.ThrowIfCancellationRequested();
- }
-
- try
- {
- await Task.WhenAny(
- httpConnection.ConnectAsync(),
- Task.Run(() => { cancellationToken.WaitHandle.WaitOne(); }))
- .ConfigureAwait(false);
- }
- catch(Java.Net.ConnectException ex)
- {
- // Wrap it nicely in a "standard" exception so that it's compatible with HttpClientHandler
- throw new WebException(ex.Message, ex, WebExceptionStatus.ConnectFailure, null);
- }
-
- if(cancellationToken.IsCancellationRequested)
- {
- httpConnection.Disconnect();
- cancellationToken.ThrowIfCancellationRequested();
- }
- cancellationToken.Register(httpConnection.Disconnect);
-
- if(httpConnection.DoOutput)
- {
- using(var stream = await request.Content.ReadAsStreamAsync())
- {
- await stream.CopyToAsync(httpConnection.OutputStream, 4096, cancellationToken)
- .ConfigureAwait(false);
- }
- }
-
- if(cancellationToken.IsCancellationRequested)
- {
- httpConnection.Disconnect();
- cancellationToken.ThrowIfCancellationRequested();
- }
-
- var statusCode = await Task.Run(() => (HttpStatusCode)httpConnection.ResponseCode).ConfigureAwait(false);
- var connectionUri = new Uri(httpConnection.URL.ToString());
-
- // If the request was redirected we need to put the new URL in the request
- request.RequestUri = connectionUri;
- var ret = new AndroidHttpResponseMessage(javaUrl, httpConnection)
- {
- RequestMessage = request,
- ReasonPhrase = httpConnection.ResponseMessage,
- StatusCode = statusCode,
- };
-
- bool disposeRet;
- if(HandleRedirect(statusCode, httpConnection, redirectState, out disposeRet))
- {
- if(disposeRet)
- {
- ret.Dispose();
- ret = null;
- }
- return ret;
- }
-
- switch(statusCode)
- {
- case HttpStatusCode.Unauthorized:
- case HttpStatusCode.ProxyAuthenticationRequired:
- // We don't resend the request since that would require new set of credentials if the
- // ones provided in Credentials are invalid (or null) and that, in turn, may require asking the
- // user which is not something that should be taken care of by us and in this
- // context. The application should be responsible for this.
- // HttpClientHandler throws an exception in this instance, but I think it's not a good
- // idea. We'll return the response message with all the information required by the
- // application to fill in the blanks and provide the requested credentials instead.
- //
- // We return the body of the response too, but the Java client will throw
- // a FileNotFound exception if we attempt to access the input stream.
- // Instead we try to read the error stream and return an default message if the error stream isn't readable.
- ret.Content = GetErrorContent(httpConnection, new StringContent("Unauthorized", Encoding.ASCII));
- CopyHeaders(httpConnection, ret);
-
- if(ret.Headers.WwwAuthenticate != null)
- {
- ProxyAuthenticationRequested = false;
- CollectAuthInfo(ret.Headers.WwwAuthenticate);
- }
- else if(ret.Headers.ProxyAuthenticate != null)
- {
- ProxyAuthenticationRequested = true;
- CollectAuthInfo(ret.Headers.ProxyAuthenticate);
- }
-
- // COMMENTED OUT: Kyle
- //ret.RequestedAuthentication = RequestedAuthentication;
- return ret;
- }
-
- if(!IsErrorStatusCode(statusCode))
- {
- ret.Content = GetContent(httpConnection, httpConnection.InputStream);
- }
- else
- {
- // For 400 >= response code <= 599 the Java client throws the FileNotFound exception when attempting to read from the input stream.
- // Instead we try to read the error stream and return an empty string if the error stream isn't readable.
- ret.Content = GetErrorContent(httpConnection, new StringContent(String.Empty, Encoding.ASCII));
- }
-
- CopyHeaders(httpConnection, ret);
-
- IEnumerable cookieHeaderValue;
- if(!UseCookies || CookieContainer == null || !ret.Headers.TryGetValues("Set-Cookie", out cookieHeaderValue) || cookieHeaderValue == null)
- {
- return ret;
- }
-
- try
- {
- CookieContainer.SetCookies(connectionUri, String.Join(",", cookieHeaderValue));
- }
- catch(Exception ex)
- {
- // We don't want to terminate the response because of a bad cookie, hence just reporting
- // the issue. We might consider adding a virtual method to let the user handle the
- // issue, but not sure if it's really needed. Set-Cookie header will be part of the
- // header collection so the user can always examine it if they spot an error.
- }
-
- return ret;
- }
-
- HttpContent GetErrorContent(HttpURLConnection httpConnection, HttpContent fallbackContent)
- {
- var contentStream = httpConnection.ErrorStream;
-
- if(contentStream != null)
- {
- return GetContent(httpConnection, contentStream);
- }
-
- return fallbackContent;
- }
-
- HttpContent GetContent(URLConnection httpConnection, Stream contentStream)
- {
- Stream inputStream = new BufferedStream(contentStream);
- if(decompress_here)
- {
- string[] encodings = httpConnection.ContentEncoding?.Split(',');
- if(encodings != null)
- {
- if(encodings.Contains(GZIP_ENCODING, StringComparer.OrdinalIgnoreCase))
- inputStream = new GZipStream(inputStream, CompressionMode.Decompress);
- else if(encodings.Contains(DEFLATE_ENCODING, StringComparer.OrdinalIgnoreCase))
- inputStream = new DeflateStream(inputStream, CompressionMode.Decompress);
- }
- }
- return new StreamContent(inputStream);
- }
-
- bool HandleRedirect(HttpStatusCode redirectCode, HttpURLConnection httpConnection, RequestRedirectionState redirectState, out bool disposeRet)
- {
- if(!AllowAutoRedirect)
- {
- disposeRet = false;
- return true; // We shouldn't follow and there's no data to fetch, just return
- }
- disposeRet = true;
-
- redirectState.NewUrl = null;
- switch(redirectCode)
- {
- case HttpStatusCode.MultipleChoices: // 300
- break;
-
- case HttpStatusCode.Moved: // 301
- case HttpStatusCode.Redirect: // 302
- case HttpStatusCode.SeeOther: // 303
- redirectState.Method = HttpMethod.Get;
- break;
-
- case HttpStatusCode.TemporaryRedirect: // 307
- break;
-
- default:
- if((int)redirectCode >= 300 && (int)redirectCode < 400)
- throw new InvalidOperationException($"HTTP Redirection status code {redirectCode} ({(int)redirectCode}) not supported");
- return false;
- }
-
- IDictionary> headers = httpConnection.HeaderFields;
- IList locationHeader;
- if(!headers.TryGetValue("Location", out locationHeader) || locationHeader == null || locationHeader.Count == 0)
- throw new InvalidOperationException($"HTTP connection redirected with code {redirectCode} ({(int)redirectCode}) but no Location header found in response");
-
-
- redirectState.RedirectCounter++;
- if(redirectState.RedirectCounter >= MaxAutomaticRedirections)
- throw new WebException($"Maximum automatic redirections exceeded (allowed {MaxAutomaticRedirections}, redirected {redirectState.RedirectCounter} times)");
-
- Uri location = new Uri(locationHeader[0], UriKind.Absolute);
- redirectState.NewUrl = location;
-
- return true;
- }
-
- bool IsErrorStatusCode(HttpStatusCode statusCode)
- {
- return (int)statusCode >= 400 && (int)statusCode <= 599;
- }
-
- void CollectAuthInfo(HttpHeaderValueCollection headers)
- {
- var authData = new List(headers.Count);
-
- foreach(AuthenticationHeaderValue ahv in headers)
- {
- var data = new AuthenticationData
- {
- Scheme = GetAuthScheme(ahv.Scheme),
- // COMMENTED OUT: Kyle
- //Challenge = $"{ahv.Scheme} {ahv.Parameter}",
- UseProxyAuthentication = ProxyAuthenticationRequested
- };
- authData.Add(data);
- }
-
- RequestedAuthentication = authData.AsReadOnly();
- }
-
- AuthenticationScheme GetAuthScheme(string scheme)
- {
- if(String.Compare("basic", scheme, StringComparison.OrdinalIgnoreCase) == 0)
- return AuthenticationScheme.Basic;
- if(String.Compare("digest", scheme, StringComparison.OrdinalIgnoreCase) == 0)
- return AuthenticationScheme.Digest;
-
- return AuthenticationScheme.Unsupported;
- }
-
- void CopyHeaders(HttpURLConnection httpConnection, HttpResponseMessage response)
- {
- IDictionary> headers = httpConnection.HeaderFields;
- foreach(string key in headers.Keys)
- {
- if(key == null) // First header entry has null key, it corresponds to the response message
- continue;
-
- HttpHeaders item_headers;
- string kind;
- if(known_content_headers.Contains(key))
- {
- kind = "content";
- item_headers = response.Content.Headers;
- }
- else
- {
- kind = "response";
- item_headers = response.Headers;
- }
- item_headers.TryAddWithoutValidation(key, headers[key]);
- }
- }
-
- ///
- /// Configure the before the request is sent. This method is meant to be overriden
- /// by applications which need to perform some extra configuration steps on the connection. It is called with all
- /// the request headers set, pre-authentication performed (if applicable) but before the request body is set
- /// (e.g. for POST requests). The default implementation in AndroidClientHandler does nothing.
- ///
- /// Request data
- /// Pre-configured connection instance
- protected virtual Task SetupRequest(HttpRequestMessage request, HttpURLConnection conn)
- {
- return Task.Factory.StartNew(AssertSelf);
- }
-
- ///
- /// Configures the key store. The parameter is set to instance of
- /// created using the type and with populated with certificates provided in the
- /// property. AndroidClientHandler implementation simply returns the instance passed in the parameter
- ///
- /// The key store.
- /// Key store to configure.
- protected virtual KeyStore ConfigureKeyStore(KeyStore keyStore)
- {
- AssertSelf();
-
- return keyStore;
- }
-
- ///
- /// Create and configure an instance of . The parameter is set to the
- /// return value of the method, so it might be null if the application overrode the method and provided
- /// no key store. It will not be null when the default implementation is used. The application can return null here since
- /// KeyManagerFactory is not required for the custom SSL configuration, but it might be used by the application to implement a more advanced
- /// mechanism of key management.
- ///
- /// The key manager factory or null.
- /// Key store.
- protected virtual KeyManagerFactory ConfigureKeyManagerFactory(KeyStore keyStore)
- {
- AssertSelf();
-
- return null;
- }
-
- ///
- /// Create and configure an instance of . The parameter is set to the
- /// return value of the method, so it might be null if the application overrode the method and provided
- /// no key store. It will not be null when the default implementation is used. The application can return null from this
- /// method in which case AndroidClientHandler will create its own instance of the trust manager factory provided that the
- /// list contains at least one valid certificate. If there are no valid certificates and this method returns null, no custom
- /// trust manager will be created since that would make all the HTTPS requests fail.
- ///
- /// The trust manager factory.
- /// Key store.
- protected virtual TrustManagerFactory ConfigureTrustManagerFactory(KeyStore keyStore)
- {
- AssertSelf();
-
- return null;
- }
-
- void AppendEncoding(string encoding, ref List list)
- {
- if(list == null)
- list = new List();
- if(list.Contains(encoding))
- return;
- list.Add(encoding);
- }
-
- async Task SetupRequestInternal(HttpRequestMessage request, URLConnection conn)
- {
- if(conn == null)
- throw new ArgumentNullException(nameof(conn));
- var httpConnection = conn.JavaCast();
- if(httpConnection == null)
- throw new InvalidOperationException($"Unsupported URL scheme {conn.URL.Protocol}");
-
- httpConnection.RequestMethod = request.Method.ToString();
-
- // SSL context must be set up as soon as possible, before adding any content or
- // headers. Otherwise Java won't use the socket factory
- SetupSSL(httpConnection as HttpsURLConnection);
- if(request.Content != null)
- AddHeaders(httpConnection, request.Content.Headers);
- AddHeaders(httpConnection, request.Headers);
-
- List accept_encoding = null;
-
- decompress_here = false;
- if((AutomaticDecompression & DecompressionMethods.GZip) != 0)
- {
- AppendEncoding(GZIP_ENCODING, ref accept_encoding);
- decompress_here = true;
- }
-
- if((AutomaticDecompression & DecompressionMethods.Deflate) != 0)
- {
- AppendEncoding(DEFLATE_ENCODING, ref accept_encoding);
- decompress_here = true;
- }
-
- if(AutomaticDecompression == DecompressionMethods.None)
- {
- accept_encoding?.Clear();
- AppendEncoding(IDENTITY_ENCODING, ref accept_encoding); // Turns off compression for the Java client
- }
-
- if(accept_encoding?.Count > 0)
- httpConnection.SetRequestProperty("Accept-Encoding", String.Join(",", accept_encoding));
-
- if(UseCookies && CookieContainer != null)
- {
- string cookieHeaderValue = CookieContainer.GetCookieHeader(request.RequestUri);
- if(!String.IsNullOrEmpty(cookieHeaderValue))
- httpConnection.SetRequestProperty("Cookie", cookieHeaderValue);
- }
-
- HandlePreAuthentication(httpConnection);
- await SetupRequest(request, httpConnection);
- SetupRequestBody(httpConnection, request);
-
- return httpConnection;
- }
-
- void SetupSSL(HttpsURLConnection httpsConnection)
- {
- if(httpsConnection == null)
- return;
-
- KeyStore keyStore = KeyStore.GetInstance(KeyStore.DefaultType);
- keyStore.Load(null, null);
- bool gotCerts = TrustedCerts?.Count > 0;
- if(gotCerts)
- {
- for(int i = 0; i < TrustedCerts.Count; i++)
- {
- Certificate cert = TrustedCerts[i];
- if(cert == null)
- continue;
- keyStore.SetCertificateEntry($"ca{i}", cert);
- }
- }
- keyStore = ConfigureKeyStore(keyStore);
- KeyManagerFactory kmf = ConfigureKeyManagerFactory(keyStore);
- TrustManagerFactory tmf = ConfigureTrustManagerFactory(keyStore);
-
- if(tmf == null)
- {
- // If there are no certs and no trust manager factory, we can't use a custom manager
- // because it will cause all the HTTPS requests to fail because of unverified trust
- // chain
- if(!gotCerts)
- return;
-
- tmf = TrustManagerFactory.GetInstance(TrustManagerFactory.DefaultAlgorithm);
- tmf.Init(keyStore);
- }
-
- SSLContext context = SSLContext.GetInstance("TLS");
- context.Init(kmf?.GetKeyManagers(), tmf.GetTrustManagers(), null);
- httpsConnection.SSLSocketFactory = context.SocketFactory;
- }
-
- void HandlePreAuthentication(HttpURLConnection httpConnection)
- {
- AuthenticationData data = PreAuthenticationData;
- if(!PreAuthenticate || data == null)
- return;
-
- ICredentials creds = data.UseProxyAuthentication ? Proxy?.Credentials : Credentials;
- if(creds == null)
- {
- return;
- }
-
- IAndroidAuthenticationModule auth = data.Scheme == AuthenticationScheme.Unsupported ? data.AuthModule : authModules.Find(m => m?.Scheme == data.Scheme);
- if(auth == null)
- {
- return;
- }
-
- Authorization authorization = auth.Authenticate(data.Challenge, httpConnection, creds);
- if(authorization == null)
- {
- return;
- }
-
- httpConnection.SetRequestProperty(data.UseProxyAuthentication ? "Proxy-Authorization" : "Authorization", authorization.Message);
- }
-
- void AddHeaders(HttpURLConnection conn, HttpHeaders headers)
- {
- if(headers == null)
- return;
-
- foreach(KeyValuePair> header in headers)
- {
- conn.SetRequestProperty(header.Key, header.Value != null ? String.Join(",", header.Value) : String.Empty);
- }
- }
-
- void SetupRequestBody(HttpURLConnection httpConnection, HttpRequestMessage request)
- {
- if(request.Content == null)
- {
- // Pilfered from System.Net.Http.HttpClientHandler:SendAync
- if(HttpMethod.Post.Equals(request.Method) || HttpMethod.Put.Equals(request.Method) || HttpMethod.Delete.Equals(request.Method))
- {
- // Explicitly set this to make sure we're sending a "Content-Length: 0" header.
- // This fixes the issue that's been reported on the forums:
- // http://forums.xamarin.com/discussion/17770/length-required-error-in-http-post-since-latest-release
- httpConnection.SetRequestProperty("Content-Length", "0");
- }
- return;
- }
-
- httpConnection.DoOutput = true;
- long? contentLength = request.Content.Headers.ContentLength;
- if(contentLength != null)
- httpConnection.SetFixedLengthStreamingMode((int)contentLength);
- else
- httpConnection.SetChunkedStreamingMode(0);
- }
- }
-
- sealed class AuthModuleBasic : IAndroidAuthenticationModule
- {
- public AuthenticationScheme Scheme { get; } = AuthenticationScheme.Basic;
- public string AuthenticationType { get; } = "Basic";
- public bool CanPreAuthenticate { get; } = true;
-
- public Authorization Authenticate(string challenge, HttpURLConnection request, ICredentials credentials)
- {
- string header = challenge?.Trim();
- if(credentials == null || String.IsNullOrEmpty(header))
- return null;
-
- if(header.IndexOf("basic", StringComparison.OrdinalIgnoreCase) == -1)
- return null;
-
- return InternalAuthenticate(request, credentials);
- }
-
- public Authorization PreAuthenticate(HttpURLConnection request, ICredentials credentials)
- {
- return InternalAuthenticate(request, credentials);
- }
-
- Authorization InternalAuthenticate(HttpURLConnection request, ICredentials credentials)
- {
- if(request == null || credentials == null)
- return null;
-
- NetworkCredential cred = credentials.GetCredential(new Uri(request.URL.ToString()), AuthenticationType.ToLowerInvariant());
- if(cred == null)
- return null;
-
- if(String.IsNullOrEmpty(cred.UserName))
- return null;
-
- string domain = cred.Domain?.Trim();
- string response = String.Empty;
-
- // If domain is set, MS sends "domain\user:password".
- if(!String.IsNullOrEmpty(domain))
- response = domain + "\\";
- response += cred.UserName + ":" + cred.Password;
-
- return new Authorization($"{AuthenticationType} {Convert.ToBase64String(Encoding.ASCII.GetBytes(response))}");
- }
- }
-}
\ No newline at end of file
diff --git a/src/Android/Services/HttpService.cs b/src/Android/Services/HttpService.cs
index 8879b70b4..a9c132ec6 100644
--- a/src/Android/Services/HttpService.cs
+++ b/src/Android/Services/HttpService.cs
@@ -7,6 +7,6 @@ namespace Bit.Android.Services
{
public class HttpService : IHttpService
{
- public ApiHttpClient Client => new ApiHttpClient(new CustomAndroidClientHandler());
+ public ApiHttpClient Client => new ApiHttpClient(new AndroidClientHandler());
}
}