@@ -202,7 +202,7 @@ public int MaxAutomaticRedirections
202
202
/// </summary>
203
203
/// <value>The pre authentication data.</value>
204
204
public AuthenticationData ? PreAuthenticationData { get ; set ; }
205
-
205
+
206
206
/// <summary>
207
207
/// If the website requires authentication, this property will contain data about each scheme supported
208
208
/// by the server after the response. Note that unauthorized request will return a valid response - you
@@ -234,12 +234,12 @@ public bool RequestNeedsAuthorization {
234
234
/// <summary>
235
235
/// <para>
236
236
/// 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
238
238
/// server's certificate or, alternatively, provides the server public key. Whichever the case, the certificate(s) must be stored
239
239
/// 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.
241
241
/// 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
243
243
/// <see cref="ConfigureTrustManagerFactory"/>, <see cref="ConfigureKeyManagerFactory"/> and <see cref="ConfigureKeyStore"/> methods
244
244
/// instead</para>
245
245
/// </summary>
@@ -264,6 +264,16 @@ public bool RequestNeedsAuthorization {
264
264
/// </summary>
265
265
public TimeSpan ReadTimeout { get ; set ; } = TimeSpan . FromHours ( 24 ) ;
266
266
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
+
267
277
/// <summary>
268
278
/// <para>
269
279
/// Specifies the connect timeout
@@ -331,12 +341,38 @@ string EncodeUrl (Uri url)
331
341
/// <returns>Task in which the request is executed</returns>
332
342
/// <param name="request">Request provided by <see cref="System.Net.Http.HttpClient"/></param>
333
343
/// <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 )
335
371
{
336
372
AssertSelf ( ) ;
337
373
if ( request == null )
338
374
throw new ArgumentNullException ( nameof ( request ) ) ;
339
-
375
+
340
376
if ( ! request . RequestUri . IsAbsoluteUri )
341
377
throw new ArgumentException ( "Must represent an absolute URI" , "request" ) ;
342
378
@@ -633,7 +669,7 @@ internal Task WriteRequestContentToOutputInternal (HttpRequestMessage request, H
633
669
return ret ;
634
670
}
635
671
636
- HttpContent GetErrorContent ( HttpURLConnection httpConnection , HttpContent fallbackContent )
672
+ HttpContent GetErrorContent ( HttpURLConnection httpConnection , HttpContent fallbackContent )
637
673
{
638
674
var contentStream = httpConnection . ErrorStream ;
639
675
@@ -796,7 +832,7 @@ void CollectAuthInfo (HttpHeaderValueCollection <AuthenticationHeaderValue> head
796
832
797
833
RequestedAuthentication = authData . AsReadOnly ( ) ;
798
834
}
799
-
835
+
800
836
AuthenticationScheme GetAuthScheme ( string scheme )
801
837
{
802
838
if ( String . Compare ( "basic" , scheme , StringComparison . OrdinalIgnoreCase ) == 0 )
@@ -851,15 +887,15 @@ void CopyHeaders (HttpURLConnection httpConnection, HttpResponseMessage response
851
887
/// <summary>
852
888
/// Configure the <see cref="HttpURLConnection"/> before the request is sent. This method is meant to be overriden
853
889
/// 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
855
891
/// (e.g. for POST requests). The default implementation in AndroidMessageHandler does nothing.
856
892
/// </summary>
857
893
/// <param name="request">Request data</param>
858
894
/// <param name="conn">Pre-configured connection instance</param>
859
895
protected virtual Task SetupRequest ( HttpRequestMessage request , HttpURLConnection conn )
860
896
{
861
897
AssertSelf ( ) ;
862
-
898
+
863
899
return Task . CompletedTask ;
864
900
}
865
901
@@ -905,9 +941,9 @@ internal Task SetupRequestInternal (HttpRequestMessage request, HttpURLConnectio
905
941
/// <summary>
906
942
/// Create and configure an instance of <see cref="TrustManagerFactory"/>. The <paramref name="keyStore"/> parameter is set to the
907
943
/// 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
909
945
/// 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
911
947
/// trust manager will be created since that would make all the HTTPS requests fail.
912
948
/// </summary>
913
949
/// <returns>The trust manager factory.</returns>
@@ -930,7 +966,7 @@ void AppendEncoding (string encoding, ref List <string>? list)
930
966
return ;
931
967
list . Add ( encoding ) ;
932
968
}
933
-
969
+
934
970
async Task < HttpURLConnection > SetupRequestInternal ( HttpRequestMessage request , URLConnection conn )
935
971
{
936
972
if ( conn == null )
@@ -951,15 +987,15 @@ void AppendEncoding (string encoding, ref List <string>? list)
951
987
if ( request . Content != null )
952
988
AddHeaders ( httpConnection , request . Content . Headers ) ;
953
989
AddHeaders ( httpConnection , request . Headers ) ;
954
-
990
+
955
991
List < string > ? accept_encoding = null ;
956
992
957
993
decompress_here = false ;
958
994
if ( ( AutomaticDecompression & DecompressionMethods . GZip ) != 0 ) {
959
995
AppendEncoding ( GZIP_ENCODING , ref accept_encoding ) ;
960
996
decompress_here = true ;
961
997
}
962
-
998
+
963
999
if ( ( AutomaticDecompression & DecompressionMethods . Deflate ) != 0 ) {
964
1000
AppendEncoding ( DEFLATE_ENCODING , ref accept_encoding ) ;
965
1001
decompress_here = true ;
@@ -978,7 +1014,7 @@ void AppendEncoding (string encoding, ref List <string>? list)
978
1014
if ( ! String . IsNullOrEmpty ( cookieHeaderValue ) )
979
1015
httpConnection . SetRequestProperty ( "Cookie" , cookieHeaderValue ) ;
980
1016
}
981
-
1017
+
982
1018
HandlePreAuthentication ( httpConnection ) ;
983
1019
await SetupRequest ( request , httpConnection ) . ConfigureAwait ( continueOnCapturedContext : false ) ; ;
984
1020
SetupRequestBody ( httpConnection , request ) ;
@@ -1035,7 +1071,7 @@ void SetupSSL (HttpsURLConnection? httpsConnection, HttpRequestMessage requestMe
1035
1071
// there is no point in changing the behavior of the default SSL socket factory
1036
1072
if ( ! gotCerts && _callbackTrustManagerHelper == null )
1037
1073
return ;
1038
-
1074
+
1039
1075
tmf = TrustManagerFactory . GetInstance ( TrustManagerFactory . DefaultAlgorithm ) ;
1040
1076
tmf ? . Init ( gotCerts ? keyStore : null ) ; // only use the custom key store if the user defined any trusted certs
1041
1077
}
@@ -1068,7 +1104,7 @@ void SetupSSL (HttpsURLConnection? httpsConnection, HttpRequestMessage requestMe
1068
1104
return keyStore ;
1069
1105
}
1070
1106
}
1071
-
1107
+
1072
1108
void HandlePreAuthentication ( HttpURLConnection httpConnection )
1073
1109
{
1074
1110
var data = PreAuthenticationData ;
@@ -1114,7 +1150,7 @@ void AddHeaders (HttpURLConnection conn, HttpHeaders headers)
1114
1150
conn . SetRequestProperty ( header . Key , header . Value != null ? String . Join ( GetHeaderSeparator ( header . Key ) , header . Value ) : String . Empty ) ;
1115
1151
}
1116
1152
}
1117
-
1153
+
1118
1154
void SetupRequestBody ( HttpURLConnection httpConnection , HttpRequestMessage request )
1119
1155
{
1120
1156
if ( request . Content == null ) {
0 commit comments