19
19
using CURLcode = Interop . libcurl . CURLcode ;
20
20
using CURLMcode = Interop . libcurl . CURLMcode ;
21
21
using CURLINFO = Interop . libcurl . CURLINFO ;
22
+ using CURLAUTH = Interop . libcurl . CURLAUTH ;
22
23
using CurlVersionInfoData = Interop . libcurl . curl_version_info_data ;
23
24
using CurlFeatures = Interop . libcurl . CURL_VERSION_Features ;
24
25
using CURLProxyType = Interop . libcurl . curl_proxytype ;
@@ -36,6 +37,7 @@ internal partial class CurlHandler : HttpMessageHandler
36
37
private const string EncodingNameGzip = "gzip" ;
37
38
private const string EncodingNameDeflate = "deflate" ;
38
39
private readonly static string [ ] AuthenticationSchemes = { "Negotiate" , "Digest" , "Basic" } ; // the order in which libcurl goes over authentication schemes
40
+ private readonly static ulong [ ] AuthSchemePriorityOrder = { CURLAUTH . Negotiate , CURLAUTH . Digest , CURLAUTH . Basic } ;
39
41
private static readonly string [ ] s_headerDelimiters = new string [ ] { "\r \n " } ;
40
42
41
43
private const int s_requestBufferSize = 16384 ; // Default used by libcurl
@@ -58,6 +60,8 @@ internal partial class CurlHandler : HttpMessageHandler
58
60
private DecompressionMethods _automaticDecompression = DecompressionMethods . GZip | DecompressionMethods . Deflate ;
59
61
private SafeCurlMultiHandle _multiHandle ;
60
62
private GCHandle _multiHandlePtr = new GCHandle ( ) ;
63
+ private bool _preAuthenticate = false ;
64
+ private CredentialCache _credentialCache = null ;
61
65
private CookieContainer _cookieContainer = null ;
62
66
private bool _useCookie = false ;
63
67
private bool _automaticRedirection = true ;
@@ -159,7 +163,6 @@ internal ICredentials Credentials
159
163
160
164
set
161
165
{
162
- CheckDisposedOrStarted ( ) ;
163
166
_serverCredentials = value ;
164
167
}
165
168
}
@@ -202,6 +205,23 @@ internal DecompressionMethods AutomaticDecompression
202
205
}
203
206
}
204
207
208
+ internal bool PreAuthenticate
209
+ {
210
+ get
211
+ {
212
+ return _preAuthenticate ;
213
+ }
214
+ set
215
+ {
216
+ CheckDisposedOrStarted ( ) ;
217
+ _preAuthenticate = value ;
218
+ if ( value )
219
+ {
220
+ _credentialCache = new CredentialCache ( ) ;
221
+ }
222
+ }
223
+ }
224
+
205
225
internal bool UseCookie
206
226
{
207
227
get
@@ -306,14 +326,13 @@ protected internal override Task<HttpResponseMessage> SendAsync(
306
326
}
307
327
308
328
// Create RequestCompletionSource object and save current values of handler settings.
309
- RequestCompletionSource state = new RequestCompletionSource
329
+ RequestCompletionSource state = new RequestCompletionSource ( this )
310
330
{
311
331
CancellationToken = cancellationToken ,
312
332
RequestMessage = request ,
313
333
} ;
314
334
315
335
BeginRequest ( state ) ;
316
-
317
336
return state . Task ;
318
337
}
319
338
@@ -398,6 +417,17 @@ private static void EndRequest(SafeCurlMultiHandle multiHandle, IntPtr statePtr,
398
417
state . TrySetException ( new HttpRequestException ( SR . net_http_client_execution_error ,
399
418
GetCurlException ( result ) ) ) ;
400
419
}
420
+
421
+ if ( state . ResponseMessage . StatusCode != HttpStatusCode . Unauthorized && state . Handler . PreAuthenticate )
422
+ {
423
+ ulong availedAuth ;
424
+ if ( Interop . libcurl . curl_easy_getinfo ( state . RequestHandle , CURLINFO . CURLINFO_HTTPAUTH_AVAIL , out availedAuth ) == CURLcode . CURLE_OK )
425
+ {
426
+ state . Handler . AddCredentialToCache ( state . RequestMessage . RequestUri , availedAuth , state . NetworkCredential ) ;
427
+ }
428
+ // ignoring the exception in this case.
429
+ // There is no point in killing the request for the sake of putting the credentials into the cache
430
+ }
401
431
}
402
432
catch ( Exception ex )
403
433
{
@@ -455,6 +485,8 @@ private SafeCurlHandle CreateRequestHandle(RequestCompletionSource state, GCHand
455
485
456
486
SetProxyOptions ( requestHandle , state . RequestMessage . RequestUri ) ;
457
487
488
+ SetRequestHandleCredentialsOptions ( requestHandle , state ) ;
489
+
458
490
SetCookieOption ( requestHandle , state . RequestMessage . RequestUri ) ;
459
491
460
492
state . RequestHeaderHandle = SetRequestHeaders ( requestHandle , state . RequestMessage ) ;
@@ -528,6 +560,44 @@ private void SetProxyOptions(SafeCurlHandle requestHandle, Uri requestUri)
528
560
}
529
561
}
530
562
563
+ private void SetRequestHandleCredentialsOptions ( SafeCurlHandle requestHandle , RequestCompletionSource state )
564
+ {
565
+ NetworkCredential credentials = GetNetworkCredentials ( state . Handler . _serverCredentials , state . RequestMessage . RequestUri ) ;
566
+ if ( credentials != null )
567
+ {
568
+ string userName = string . IsNullOrEmpty ( credentials . Domain ) ?
569
+ credentials . UserName :
570
+ string . Format ( "{0}\\ {1}" , credentials . Domain , credentials . UserName ) ;
571
+
572
+ SetCurlOption ( requestHandle , CURLoption . CURLOPT_USERNAME , userName ) ;
573
+ SetCurlOption ( requestHandle , CURLoption . CURLOPT_HTTPAUTH , CURLAUTH . AuthAny ) ;
574
+ if ( credentials . Password != null )
575
+ {
576
+ SetCurlOption ( requestHandle , CURLoption . CURLOPT_PASSWORD , credentials . Password ) ;
577
+ }
578
+
579
+ state . NetworkCredential = credentials ;
580
+ }
581
+ }
582
+
583
+ private NetworkCredential GetNetworkCredentials ( ICredentials credentials , Uri requestUri )
584
+ {
585
+ if ( _preAuthenticate )
586
+ {
587
+ NetworkCredential nc = null ;
588
+ lock ( _multiHandle )
589
+ {
590
+ nc = GetCredentials ( _credentialCache , requestUri ) ;
591
+ }
592
+ if ( nc != null )
593
+ {
594
+ return nc ;
595
+ }
596
+ }
597
+
598
+ return GetCredentials ( credentials , requestUri ) ;
599
+ }
600
+
531
601
private void SetCookieOption ( SafeCurlHandle requestHandle , Uri requestUri )
532
602
{
533
603
if ( ! _useCookie )
@@ -547,22 +617,35 @@ private void SetCookieOption(SafeCurlHandle requestHandle, Uri requestUri)
547
617
}
548
618
}
549
619
550
- private NetworkCredential GetCredentials ( ICredentials proxyCredentials , Uri requestUri )
620
+ private void AddCredentialToCache ( Uri serverUri , ulong authAvail , NetworkCredential nc )
621
+ {
622
+ lock ( _multiHandle )
623
+ {
624
+ for ( int i = 0 ; i < AuthSchemePriorityOrder . Length ; i ++ )
625
+ {
626
+ if ( ( authAvail & AuthSchemePriorityOrder [ i ] ) != 0 )
627
+ {
628
+ _credentialCache . Add ( serverUri , AuthenticationSchemes [ i ] , nc ) ;
629
+ }
630
+ }
631
+ }
632
+ }
633
+
634
+ private static NetworkCredential GetCredentials ( ICredentials credentials , Uri requestUri )
551
635
{
552
- if ( proxyCredentials == null )
636
+ if ( credentials == null )
553
637
{
554
638
return null ;
555
639
}
556
640
557
641
foreach ( var authScheme in AuthenticationSchemes )
558
642
{
559
- NetworkCredential proxyCreds = proxyCredentials . GetCredential ( requestUri , authScheme ) ;
560
- if ( proxyCreds != null )
643
+ NetworkCredential networkCredential = credentials . GetCredential ( requestUri , authScheme ) ;
644
+ if ( networkCredential != null )
561
645
{
562
- return proxyCreds ;
646
+ return networkCredential ;
563
647
}
564
648
}
565
-
566
649
return null ;
567
650
}
568
651
@@ -660,6 +743,15 @@ private void SetCurlOption(SafeCurlHandle handle, int option, long value)
660
743
}
661
744
}
662
745
746
+ private void SetCurlOption ( SafeCurlHandle handle , int option , ulong value )
747
+ {
748
+ int result = Interop . libcurl . curl_easy_setopt ( handle , option , value ) ;
749
+ if ( result != CURLcode . CURLE_OK )
750
+ {
751
+ throw new HttpRequestException ( SR . net_http_client_execution_error , GetCurlException ( result ) ) ;
752
+ }
753
+ }
754
+
663
755
private void SetCurlOption ( SafeCurlHandle handle , int option , Interop . libcurl . curl_readwrite_callback value )
664
756
{
665
757
int result = Interop . libcurl . curl_easy_setopt ( handle , option , value ) ;
@@ -868,6 +960,13 @@ private static void RemoveEasyHandle(SafeCurlMultiHandle multiHandle, GCHandle s
868
960
869
961
private sealed class RequestCompletionSource : TaskCompletionSource < HttpResponseMessage >
870
962
{
963
+ private readonly CurlHandler _handler ;
964
+
965
+ public RequestCompletionSource ( CurlHandler handler )
966
+ {
967
+ this . _handler = handler ;
968
+ }
969
+
871
970
public CancellationToken CancellationToken { get ; set ; }
872
971
873
972
public HttpRequestMessage RequestMessage { get ; set ; }
@@ -883,6 +982,16 @@ private sealed class RequestCompletionSource : TaskCompletionSource<HttpResponse
883
982
public Stream RequestContentStream { get ; set ; }
884
983
885
984
public byte [ ] RequestContentBuffer { get ; set ; }
985
+
986
+ public NetworkCredential NetworkCredential { get ; set ; }
987
+
988
+ public CurlHandler Handler
989
+ {
990
+ get
991
+ {
992
+ return _handler ;
993
+ }
994
+ }
886
995
}
887
996
888
997
private enum ProxyUsePolicy
0 commit comments