Skip to content

Commit a32e0cf

Browse files
committed
Use custom HTTPS transport
1 parent c7a876e commit a32e0cf

File tree

3 files changed

+239
-2
lines changed

3 files changed

+239
-2
lines changed

LibGit2Sharp.Tests/NetworkFixture.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ public void CanPull(FastForwardStrategy fastForwardStrategy)
168168

169169
if(fastForwardStrategy == FastForwardStrategy.Default || fastForwardStrategy == FastForwardStrategy.FastForwardOnly)
170170
{
171+
171172
Assert.Equal(MergeStatus.FastForward, mergeResult.Status);
172173
Assert.Equal(mergeResult.Commit, repo.Branches["refs/remotes/origin/master"].Tip);
173174
Assert.Equal(repo.Head.Tip, repo.Branches["refs/remotes/origin/master"].Tip);
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
using System;
2+
using System.IO;
3+
using System.Net;
4+
5+
namespace LibGit2Sharp.Core
6+
{
7+
internal class ManagedHttpSmartSubtransport : RpcSmartSubtransport
8+
{
9+
protected override SmartSubtransportStream Action(string url, GitSmartSubtransportAction action)
10+
{
11+
string endpointUrl, contentType = null;
12+
bool isPost = false;
13+
14+
switch (action)
15+
{
16+
case GitSmartSubtransportAction.UploadPackList:
17+
endpointUrl = string.Concat(url, "/info/refs?service=git-upload-pack");
18+
break;
19+
20+
case GitSmartSubtransportAction.UploadPack:
21+
endpointUrl = string.Concat(url, "/git-upload-pack");
22+
contentType = "application/x-git-upload-pack-request";
23+
isPost = true;
24+
break;
25+
26+
case GitSmartSubtransportAction.ReceivePackList:
27+
endpointUrl = string.Concat(url, "/info/refs?service=git-receive-pack");
28+
break;
29+
30+
case GitSmartSubtransportAction.ReceivePack:
31+
endpointUrl = string.Concat(url, "/git-receive-pack");
32+
contentType = "application/x-git-receive-pack-request";
33+
isPost = true;
34+
break;
35+
36+
default:
37+
throw new InvalidOperationException();
38+
}
39+
40+
return new ManagedHttpSmartSubtransportStream(this, endpointUrl, isPost, contentType);
41+
}
42+
43+
private class ManagedHttpSmartSubtransportStream : SmartSubtransportStream
44+
{
45+
private static int MAX_REDIRECTS = 7;
46+
47+
private MemoryStream postBuffer = new MemoryStream();
48+
private Stream responseStream;
49+
50+
public ManagedHttpSmartSubtransportStream(ManagedHttpSmartSubtransport parent, string endpointUrl, bool isPost, string contentType)
51+
: base(parent)
52+
{
53+
EndpointUrl = endpointUrl;
54+
IsPost = isPost;
55+
ContentType = contentType;
56+
}
57+
58+
private string EndpointUrl
59+
{
60+
get;
61+
set;
62+
}
63+
64+
private bool IsPost
65+
{
66+
get;
67+
set;
68+
}
69+
70+
private string ContentType
71+
{
72+
get;
73+
set;
74+
}
75+
76+
public override int Write(Stream dataStream, long length)
77+
{
78+
byte[] buffer = new byte[4096];
79+
long writeTotal = 0;
80+
81+
while (length > 0)
82+
{
83+
int readLen = dataStream.Read(buffer, 0, (int)Math.Min(buffer.Length, length));
84+
85+
if (readLen == 0)
86+
{
87+
break;
88+
}
89+
90+
postBuffer.Write(buffer, 0, readLen);
91+
length -= readLen;
92+
writeTotal += readLen;
93+
}
94+
95+
if (writeTotal < length)
96+
{
97+
throw new EndOfStreamException("Could not write buffer (short read)");
98+
}
99+
100+
return 0;
101+
}
102+
103+
private static HttpWebRequest CreateWebRequest(string endpointUrl, bool isPost, string contentType)
104+
{
105+
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
106+
107+
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(endpointUrl);
108+
webRequest.UserAgent = "git/1.0 (libgit2 custom transport)";
109+
webRequest.ServicePoint.Expect100Continue = false;
110+
webRequest.AllowAutoRedirect = false;
111+
112+
if (isPost)
113+
{
114+
webRequest.Method = "POST";
115+
webRequest.ContentType = contentType;
116+
}
117+
118+
return webRequest;
119+
}
120+
121+
private HttpWebResponse GetResponseWithRedirects()
122+
{
123+
HttpWebRequest request = CreateWebRequest(EndpointUrl, IsPost, ContentType);
124+
HttpWebResponse response = null;
125+
int retries;
126+
127+
for (retries = 0; ; retries++)
128+
{
129+
if (retries > MAX_REDIRECTS)
130+
{
131+
throw new Exception("too many redirects or authentication replays");
132+
}
133+
134+
if (IsPost && postBuffer.Length > 0)
135+
{
136+
postBuffer.Seek(0, SeekOrigin.Begin);
137+
138+
using (Stream requestStream = request.GetRequestStream())
139+
{
140+
postBuffer.WriteTo(requestStream);
141+
}
142+
}
143+
144+
try
145+
{
146+
response = (HttpWebResponse)request.GetResponse();
147+
}
148+
catch (WebException ex)
149+
{
150+
response = (HttpWebResponse)ex.Response;
151+
}
152+
153+
if (response.StatusCode == HttpStatusCode.OK)
154+
{
155+
break;
156+
}
157+
else if (response.StatusCode == HttpStatusCode.Unauthorized)
158+
{
159+
Credentials cred;
160+
int ret = SmartTransport.AcquireCredentials(out cred, null, typeof(UsernamePasswordCredentials));
161+
162+
if (ret != 0)
163+
{
164+
throw new InvalidOperationException("authentication cancelled");
165+
}
166+
167+
request = CreateWebRequest(EndpointUrl, IsPost, ContentType);
168+
UsernamePasswordCredentials userpass = (UsernamePasswordCredentials)cred;
169+
request.Credentials = new NetworkCredential(userpass.Username, userpass.Password);
170+
continue;
171+
}
172+
else if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.Redirect)
173+
{
174+
request = CreateWebRequest(response.Headers["Location"], IsPost, ContentType);
175+
continue;
176+
}
177+
178+
throw new Exception(string.Format("unexpected HTTP response: {0}", response.StatusCode));
179+
}
180+
181+
return response;
182+
}
183+
184+
public override int Read(Stream dataStream, long length, out long readTotal)
185+
{
186+
byte[] buffer = new byte[4096];
187+
readTotal = 0;
188+
189+
if (responseStream == null)
190+
{
191+
HttpWebResponse response = GetResponseWithRedirects();
192+
responseStream = response.GetResponseStream();
193+
}
194+
195+
while (length > 0)
196+
{
197+
int readLen = responseStream.Read(buffer, 0, (int)Math.Min(buffer.Length, length));
198+
199+
if (readLen == 0)
200+
break;
201+
202+
dataStream.Write(buffer, 0, readLen);
203+
readTotal += readLen;
204+
length -= readLen;
205+
}
206+
207+
return 0;
208+
}
209+
210+
protected override void Free()
211+
{
212+
if (responseStream != null)
213+
{
214+
responseStream.Dispose();
215+
responseStream = null;
216+
}
217+
218+
base.Free();
219+
}
220+
}
221+
}
222+
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ internal static class NativeMethods
2525
private static NativeShutdownObject shutdownObject;
2626
#pragma warning restore 0414
2727

28+
private static SmartSubtransportRegistration<ManagedHttpSmartSubtransport> httpSubtransportRegistration;
29+
private static SmartSubtransportRegistration<ManagedHttpSmartSubtransport> httpsSubtransportRegistration;
30+
2831
static NativeMethods()
2932
{
3033
if (Platform.IsRunningOnNetFramework() || Platform.IsRunningOnNetCore())
@@ -79,10 +82,11 @@ private static void InitializeNativeLibrary()
7982
shutdownObject = new NativeShutdownObject();
8083
}
8184

82-
// Configure the OpenSSL locking on the first initialization of the library in the current process.
85+
// Configure the .NET HTTP(S) mechanism on the first initialization of the library in the current process.
8386
if (initCounter == 1)
8487
{
85-
git_openssl_set_locking();
88+
httpSubtransportRegistration = GlobalSettings.RegisterSmartSubtransport<ManagedHttpSmartSubtransport>("http");
89+
httpsSubtransportRegistration = GlobalSettings.RegisterSmartSubtransport<ManagedHttpSmartSubtransport>("https");
8690
}
8791
}
8892

@@ -91,6 +95,16 @@ private sealed class NativeShutdownObject : CriticalFinalizerObject
9195
{
9296
~NativeShutdownObject()
9397
{
98+
if (httpSubtransportRegistration != null)
99+
{
100+
GlobalSettings.UnregisterSmartSubtransport(httpSubtransportRegistration);
101+
}
102+
103+
if (httpsSubtransportRegistration != null)
104+
{
105+
GlobalSettings.UnregisterSmartSubtransport(httpsSubtransportRegistration);
106+
}
107+
94108
git_libgit2_shutdown();
95109
}
96110
}

0 commit comments

Comments
 (0)