Skip to content

Commit 512a7ed

Browse files
committed
Use the .NET standard HTTP class
Use the .NET standard HTTP class for the Managed HTTP subtransport
1 parent 712d2f6 commit 512a7ed

File tree

3 files changed

+116
-78
lines changed

3 files changed

+116
-78
lines changed

LibGit2Sharp/Core/ManagedHttpSmartSubtransport.cs

Lines changed: 87 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,34 @@ 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+
var httpClientHandler = new HttpClientHandler();
72+
73+
httpClientHandler.AllowAutoRedirect = false;
74+
httpClientHandler.ServerCertificateCustomValidationCallback = CertificateValidationProxy;
75+
76+
#if !NETFRAMEWORK
77+
httpClientHandler.SslProtocols = SslProtocols.Tls12;
78+
#else
79+
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
80+
#endif
81+
82+
return httpClientHandler;
5883
}
5984

6085
private Uri EndpointUrl
@@ -104,13 +129,21 @@ public override int Write(Stream dataStream, long length)
104129

105130
private bool CertificateValidationProxy(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
106131
{
107-
int ret = SmartTransport.CertificateCheck(new CertificateX509(cert), (errors == SslPolicyErrors.None), EndpointUrl.Host);
108-
Ensure.ZeroResult(ret);
132+
try
133+
{
134+
int ret = SmartTransport.CertificateCheck(new CertificateX509(cert), (errors == SslPolicyErrors.None), EndpointUrl.Host);
135+
Ensure.ZeroResult(ret);
109136

110-
return true;
137+
return true;
138+
}
139+
catch(Exception e)
140+
{
141+
SetError(e);
142+
return false;
143+
}
111144
}
112145

113-
private string getUserAgent()
146+
private string GetUserAgent()
114147
{
115148
string userAgent = GlobalSettings.GetUserAgent();
116149

@@ -122,97 +155,76 @@ private string getUserAgent()
122155
return userAgent;
123156
}
124157

125-
private HttpWebRequest CreateWebRequest(Uri endpointUrl, bool isPost, string contentType)
158+
private HttpRequestMessage CreateRequest(Uri endpointUrl, bool isPost, string contentType)
126159
{
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;
160+
var verb = isPost ? new HttpMethod("POST") : new HttpMethod("GET");
161+
var request = new HttpRequestMessage(verb, endpointUrl);
162+
request.Headers.Add("User-Agent", String.Format("git/2.0 ({0})", GetUserAgent()));
163+
request.Headers.Remove("Expect");
134164

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

144-
private HttpWebResponse GetResponseWithRedirects()
168+
private HttpResponseMessage GetResponseWithRedirects()
145169
{
146-
HttpWebRequest request = CreateWebRequest(EndpointUrl, IsPost, ContentType);
147-
HttpWebResponse response = null;
170+
ICredentials credentials = null;
171+
var url = EndpointUrl;
148172
int retries;
149173

150174
for (retries = 0; ; retries++)
151175
{
152-
if (retries > MAX_REDIRECTS)
153-
{
154-
throw new Exception("too many redirects or authentication replays");
155-
}
176+
var httpClientHandler = CreateClientHandler();
177+
httpClientHandler.Credentials = credentials;
156178

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

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

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

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));
208+
if (ret != 0)
209+
{
210+
throw new InvalidOperationException("authentication cancelled");
211+
}
195212

196-
if (ret != 0)
213+
UsernamePasswordCredentials userpass = (UsernamePasswordCredentials)cred;
214+
credentials = new NetworkCredential(userpass.Username, userpass.Password);
215+
continue;
216+
}
217+
else if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect)
197218
{
198-
throw new InvalidOperationException("authentication cancelled");
219+
url = new Uri(response.Headers.GetValues("Location").First());
220+
continue;
199221
}
200222

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;
223+
throw new Exception(string.Format("unexpected HTTP response: {0}", response.StatusCode));
210224
}
211-
212-
throw new Exception(string.Format("unexpected HTTP response: {0}", response.StatusCode));
213225
}
214226

215-
return response;
227+
throw new Exception("too many redirects or authentication replays");
216228
}
217229

218230
public override int Read(Stream dataStream, long length, out long readTotal)
@@ -222,8 +234,8 @@ public override int Read(Stream dataStream, long length, out long readTotal)
222234

223235
if (responseStream == null)
224236
{
225-
HttpWebResponse response = GetResponseWithRedirects();
226-
responseStream = response.GetResponseStream();
237+
HttpResponseMessage response = GetResponseWithRedirects();
238+
responseStream = (response.Content as StreamContent).ReadAsStreamAsync().Result;
227239
}
228240

229241
while (length > 0)
@@ -249,6 +261,12 @@ protected override void Free()
249261
responseStream = null;
250262
}
251263

264+
if (httpClient != null)
265+
{
266+
httpClient.Dispose();
267+
httpClient = null;
268+
}
269+
252270
base.Free();
253271
}
254272
}

LibGit2Sharp/LibGit2Sharp.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,17 @@
2828
<Compile Update="Core\Handles\Objects.cs" DependentUpon="Objects.tt" />
2929
</ItemGroup>
3030

31+
<ItemGroup>
32+
<Reference Include="System.Net.Http" />
33+
</ItemGroup>
34+
3135
<ItemGroup>
3236
<PackageReference Include="LibGit2Sharp.NativeBinaries.NoTLS" Version="[1.0.250-pre20181106235659]" PrivateAssets="none" />
3337
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-63127-02" PrivateAssets="all" />
3438
<PackageReference Include="Nerdbank.GitVersioning" Version="2.2.13" PrivateAssets="all" />
39+
<PackageReference Include="System.Net.Http" Version="4.3.4">
40+
<ExcludeAssets>All</ExcludeAssets>
41+
</PackageReference>
3542
</ItemGroup>
3643

3744
<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.giterr_set_str(GitErrorCategory.Net, ex);
140-
return (int)ex.ErrorCode;
141-
}
142157
catch (Exception ex)
143158
{
144-
Proxy.giterr_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.giterr_set_str(GitErrorCategory.Net, ex);
178-
return (int)GitErrorCode.Error;
191+
return SetError(transportStream, ex);
179192
}
180193
}
181194

0 commit comments

Comments
 (0)