From 714a4262374c8da4964debb7a049ef7a9d88b823 Mon Sep 17 00:00:00 2001 From: James La Novara-Gsell Date: Thu, 2 Nov 2023 23:04:14 -0400 Subject: [PATCH] feat: Add CustomHeaders to PushOptions Wires up CustomHeaders in PushOptions in the same vain as FetchOptions. --- LibGit2Sharp.Tests/PushFixture.cs | 27 ++++++++++++++++ LibGit2Sharp/Core/GitPushOptionsWrapper.cs | 36 ++++++++++++++++++++++ LibGit2Sharp/Network.cs | 21 +++++++++---- LibGit2Sharp/PushOptions.cs | 20 ++++++++++++ 4 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 LibGit2Sharp/Core/GitPushOptionsWrapper.cs diff --git a/LibGit2Sharp.Tests/PushFixture.cs b/LibGit2Sharp.Tests/PushFixture.cs index d8cf2befe..fc6c0c42d 100644 --- a/LibGit2Sharp.Tests/PushFixture.cs +++ b/LibGit2Sharp.Tests/PushFixture.cs @@ -196,6 +196,33 @@ public void CanForcePush() } } + [Fact] + public void CanPushWithCustomHeaders() + { + const string knownHeader = "X-Hello: mygit-201"; + var options = new PushOptions { CustomHeaders = new string[] { knownHeader } }; + AssertPush(repo => + repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options)); + } + + [Fact] + public void CannotPushWithForbiddenCustomHeaders() + { + const string knownHeader = "User-Agent: mygit-201"; + var options = new PushOptions { CustomHeaders = new string[] { knownHeader } }; + Assert.Throws( + () => AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options))); + } + + [Fact] + public void CannotPushWithMalformedCustomHeaders() + { + const string knownHeader = "Hello world"; + var options = new PushOptions { CustomHeaders = new string[] { knownHeader } }; + Assert.Throws( + () => AssertPush(repo => repo.Network.Push(repo.Network.Remotes["origin"], "HEAD", @"refs/heads/master", options))); + } + private static void AssertRemoteHeadTipEquals(IRepository localRepo, string sha) { var remoteReferences = localRepo.Network.ListReferences(localRepo.Network.Remotes.Single()); diff --git a/LibGit2Sharp/Core/GitPushOptionsWrapper.cs b/LibGit2Sharp/Core/GitPushOptionsWrapper.cs new file mode 100644 index 000000000..ffac5a220 --- /dev/null +++ b/LibGit2Sharp/Core/GitPushOptionsWrapper.cs @@ -0,0 +1,36 @@ +using System; + +namespace LibGit2Sharp.Core +{ + /// + /// Git push options wrapper. Disposable wrapper for . + /// + internal class GitPushOptionsWrapper : IDisposable + { + public GitPushOptionsWrapper() : this(new GitPushOptions()) { } + + public GitPushOptionsWrapper(GitPushOptions pushOptions) + { + this.Options = pushOptions; + } + + public GitPushOptions Options { get; private set; } + + #region IDisposable + private bool disposedValue = false; // To detect redundant calls + protected virtual void Dispose(bool disposing) + { + if (disposedValue) + return; + + this.Options.CustomHeaders.Dispose(); + disposedValue = true; + } + + public void Dispose() + { + Dispose(true); + } + #endregion + } +} diff --git a/LibGit2Sharp/Network.cs b/LibGit2Sharp/Network.cs index d5f032058..825f3c886 100644 --- a/LibGit2Sharp/Network.cs +++ b/LibGit2Sharp/Network.cs @@ -365,18 +365,27 @@ public virtual void Push(Remote remote, IEnumerable pushRefSpecs, PushOp // Load the remote. using (RemoteHandle remoteHandle = Proxy.git_remote_lookup(repository.Handle, remote.Name, true)) + + // Create a git options wrapper so managed strings are disposed. + using (var pushOptionsWrapper = new GitPushOptionsWrapper()) { var callbacks = new RemoteCallbacks(pushOptions); GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks(); + var gitPushOptions = pushOptionsWrapper.Options; + gitPushOptions.PackbuilderDegreeOfParallelism = pushOptions.PackbuilderDegreeOfParallelism; + gitPushOptions.RemoteCallbacks = gitCallbacks; + gitPushOptions.ProxyOptions = new GitProxyOptions { Version = 1 }; + + // If there are custom headers, create a managed string array. + if (pushOptions.CustomHeaders != null && pushOptions.CustomHeaders.Length > 0) + { + gitPushOptions.CustomHeaders = GitStrArrayManaged.BuildFrom(pushOptions.CustomHeaders); + } + Proxy.git_remote_push(remoteHandle, pushRefSpecs, - new GitPushOptions() - { - PackbuilderDegreeOfParallelism = pushOptions.PackbuilderDegreeOfParallelism, - RemoteCallbacks = gitCallbacks, - ProxyOptions = new GitProxyOptions { Version = 1 }, - }); + gitPushOptions); } } diff --git a/LibGit2Sharp/PushOptions.cs b/LibGit2Sharp/PushOptions.cs index 99c65dd8b..f7d37eba9 100644 --- a/LibGit2Sharp/PushOptions.cs +++ b/LibGit2Sharp/PushOptions.cs @@ -51,5 +51,25 @@ public sealed class PushOptions /// information about what updates will be performed. /// public PrePushHandler OnNegotiationCompletedBeforePush { get; set; } + + /// + /// Get/Set the custom headers. + /// + /// This allows you to set custom headers (e.g. X-Forwarded-For, + /// X-Request-Id, etc), + /// + /// + /// + /// Libgit2 sets some headers for HTTP requests (User-Agent, Host, + /// Accept, Content-Type, Transfer-Encoding, Content-Length, Accept) that + /// cannot be overriden. + /// + /// + /// var pushOptions - new PushOptions() { + /// CustomHeaders = new String[] {"X-Request-Id: 12345"} + /// }; + /// + /// The custom headers string array + public string[] CustomHeaders { get; set; } } }