Skip to content

Commit 70b393b

Browse files
committed
Use the .NET standard HTTP class
Use the .NET standard HTTP class for the Managed HTTP subtransport
1 parent 7be9794 commit 70b393b

File tree

3 files changed

+119
-78
lines changed

3 files changed

+119
-78
lines changed

LibGit2Sharp/Core/ManagedHttpSmartSubtransport.cs

Lines changed: 89 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
using System;
22
using System.IO;
3+
using System.Linq;
34
using System.Net;
5+
using System.Net.Http;
46
using System.Net.Security;
7+
using System.Security.Authentication;
58
using System.Security.Cryptography.X509Certificates;
69

710
namespace LibGit2Sharp.Core
@@ -49,12 +52,36 @@ private class ManagedHttpSmartSubtransportStream : SmartSubtransportStream
4952
private MemoryStream postBuffer = new MemoryStream();
5053
private Stream responseStream;
5154

55+
private HttpClientHandler httpClientHandler;
56+
private HttpClient httpClient;
57+
5258
public ManagedHttpSmartSubtransportStream(ManagedHttpSmartSubtransport parent, string endpointUrl, bool isPost, string contentType)
5359
: base(parent)
5460
{
5561
EndpointUrl = new Uri(endpointUrl);
5662
IsPost = isPost;
5763
ContentType = contentType;
64+
65+
httpClientHandler = CreateClientHandler();
66+
httpClient = new HttpClient(httpClientHandler);
67+
}
68+
69+
private HttpClientHandler CreateClientHandler()
70+
{
71+
#if !NETFRAMEWORK
72+
var httpClientHandler = new HttpClientHandler();
73+
httpClientHandler.SslProtocols |= SslProtocols.Tls12;
74+
httpClientHandler.ServerCertificateCustomValidationCallback = CertificateValidationProxy;
75+
#else
76+
var httpClientHandler = new WebRequestHandler();
77+
httpClientHandler.ServerCertificateValidationCallback = CertificateValidationProxy;
78+
79+
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
80+
#endif
81+
82+
httpClientHandler.AllowAutoRedirect = false;
83+
84+
return httpClientHandler;
5885
}
5986

6087
private Uri EndpointUrl
@@ -104,13 +131,21 @@ public override int Write(Stream dataStream, long length)
104131

