@@ -202,7 +202,7 @@ public int MaxAutomaticRedirections
202202 /// </summary>
203203 /// <value>The pre authentication data.</value>
204204 public AuthenticationData ? PreAuthenticationData { get ; set ; }
205-
205+
206206 /// <summary>
207207 /// If the website requires authentication, this property will contain data about each scheme supported
208208 /// by the server after the response. Note that unauthorized request will return a valid response - you
@@ -234,12 +234,12 @@ public bool RequestNeedsAuthorization {
234234 /// <summary>
235235 /// <para>
236236 /// If the request is to the server protected with a self-signed (or otherwise untrusted) SSL certificate, the request will
237- /// fail security chain verification unless the application provides either the CA certificate of the entity which issued the
237+ /// fail security chain verification unless the application provides either the CA certificate of the entity which issued the
238238 /// server's certificate or, alternatively, provides the server public key. Whichever the case, the certificate(s) must be stored
239239 /// in this property in order for AndroidMessageHandler to configure the request to accept the server certificate.</para>
240- /// <para>AndroidMessageHandler uses a custom <see cref="KeyStore"/> and <see cref="TrustManagerFactory"/> to configure the connection.
240+ /// <para>AndroidMessageHandler uses a custom <see cref="KeyStore"/> and <see cref="TrustManagerFactory"/> to configure the connection.
241241 /// If, however, the application requires finer control over the SSL configuration (e.g. it implements its own TrustManager) then
242- /// it should leave this property empty and instead derive a custom class from AndroidMessageHandler and override, as needed, the
242+ /// it should leave this property empty and instead derive a custom class from AndroidMessageHandler and override, as needed, the
243243 /// <see cref="ConfigureTrustManagerFactory"/>, <see cref="ConfigureKeyManagerFactory"/> and <see cref="ConfigureKeyStore"/> methods
244244 /// instead</para>
245245 /// </summary>
@@ -264,6 +264,16 @@ public bool RequestNeedsAuthorization {
264264 /// </summary>
265265 public TimeSpan ReadTimeout { get ; set ; } = TimeSpan . FromHours ( 24 ) ;
266266
267+ #if ! MONOANDROID1_0
268+ /// <summary>
269+ /// A feature switch that determines whether the message handler should attempt to authenticate the user
270+ /// using the NTLM/Negotiate authentication method. Enable the feature by adding
271+ /// <c><AndroidUseNegotiateAuthentication>true</AndroidUseNegotiateAuthentication></c> to your project file.
272+ /// </summary>
273+ static bool NegotiateAuthenticationIsEnabled =>
274+ AppContext . TryGetSwitch ( "Xamarin.Android.Net.UseNegotiateAuthentication" , out bool isEnabled ) && isEnabled ;
275+ #endif
276+
267277 /// <summary>
268278 /// <para>
269279 /// Specifies the connect timeout
@@ -331,12 +341,38 @@ string EncodeUrl (Uri url)
331341 /// <returns>Task in which the request is executed</returns>
332342 /// <param name="request">Request provided by <see cref="System.Net.Http.HttpClient"/></param>
333343 /// <param name="cancellationToken">Cancellation token.</param>
334- protected override async Task < HttpResponseMessage > SendAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
344+ protected override Task < HttpResponseMessage > SendAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
345+ {
346+ #if ! MONOANDROID1_0
347+ if ( NegotiateAuthenticationIsEnabled ) {
348+ return SendWithNegotiateAuthenticationAsync ( request , cancellationToken ) ;
349+ }
350+ #endif
351+
352+ return DoSendAsync ( request , cancellationToken ) ;
353+ }
354+
355+ #if ! MONOANDROID1_0
356+ async Task < HttpResponseMessage ? > SendWithNegotiateAuthenticationAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
357+ {
358+ var response = await DoSendAsync ( request , cancellationToken ) . ConfigureAwait ( false ) ;
359+
360+ if ( RequestNeedsAuthorization && NegotiateAuthenticationHelper . RequestNeedsNegotiateAuthentication ( this , request , out var requestedAuth ) ) {
361+ var authenticatedResponse = await NegotiateAuthenticationHelper . SendWithAuthAsync ( this , request , requestedAuth , cancellationToken ) . ConfigureAwait ( false ) ;
362+ if ( authenticatedResponse != null )
363+ return authenticatedResponse ;
364+ }
365+
366+ return response ;
367+ }
368+ #endif
369+
370+ internal async Task < HttpResponseMessage > DoSendAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
335371 {
336372 AssertSelf ( ) ;
337373 if ( request == null )
338374 throw new ArgumentNullException ( nameof ( request ) ) ;
339-
375+
340376 if ( ! request . RequestUri . IsAbsoluteUri )
341377 throw new ArgumentException ( "Must represent an absolute URI" , "request" ) ;
342378
@@ -633,7 +669,7 @@ internal Task WriteRequestContentToOutputInternal (HttpRequestMessage request, H
633669 return ret ;
634670 }
635671
636- HttpContent GetErrorContent ( HttpURLConnection httpConnection , HttpContent fallbackContent )
672+ HttpContent GetErrorContent ( HttpURLConnection httpConnection , HttpContent fallbackContent )
637673 {
638674 var contentStream = httpConnection . ErrorStream ;
639675
@@ -796,7 +832,7 @@ void CollectAuthInfo (HttpHeaderValueCollection <AuthenticationHeaderValue> head
796832
797833 RequestedAuthentication = authData . AsReadOnly ( ) ;
798834 }
799-
835+
800836 AuthenticationScheme GetAuthScheme ( string scheme )
801837 {
802838 if ( String . Compare ( "basic" , scheme , StringComparison . OrdinalIgnoreCase ) == 0 )
@@ -851,15 +887,15 @@ void CopyHeaders (HttpURLConnection httpConnection, HttpResponseMessage response
851887 /// <summary>
852888 /// Configure the <see cref="HttpURLConnection"/> before the request is sent. This method is meant to be overriden
853889 /// by applications which need to perform some extra configuration steps on the connection. It is called with all
854- /// the request headers set, pre-authentication performed (if applicable) but before the request body is set
890+ /// the request headers set, pre-authentication performed (if applicable) but before the request body is set
855891 /// (e.g. for POST requests). The default implementation in AndroidMessageHandler does nothing.
856892 /// </summary>
857893 /// <param name="request">Request data</param>
858894 /// <param name="conn">Pre-configured connection instance</param>
859895 protected virtual Task SetupRequest ( HttpRequestMessage request , HttpURLConnection conn )
860896 {
861897 AssertSelf ( ) ;
862-
898+
863899 return Task . CompletedTask ;
864900 }
865901
@@ -905,9 +941,9 @@ internal Task SetupRequestInternal (HttpRequestMessage request, HttpURLConnectio
905941 /// <summary>
906942 /// Create and configure an instance of <see cref="TrustManagerFactory"/>. The <paramref name="keyStore"/> parameter is set to the
907943 /// return value of the <see cref="ConfigureKeyStore"/> method, so it might be null if the application overrode the method and provided
908- /// no key store. It will not be <c>null</c> when the default implementation is used. The application can return <c>null</c> from this
944+ /// no key store. It will not be <c>null</c> when the default implementation is used. The application can return <c>null</c> from this
909945 /// method in which case AndroidMessageHandler will create its own instance of the trust manager factory provided that the <see cref="TrustCerts"/>
910- /// list contains at least one valid certificate. If there are no valid certificates and this method returns <c>null</c>, no custom
946+ /// list contains at least one valid certificate. If there are no valid certificates and this method returns <c>null</c>, no custom
911947 /// trust manager will be created since that would make all the HTTPS requests fail.
912948 /// </summary>
913949 /// <returns>The trust manager factory.</returns>
@@ -930,7 +966,7 @@ void AppendEncoding (string encoding, ref List <string>? list)
930966 return ;
931967 list . Add ( encoding ) ;
932968 }
933-
969+
934970 async Task < HttpURLConnection > SetupRequestInternal ( HttpRequestMessage request , URLConnection conn )
935971 {
936972 if ( conn == null )
@@ -951,15 +987,15 @@ void AppendEncoding (string encoding, ref List <string>? list)
951987 if ( request . Content != null )
952988 AddHeaders ( httpConnection , request . Content . Headers ) ;
953989 AddHeaders ( httpConnection , request . Headers ) ;
954-
990+
955991 List < string > ? accept_encoding = null ;
956992
957993 decompress_here = false ;
958994 if ( ( AutomaticDecompression & DecompressionMethods . GZip ) != 0 ) {
959995 AppendEncoding ( GZIP_ENCODING , ref accept_encoding ) ;
960996 decompress_here = true ;
961997 }
962-
998+
963999 if ( ( AutomaticDecompression & DecompressionMethods . Deflate ) != 0 ) {
9641000 AppendEncoding ( DEFLATE_ENCODING , ref accept_encoding ) ;
9651001 decompress_here = true ;
@@ -978,7 +1014,7 @@ void AppendEncoding (string encoding, ref List <string>? list)
9781014 if ( ! String . IsNullOrEmpty ( cookieHeaderValue ) )
9791015 httpConnection . SetRequestProperty ( "Cookie" , cookieHeaderValue ) ;
9801016 }
981-
1017+
9821018 HandlePreAuthentication ( httpConnection ) ;
9831019 await SetupRequest ( request , httpConnection ) . ConfigureAwait ( continueOnCapturedContext : false ) ; ;
9841020 SetupRequestBody ( httpConnection , request ) ;
@@ -1035,7 +1071,7 @@ void SetupSSL (HttpsURLConnection? httpsConnection, HttpRequestMessage requestMe
10351071 // there is no point in changing the behavior of the default SSL socket factory
10361072 if ( ! gotCerts && _callbackTrustManagerHelper == null )
10371073 return ;
1038-
1074+
10391075 tmf = TrustManagerFactory . GetInstance ( TrustManagerFactory . DefaultAlgorithm ) ;
10401076 tmf ? . Init ( gotCerts ? keyStore : null ) ; // only use the custom key store if the user defined any trusted certs
10411077 }
@@ -1068,7 +1104,7 @@ void SetupSSL (HttpsURLConnection? httpsConnection, HttpRequestMessage requestMe
10681104 return keyStore ;
10691105 }
10701106 }
1071-
1107+
10721108 void HandlePreAuthentication ( HttpURLConnection httpConnection )
10731109 {
10741110 var data = PreAuthenticationData ;
@@ -1114,7 +1150,7 @@ void AddHeaders (HttpURLConnection conn, HttpHeaders headers)
11141150 conn . SetRequestProperty ( header . Key , header . Value != null ? String . Join ( GetHeaderSeparator ( header . Key ) , header . Value ) : String . Empty ) ;
11151151 }
11161152 }
1117-
1153+
11181154 void SetupRequestBody ( HttpURLConnection httpConnection , HttpRequestMessage request )
11191155 {
11201156 if ( request . Content == null ) {
0 commit comments