Skip to content

Commit 0ddf3a2

Browse files
committed
feat: Add CustomHeaders to PushOptions
Wires up CustomHeaders in PushOptions in the same vein as FetchOptions.
1 parent 8c32b61 commit 0ddf3a2

File tree

6 files changed

+148
-6
lines changed

6 files changed

+148
-6
lines changed

.devcontainer/Dockerfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
FROM mcr.microsoft.com/devcontainers/base:debian
2+
3+
# Install .net 6 and .net 7
4+
# https://learn.microsoft.com/en-us/dotnet/core/install/linux-debian
5+
RUN wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb && \
6+
dpkg -i packages-microsoft-prod.deb && \
7+
rm packages-microsoft-prod.deb
8+
RUN apt-get update -yq && \
9+
apt-get install -yq --no-install-recommends \
10+
dotnet-sdk-6.0 dotnet-sdk-7.0
11+
12+
# Install mono framework
13+
# https://www.mono-project.com/download/stable/#download-lin-debian
14+
RUN apt-get install -yq --no-install-recommends \
15+
dirmngr ca-certificates gnupg
16+
RUN gpg --keyserver keyserver.ubuntu.com --recv 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF && \
17+
gpg --export 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF | tee /usr/share/keyrings/mono.gpg >/dev/null && \
18+
gpg --batch --yes --delete-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF && \
19+
echo "deb [signed-by=/usr/share/keyrings/mono.gpg] https://download.mono-project.com/repo/debian stable-buster main" | tee /etc/apt/sources.list.d/mono-official-stable.list
20+
RUN apt-get update -yq && \
21+
apt-get install -yq --no-install-recommends \
22+
mono-devel

.devcontainer/devcontainer.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
3+
{
4+
"name": "C# (.NET)",
5+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6+
"dockerFile": "Dockerfile",
7+
// Features to add to the dev container. More info: https://containers.dev/features.
8+
// "features": {},
9+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
10+
// "forwardPorts": [5000, 5001],
11+
// "portsAttributes": {
12+
// "5001": {
13+
// "protocol": "https"
14+
// }
15+
// }
16+
// Use 'postCreateCommand' to run commands after the container is created.
17+
// "postCreateCommand": "dotnet restore",
18+
// Configure tool-specific properties.
19+
"customizations": {
20+
"vscode": {
21+
"extensions": [
22+
"ms-dotnettools.csharp"
23+
]
24+
}
25+
}
26+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
27+
// "remoteUser": "root"
28+
}

LibGit2Sharp.Tests/PushFixture.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,33 @@ public void CanForcePush()
196196
}
197197
}
198198

199+
[Fact]
200+
public void CanPushWithCustomHeaders()
201+
{
202+
const string knownHeader = "X-Hello: mygit-201";
203+
var options = new PushOptions { CustomHeaders = new string[] { knownHeader } };
204+
AssertPush(repo =>
205+
repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options));
206+
}
207+
208+
[Fact]
209+
public void CannotPushWithForbiddenCustomHeaders()
210+
{
211+
const string knownHeader = "User-Agent: mygit-201";
212+
var options = new PushOptions { CustomHeaders = new string[] { knownHeader } };
213+
Assert.Throws<LibGit2SharpException>(
214+
() => AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options)));
215+
}
216+
217+
[Fact]
218+
public void CannotPushWithMalformedCustomHeaders()
219+
{
220+
const string knownHeader = "Hello world";
221+
var options = new PushOptions { CustomHeaders = new string[] { knownHeader } };
222+
Assert.Throws<LibGit2SharpException>(
223+
() => AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options)));
224+
}
225+
199226
private static void AssertRemoteHeadTipEquals(IRepository localRepo, string sha)
200227
{
201228
var remoteReferences = localRepo.Network.ListReferences(localRepo.Network.Remotes.Single());
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
3+
namespace LibGit2Sharp.Core
4+
{
5+
/// <summary>
6+
/// Git push options wrapper. Disposable wrapper for <see cref="GitPushOptions"/>.
7+
/// </summary>
8+
internal class GitPushOptionsWrapper : IDisposable
9+
{
10+
public GitPushOptionsWrapper() : this(new GitPushOptions()) { }
11+
12+
public GitPushOptionsWrapper(GitPushOptions pushOptions)
13+
{
14+
this.Options = pushOptions;
15+
}
16+
17+
public GitPushOptions Options { get; private set; }
18+
19+
#region IDisposable
20+
private bool disposedValue = false; // To detect redundant calls
21+
protected virtual void Dispose(bool disposing)
22+
{
23+
if (disposedValue)
24+
return;
25+
26+
this.Options.CustomHeaders.Dispose();
27+
disposedValue = true;
28+
}
29+
30+
public void Dispose()
31+
{
32+
Dispose(true);
33+
}
34+
#endregion
35+
}
36+
}

LibGit2Sharp/Network.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -365,18 +365,27 @@ public virtual void Push(Remote remote, IEnumerable<string> pushRefSpecs, PushOp
365365

366366
// Load the remote.
367367
using (RemoteHandle remoteHandle = Proxy.git_remote_lookup(repository.Handle, remote.Name, true))
368+
369+
// Create a git options wrapper so managed strings are disposed.
370+
using (var pushOptionsWrapper = new GitPushOptionsWrapper())
368371
{
369372
var callbacks = new RemoteCallbacks(pushOptions);
370373
GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks();
371374

375+
var gitPushOptions = pushOptionsWrapper.Options;
376+
gitPushOptions.PackbuilderDegreeOfParallelism = pushOptions.PackbuilderDegreeOfParallelism;
377+
gitPushOptions.RemoteCallbacks = gitCallbacks;
378+
gitPushOptions.ProxyOptions = new GitProxyOptions { Version = 1 };
379+
380+
// If there are custom headers, create a managed string array.
381+
if (pushOptions.CustomHeaders != null && pushOptions.CustomHeaders.Length > 0)
382+
{
383+
gitPushOptions.CustomHeaders = GitStrArrayManaged.BuildFrom(pushOptions.CustomHeaders);
384+
}
385+
372386
Proxy.git_remote_push(remoteHandle,
373387
pushRefSpecs,
374-
new GitPushOptions()
375-
{
376-
PackbuilderDegreeOfParallelism = pushOptions.PackbuilderDegreeOfParallelism,
377-
RemoteCallbacks = gitCallbacks,
378-
ProxyOptions = new GitProxyOptions { Version = 1 },
379-
});
388+
gitPushOptions);
380389
}
381390
}
382391

LibGit2Sharp/PushOptions.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,25 @@ public sealed class PushOptions
5151
/// information about what updates will be performed.
5252
/// </summary>
5353
public PrePushHandler OnNegotiationCompletedBeforePush { get; set; }
54+
55+
/// <summary>
56+
/// Get/Set the custom headers.
57+
/// <para>
58+
/// This allows you to set custom headers (e.g. X-Forwarded-For,
59+
/// X-Request-Id, etc),
60+
/// </para>
61+
/// </summary>
62+
/// <remarks>
63+
/// Libgit2 sets some headers for HTTP requests (User-Agent, Host,
64+
/// Accept, Content-Type, Transfer-Encoding, Content-Length, Accept) that
65+
/// cannot be overriden.
66+
/// </remarks>
67+
/// <example>
68+
/// var pushOptions - new PushOptions() {
69+
/// CustomHeaders = new String[] {"X-Request-Id: 12345"}
70+
/// };
71+
/// </example>
72+
/// <value>The custom headers string array</value>
73+
public string[] CustomHeaders { get; set; }
5474
}
5575
}

0 commit comments

Comments
 (0)