105132
private bool CertificateValidationProxy(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
106133
{
107-
int ret = SmartTransport.CertificateCheck(new CertificateX509(cert), (errors == SslPolicyErrors.None), EndpointUrl.Host);
108-
Ensure.ZeroResult(ret);
134+
try
135+
{
136+
int ret = SmartTransport.CertificateCheck(new CertificateX509(cert), (errors == SslPolicyErrors.None), EndpointUrl.Host);
137+
Ensure.ZeroResult(ret);
109138

110-
return true;
139+
return true;
140+
}
141+
catch(Exception e)
142+
{
143+
SetError(e);
144+
return false;
145+
}
111146
}
112147

113-
private string getUserAgent()
148+
private string GetUserAgent()
114149
{
115150
string userAgent = GlobalSettings.GetUserAgent();
116151

@@ -122,97 +157,76 @@ private string getUserAgent()
122157
return userAgent;
123158
}
124159

125-
private HttpWebRequest CreateWebRequest(Uri endpointUrl, bool isPost, string contentType)
160+
private HttpRequestMessage CreateRequest(Uri endpointUrl, bool isPost, string contentType)
126161
{
127-
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
128-
129-
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(endpointUrl);
130-
webRequest.UserAgent = String.Format("git/2.0 ({0})", getUserAgent());
131-
webRequest.ServicePoint.Expect100Continue = false;
132-
webRequest.AllowAutoRedirect = false;
133-
webRequest.ServerCertificateValidationCallback += CertificateValidationProxy;
162+
var verb = isPost ? new HttpMethod("POST") : new HttpMethod("GET");
163+
var request = new HttpRequestMessage(verb, endpointUrl);
164+
request.Headers.Add("User-Agent", String.Format("git/2.0 ({0})", GetUserAgent()));
165+
request.Headers.Remove("Expect");
134166

135-
if (isPost)
136-
{
137-
webRequest.Method = "POST";
138-
webRequest.ContentType = contentType;
139-
}
140-
141-
return webRequest;
167+
return request;
142168
}
143169

144-
private HttpWebResponse GetResponseWithRedirects()
170+
private HttpResponseMessage GetResponseWithRedirects()
145171
{
146-
HttpWebRequest request = CreateWebRequest(EndpointUrl, IsPost, ContentType);
147-
HttpWebResponse response = null;
172+
ICredentials credentials = null;
173+
var url = EndpointUrl;
148174
int retries;
149175

150176
for (retries = 0; ; retries++)
151177
{
152-
if (retries > MAX_REDIRECTS)
153-
{
154-
throw new Exception("too many redirects or authentication replays");
155-
}
178+
var httpClientHandler = CreateClientHandler();
179+
httpClientHandler.Credentials = credentials;
156180

157-
if (IsPost && postBuffer.Length > 0)
181+
using (var httpClient = new HttpClient(httpClientHandler))
158182
{
159-
postBuffer.Seek(0, SeekOrigin.Begin);
183+
var request = CreateRequest(url, IsPost, ContentType);
160184

161-
using (Stream requestStream = request.GetRequestStream())
185+
if (retries > MAX_REDIRECTS)
162186
{
163-
postBuffer.WriteTo(requestStream);
187+
throw new Exception("too many redirects or authentication replays");
164188
}
165-
}
166189

167-
try
168-
{
169-
response = (HttpWebResponse)request.GetResponse();
170-
}
171-
catch (WebException ex)
172-
{
173-
if (ex.Response != null)
190+
if (IsPost && postBuffer.Length > 0)
174191
{
175-
response = (HttpWebResponse)ex.Response;
192+
var bufferDup = new MemoryStream(postBuffer.GetBuffer());
193+
bufferDup.Seek(0, SeekOrigin.Begin);
194+
195+
request.Content = new StreamContent(bufferDup);
196+
request.Content.Headers.Add("Content-Type", ContentType);
176197
}
177-
else if (ex.InnerException != null)
198+
199+
var response = httpClient.SendAsync(request).Result;
200+
201+
if (response.StatusCode == HttpStatusCode.OK)
178202
{
179-
throw ex.InnerException;
203+
return response;
180204
}
181-
else
205+
else if (response.StatusCode == HttpStatusCode.Unauthorized)
182206
{
183-
throw new Exception("unknown network failure");
184-
}
185-
}
207+
Credentials cred;
208+
int ret = SmartTransport.AcquireCredentials(out cred, null, typeof(UsernamePasswordCredentials));
186209

187-
if (response.StatusCode == HttpStatusCode.OK)
188-
{
189-
break;
190-
}
191-
else if (response.StatusCode == HttpStatusCode.Unauthorized)
192-
{
193-
Credentials cred;
194-
int ret = SmartTransport.AcquireCredentials(out cred, null, typeof(UsernamePasswordCredentials));
210+
if (ret != 0)
211+
{
212+
throw new InvalidOperationException("authentication cancelled");
213+
}
195214

196-
if (ret != 0)
215+
UsernamePasswordCredentials userpass = (UsernamePasswordCredentials)cred;
216+
credentials = new NetworkCredential(userpass.Username, userpass.Password);
217+
continue;
218+
}
219+
else if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect)
197220
{
198-
throw new InvalidOperationException("authentication cancelled");
221+
url = new Uri(response.Headers.GetValues("Location").First());
222+
continue;
199223
}
200224

201-
request = CreateWebRequest(EndpointUrl, IsPost, ContentType);
202-
UsernamePasswordCredentials userpass = (UsernamePasswordCredentials)cred;
203-
request.Credentials = new NetworkCredential(userpass.Username, userpass.Password);
204-
continue;
205-
}
206-
else if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect)
207-
{
208-
request = CreateWebRequest(new Uri(response.Headers["Location"]), IsPost, ContentType);
209-
continue;
225+
throw new Exception(string.Format("unexpected HTTP response: {0}", response.StatusCode));
210226
}
211-
212-
throw new Exception(string.Format("unexpected HTTP response: {0}", response.StatusCode));
213227
}
214228

