Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit fe4f468

Browse files
committed
Merge pull request #2557 from kapilash/proxy
HttpClient via libcurl : proxy support
2 parents 03c5b0b + 71efecc commit fe4f468

File tree

3 files changed

+162
-11
lines changed

3 files changed

+162
-11
lines changed

src/Common/src/Interop/Unix/libcurl/Interop.libcurl_types.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System;
5+
46
internal static partial class Interop
57
{
68
internal static partial class libcurl
@@ -12,8 +14,14 @@ internal static partial class CURLoption
1214
private const int CurlOptionLongBase = 0;
1315
private const int CurlOptionObjectPointBase = 10000;
1416

15-
internal const int CURLOPT_FOLLOWLOCATION = CurlOptionLongBase + 52;
1617
internal const int CURLOPT_URL = CurlOptionObjectPointBase + 2;
18+
internal const int CURLOPT_FOLLOWLOCATION = CurlOptionLongBase + 52;
19+
internal const int CURLOPT_PROXYPORT = CurlOptionLongBase + 59;
20+
internal const int CURLOPT_PROXYTYPE = CurlOptionLongBase + 101;
21+
22+
internal const int CURLOPT_WRITEDATA = CurlOptionObjectPointBase + 1;
23+
internal const int CURLOPT_PROXY = CurlOptionObjectPointBase + 4;
24+
internal const int CURLOPT_PROXYUSERPWD = CurlOptionObjectPointBase + 6;
1725
}
1826

1927
// Class for constants defined for the enum CURLINFO in curl.h
@@ -24,6 +32,12 @@ internal static partial class CURLINFO
2432
internal const int CURLINFO_RESPONSE_CODE = CurlInfoLongBase + 2;
2533
}
2634

35+
// Class for constants defined for the enum curl_proxytype in curl.h
36+
internal static partial class curl_proxytype
37+
{
38+
internal const int CURLPROXY_HTTP = 0;
39+
}
40+
2741
// Class for constants defined for the enum CURLcode in curl.h
2842
internal static partial class CURLcode
2943
{

src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.cs

Lines changed: 140 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using CURLoption = Interop.libcurl.CURLoption;
1313
using CURLcode = Interop.libcurl.CURLcode;
1414
using CURLINFO = Interop.libcurl.CURLINFO;
15+
using CURLProxyType = Interop.libcurl.curl_proxytype;
1516

1617
namespace System.Net.Http
1718
{
@@ -20,14 +21,17 @@ internal class CurlHandler : HttpMessageHandler
2021
#region Constants
2122

2223
private const string UriSchemeHttps = "https";
23-
24+
private readonly static string[] AuthenticationSchemes = { "Negotiate", "Digest", "Basic" }; // the order in which libcurl goes over authentication schemes
2425
#endregion
2526

2627
#region Fields
2728

2829
private volatile bool _anyOperationStarted;
2930
private volatile bool _disposed;
3031
private bool _automaticRedirection = true;
32+
private IWebProxy _proxy = null;
33+
private ICredentials _serverCredentials = null;
34+
private ProxyUsePolicy _proxyPolicy = ProxyUsePolicy.UseDefaultProxy;
3135

3236
#endregion
3337

@@ -50,6 +54,59 @@ internal bool AutomaticRedirection
5054
}
5155
}
5256

57+
internal bool SupportsProxy
58+
{
59+
get
60+
{
61+
return true;
62+
}
63+
}
64+
65+
internal bool UseProxy
66+
{
67+
get
68+
{
69+
return _proxyPolicy != ProxyUsePolicy.DoNotUseProxy;
70+
}
71+
set
72+
{
73+
CheckDisposedOrStarted();
74+
if (value)
75+
{
76+
_proxyPolicy = ProxyUsePolicy.UseCustomProxy;
77+
}
78+
else
79+
{
80+
_proxyPolicy = ProxyUsePolicy.DoNotUseProxy;
81+
}
82+
}
83+
}
84+
85+
internal IWebProxy Proxy
86+
{
87+
get
88+
{
89+
return _proxy;
90+
}
91+
set
92+
{
93+
CheckDisposedOrStarted();
94+
_proxy = value;
95+
}
96+
}
97+
98+
internal ICredentials Credentials
99+
{
100+
get
101+
{
102+
return _serverCredentials;
103+
}
104+
set
105+
{
106+
CheckDisposedOrStarted();
107+
_serverCredentials = value;
108+
}
109+
}
53110
#endregion
54111

55112
protected override void Dispose(bool disposing)
@@ -175,11 +232,83 @@ private SafeCurlHandle CreateRequestHandle(RequestCompletionSource state)
175232
Interop.libcurl.curl_easy_setopt(requestHandle, CURLoption.CURLOPT_FOLLOWLOCATION, 1);
176233
}
177234

178-
// TODO: Handle headers, proxy and other options
235+
SetProxyOptions(state, requestHandle);
236+
// TODO: Handle headers and other options
179237

180238
return requestHandle;
181239
}
182240

241+
private static void SetProxyOptions(RequestCompletionSource state, SafeCurlHandle requestHandle)
242+
{
243+
var requestUri = state.RequestMessage.RequestUri;
244+
Debug.Assert(state.Handler != null);
245+
if (state.Handler._proxyPolicy == ProxyUsePolicy.DoNotUseProxy)
246+
{
247+
Interop.libcurl.curl_easy_setopt(requestHandle, CURLoption.CURLOPT_PROXY, string.Empty);
248+
return;
249+
}
250+
251+
if ((state.Handler._proxyPolicy == ProxyUsePolicy.UseDefaultProxy) || (state.Handler.Proxy == null))
252+
{
253+
return;
254+
}
255+
256+
Debug.Assert( (state.Handler.Proxy != null) && (state.Handler._proxyPolicy == ProxyUsePolicy.UseCustomProxy));
257+
if (state.Handler.Proxy.IsBypassed(requestUri))
258+
{
259+
Interop.libcurl.curl_easy_setopt(requestHandle, CURLoption.CURLOPT_PROXY, string.Empty);
260+
return;
261+
}
262+
263+
var proxyUri = state.Handler.Proxy.GetProxy(requestUri);
264+
if (proxyUri == null)
265+
{
266+
return;
267+
}
268+
269+
Interop.libcurl.curl_easy_setopt(requestHandle, CURLoption.CURLOPT_PROXYTYPE, CURLProxyType.CURLPROXY_HTTP);
270+
Interop.libcurl.curl_easy_setopt(requestHandle, CURLoption.CURLOPT_PROXY, proxyUri.AbsoluteUri);
271+
Interop.libcurl.curl_easy_setopt(requestHandle, CURLoption.CURLOPT_PROXYPORT, proxyUri.Port);
272+
NetworkCredential credentials = GetCredentials(state.Handler.Proxy.Credentials, requestUri);
273+
if (credentials != null)
274+
{
275+
if (string.IsNullOrEmpty(credentials.UserName))
276+
{
277+
throw new ArgumentException(SR.net_http_argument_empty_string, "UserName");
278+
}
279+
280+
string credentialText;
281+
if (string.IsNullOrEmpty(credentials.Domain))
282+
{
283+
credentialText = string.Format("{0}:{1}", credentials.UserName, credentials.Password);
284+
}
285+
else
286+
{
287+
credentialText = string.Format("{2}\\{0}:{1}", credentials.UserName, credentials.Password, credentials.Domain);
288+
}
289+
Interop.libcurl.curl_easy_setopt(requestHandle, CURLoption.CURLOPT_PROXYUSERPWD, credentialText);
290+
}
291+
}
292+
293+
private static NetworkCredential GetCredentials(ICredentials proxyCredentials, Uri requestUri)
294+
{
295+
if (proxyCredentials == null)
296+
{
297+
return null;
298+
}
299+
300+
foreach (var authScheme in AuthenticationSchemes)
301+
{
302+
NetworkCredential proxyCreds = proxyCredentials.GetCredential(requestUri, authScheme);
303+
if (proxyCreds != null)
304+
{
305+
return proxyCreds;
306+
}
307+
}
308+
309+
return null;
310+
}
311+
183312
private HttpResponseMessage CreateResponseMessage(SafeCurlHandle requestHandle, HttpRequestMessage request)
184313
{
185314
var response = new HttpResponseMessage();
@@ -190,7 +319,7 @@ private HttpResponseMessage CreateResponseMessage(SafeCurlHandle requestHandle,
190319
{
191320
throw new HttpRequestException(SR.net_http_client_execution_error, GetCurlException(result));
192321
}
193-
response.StatusCode = (HttpStatusCode) httpStatusCode;
322+
response.StatusCode = (HttpStatusCode)httpStatusCode;
194323

195324
// TODO: Do error processing if needed and return actual response
196325
response.Content =
@@ -255,5 +384,13 @@ private sealed class RequestCompletionSource : TaskCompletionSource<HttpResponse
255384

256385
public CurlHandler Handler { get; set; }
257386
}
387+
388+
private enum ProxyUsePolicy
389+
{
390+
DoNotUseProxy = 0, // Do not use proxy. Ignores the value set in the environment.
391+
UseDefaultProxy = 1, // Do not set the proxy parameter. Use the value of environment variable, if any.
392+
UseCustomProxy = 2 // Use The proxy specified by the user.
393+
}
258394
}
259395
}
396+

src/System.Net.Http/src/System/Net/Http/Unix/HttpClientHandler.Unix.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public virtual bool SupportsAutomaticDecompression
1717

1818
public virtual bool SupportsProxy
1919
{
20-
get { throw NotImplemented.ByDesignWithMessage("HTTP stack not implemented"); }
20+
get { return this._curlHandler.SupportsProxy; }
2121
}
2222

2323
public virtual bool SupportsRedirectConfiguration
@@ -51,14 +51,14 @@ public DecompressionMethods AutomaticDecompression
5151

5252
public bool UseProxy
5353
{
54-
get { throw NotImplemented.ByDesignWithMessage("HTTP stack not implemented"); }
55-
set { throw NotImplemented.ByDesignWithMessage("HTTP stack not implemented"); }
54+
get { return this._curlHandler.UseProxy; }
55+
set { this._curlHandler.UseProxy = value; }
5656
}
5757

5858
public IWebProxy Proxy
5959
{
60-
get { throw NotImplemented.ByDesignWithMessage("HTTP stack not implemented"); }
61-
set { throw NotImplemented.ByDesignWithMessage("HTTP stack not implemented"); }
60+
get { return this._curlHandler.Proxy; }
61+
set { this._curlHandler.Proxy = value; }
6262
}
6363

6464
public bool PreAuthenticate
@@ -75,8 +75,8 @@ public bool UseDefaultCredentials
7575

7676
public ICredentials Credentials
7777
{
78-
get { throw NotImplemented.ByDesignWithMessage("HTTP stack not implemented"); }
79-
set { throw NotImplemented.ByDesignWithMessage("HTTP stack not implemented"); }
78+
get { return this._curlHandler.Credentials; }
79+
set { this._curlHandler.Credentials = value; }
8080
}
8181

8282
public bool AllowAutoRedirect

0 commit comments

Comments
 (0)