215-
return response;
229+
throw new Exception("too many redirects or authentication replays");
216230
}
217231

218232
public override int Read(Stream dataStream, long length, out long readTotal)
@@ -222,8 +236,8 @@ public override int Read(Stream dataStream, long length, out long readTotal)
222236

223237
if (responseStream == null)
224238
{
225-
HttpWebResponse response = GetResponseWithRedirects();
226-
responseStream = response.GetResponseStream();
239+
HttpResponseMessage response = GetResponseWithRedirects();
240+
responseStream = response.Content.ReadAsStreamAsync().Result;
227241
}
228242

229243
while (length > 0)
@@ -249,6 +263,12 @@ protected override void Free()
249263
responseStream = null;
250264
}
251265

266+
if (httpClient != null)
267+
{
268+
httpClient.Dispose();
269+
httpClient = null;
270+
}
271+
252272
base.Free();
253273
}
254274
}

LibGit2Sharp/LibGit2Sharp.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,18 @@
3131
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" /> <!-- Needed for T4 generation -->
3232
</ItemGroup>
3333

34+
<ItemGroup>
35+
<Reference Include="System.Net.Http" />
36+
<Reference Include="System.Net.Http.WebRequest" />
37+
</ItemGroup>
38+
3439
<ItemGroup>
3540
<PackageReference Include="LibGit2Sharp.NativeBinaries" Version="[2.0.305]" PrivateAssets="none" />
3641
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="all" />
3742
<PackageReference Include="Nerdbank.GitVersioning" Version="3.0.50" PrivateAssets="all" />
43+
<PackageReference Include="System.Net.Http" Version="4.3.4">
44+
<ExcludeAssets>All</ExcludeAssets>
45+
</PackageReference>
3846
</ItemGroup>
3947

4048
<Import Project="..\Targets\CodeGenerator.targets" />

LibGit2Sharp/SmartSubtransportStream.cs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ public virtual SmartSubtransport SmartTransport
6161
get { return this.subtransport; }
6262
}
6363

64+
private Exception StoredError { get; set; }
65+
66+
internal void SetError(Exception ex)
67+
{
68+
StoredError = ex;
69+
}
70+
6471
private SmartSubtransport subtransport;
6572
private IntPtr nativeStreamPointer;
6673

@@ -96,6 +103,19 @@ private static class EntryPoints
96103
public static GitSmartSubtransportStream.write_callback WriteCallback = new GitSmartSubtransportStream.write_callback(Write);
97104
public static GitSmartSubtransportStream.free_callback FreeCallback = new GitSmartSubtransportStream.free_callback(Free);
98105

106+
private static int SetError(SmartSubtransportStream stream, Exception caught)
107+
{
108+
Exception ret = (stream.StoredError != null) ? stream.StoredError : caught;
109+
GitErrorCode errorCode = GitErrorCode.Error;
110+
111+
if (ret is NativeException)
112+
{
113+
errorCode = ((NativeException)ret).ErrorCode;
114+
}
115+
116+
return (int)errorCode;
117+
}
118+
99119
private unsafe static int Read(
100120
IntPtr stream,
101121
IntPtr buffer,
@@ -134,15 +154,9 @@ private unsafe static int Read(
134154
return toReturn;
135155
}
136156
}
137-
catch (NativeException ex)
138-
{
139-
Proxy.git_error_set_str(GitErrorCategory.Net, ex);
140-
return (int)ex.ErrorCode;
141-
}
142157
catch (Exception ex)
143158
{
144-
Proxy.git_error_set_str(GitErrorCategory.Net, ex);
145-
return (int)GitErrorCode.Error;
159+
return SetError(transportStream, ex);
146160
}
147161
}
148162

@@ -174,8 +188,7 @@ private static unsafe int Write(IntPtr stream, IntPtr buffer, UIntPtr len)
174188
}
175189
catch (Exception ex)
176190
{
177-
Proxy.git_error_set_str(GitErrorCategory.Net, ex);
178-
return (int)GitErrorCode.Error;
191+
return SetError(transportStream, ex);
179192
}
180193
}
181194

0 commit comments

Comments
 (0)