Skip to content

Wireup http custom headers in fetch options #1542

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions LibGit2Sharp.Tests/CloneFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -557,5 +557,54 @@ public void CanCancelRecursiveClone()

}
}

[Fact]
public void CannotCloneWithForbiddenCustomHeaders()
{
var scd = BuildSelfCleaningDirectory();

const string url = "https://github.com/libgit2/TestGitRepository";

const string knownHeader = "User-Agent: mygit-201";
var cloneOptions = new CloneOptions()
{
FetchOptions = new FetchOptions { CustomHeaders = new String[] { knownHeader } }
};

Assert.Throws<LibGit2SharpException>(() => Repository.Clone(url, scd.DirectoryPath, cloneOptions));
}

[Fact]
public void CannotCloneWithMalformedCustomHeaders()
{
var scd = BuildSelfCleaningDirectory();

const string url = "https://github.com/libgit2/TestGitRepository";

const string knownHeader = "hello world";
var cloneOptions = new CloneOptions()
{
FetchOptions = new FetchOptions { CustomHeaders = new String[] { knownHeader } }
};

Assert.Throws<LibGit2SharpException>(() => Repository.Clone(url, scd.DirectoryPath, cloneOptions));
}

[Fact]
public void CanCloneWithCustomHeaders()
{
var scd = BuildSelfCleaningDirectory();

const string url = "https://github.com/libgit2/TestGitRepository";

const string knownHeader = "X-Hello: world";
var cloneOptions = new CloneOptions()
{
FetchOptions = new FetchOptions { CustomHeaders = new String[] { knownHeader } }
};

var clonedRepoPath = Repository.Clone(url, scd.DirectoryPath, cloneOptions);
Assert.True(Directory.Exists(clonedRepoPath));
}
}
}
52 changes: 52 additions & 0 deletions LibGit2Sharp.Tests/FetchFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,5 +239,57 @@ public void FetchHonorsTheFetchPruneConfigurationEntry()
Assert.Equal(4, clonedRepo.Branches.Count(b => b.IsRemote));
}
}

[Fact]
public void CannotFetchWithForbiddenCustomHeaders()
{
var scd = BuildSelfCleaningDirectory();

const string url = "https://github.com/libgit2/TestGitRepository";

string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath);

const string knownHeader = "User-Agent: mygit-201";
var options = new FetchOptions { CustomHeaders = new String[] { knownHeader } };
using (var repo = new Repository(clonedRepoPath))
{
Assert.Throws<LibGit2SharpException>(() => Commands.Fetch(repo, "origin", new string[0], options, null));
}
}

[Fact]
public void CanFetchWithCustomHeaders()
{
var scd = BuildSelfCleaningDirectory();

const string url = "https://github.com/libgit2/TestGitRepository";

string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath);

const string knownHeader = "X-Hello: mygit-201";
var options = new FetchOptions { CustomHeaders = new String[] { knownHeader } };
using (var repo = new Repository(clonedRepoPath))
{
Commands.Fetch(repo, "origin", new string[0], options, null);
}
}

[Fact]
public void CannotFetchWithMalformedCustomHeaders()
{
var scd = BuildSelfCleaningDirectory();

const string url = "https://github.com/libgit2/TestGitRepository";

string clonedRepoPath = Repository.Clone(url, scd.DirectoryPath);

const string knownHeader = "Hello world";
var options = new FetchOptions { CustomHeaders = new String[] { knownHeader } };
using (var repo = new Repository(clonedRepoPath))
{
Assert.Throws<LibGit2SharpException>(() => Commands.Fetch(repo, "origin", new string[0], options, null));
}
}

}
}
5 changes: 5 additions & 0 deletions LibGit2Sharp/CloneOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public CloneOptions()
/// </summary>
public CheckoutProgressHandler OnCheckoutProgress { get; set; }

/// <summary>
/// Gets or sets the fetch options.
/// </summary>
public FetchOptions FetchOptions { get; set; }

#region IConvertableToGitCheckoutOpts

CheckoutCallbacks IConvertableToGitCheckoutOpts.GenerateCallbacks()
Expand Down
14 changes: 9 additions & 5 deletions LibGit2Sharp/Commands/Fetch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public static void Fetch(Repository repository, string remote, IEnumerable<strin

options = options ?? new FetchOptions();
using (var remoteHandle = RemoteFromNameOrUrl(repository.Handle, remote))
using (var fetchOptionsWrapper = new GitFetchOptionsWrapper())
{

var callbacks = new RemoteCallbacks(options);
Expand All @@ -51,11 +52,9 @@ public static void Fetch(Repository repository, string remote, IEnumerable<strin
//
// Also, if GitRemoteCallbacks were a class instead of a struct, we would need to guard against
// GC occuring in between setting the remote callbacks and actual usage in one of the functions afterwords.
var fetchOptions = new GitFetchOptions
{
RemoteCallbacks = gitCallbacks,
download_tags = Proxy.git_remote_autotag(remoteHandle),
};
var fetchOptions = fetchOptionsWrapper.Options;
fetchOptions.RemoteCallbacks = gitCallbacks;
fetchOptions.download_tags = Proxy.git_remote_autotag(remoteHandle);

if (options.TagFetchMode.HasValue)
{
Expand All @@ -71,6 +70,11 @@ public static void Fetch(Repository repository, string remote, IEnumerable<strin
fetchOptions.Prune = FetchPruneStrategy.FromConfigurationOrDefault;
}

if (options.CustomHeaders != null && options.CustomHeaders.Length > 0)
{
fetchOptions.CustomHeaders = GitStrArrayManaged.BuildFrom(options.CustomHeaders);
}

fetchOptions.ProxyOptions = new GitProxyOptions { Version = 1 };

Proxy.git_remote_fetch(remoteHandle, refspecs, fetchOptions, logMessage);
Expand Down
2 changes: 1 addition & 1 deletion LibGit2Sharp/Core/GitFetchOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ internal class GitFetchOptions
public bool UpdateFetchHead = true;
public TagFetchMode download_tags;
public GitProxyOptions ProxyOptions;
public GitStrArrayManaged custom_headers;
public GitStrArrayManaged CustomHeaders;
}
}
36 changes: 36 additions & 0 deletions LibGit2Sharp/Core/GitFetchOptionsWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;

namespace LibGit2Sharp.Core
{
/// <summary>
/// Git fetch options wrapper. Disposable wrapper for GitFetchOptions
/// </summary>
internal class GitFetchOptionsWrapper : IDisposable
{
public GitFetchOptionsWrapper() : this(new GitFetchOptions()) { }

public GitFetchOptionsWrapper(GitFetchOptions fetchOptions)
{
this.Options = fetchOptions;
}

public GitFetchOptions 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
}
}
21 changes: 21 additions & 0 deletions LibGit2Sharp/FetchOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,26 @@ public sealed class FetchOptions : FetchOptionsBase
/// </para>
/// </summary>
public bool? Prune { get; set; }

/// <summary>
/// Get/Set the custom headers.
///
/// <para>
/// This allows you to set custom headers (e.g. X-Forwarded-For,
/// X-Request-Id, etc),
/// </para>
/// </summary>
/// <remarks>
/// Libgit2 sets some headers for HTTP requests (User-Agent, Host,
/// Accept, Content-Type, Transfer-Encoding, Content-Length, Accept) that
/// cannot be overriden.
/// </remarks>
/// <example>
/// var fetchOptions - new FetchOptions() {
/// CustomHeaders = new String[] {"X-Request-Id: 12345"}
/// };
/// </example>
/// <value>The custom headers string array</value>
public string[] CustomHeaders { get; set; }
}
}
17 changes: 11 additions & 6 deletions LibGit2Sharp/Repository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -703,21 +703,26 @@ public static string Clone(string sourceUrl, string workdirPath,
throw new UserCancelledException("Clone cancelled by the user.");
}

using (GitCheckoutOptsWrapper checkoutOptionsWrapper = new GitCheckoutOptsWrapper(options))
using (var checkoutOptionsWrapper = new GitCheckoutOptsWrapper(options))
using (var fetchOptionsWrapper = new GitFetchOptionsWrapper())
{
var gitCheckoutOptions = checkoutOptionsWrapper.Options;

var remoteCallbacks = new RemoteCallbacks(options);
var gitRemoteCallbacks = remoteCallbacks.GenerateCallbacks();

var gitProxyOptions = new GitProxyOptions { Version = 1 };
var gitFetchOptions = fetchOptionsWrapper.Options;
gitFetchOptions.ProxyOptions = new GitProxyOptions { Version = 1 };
gitFetchOptions.RemoteCallbacks = new RemoteCallbacks(options).GenerateCallbacks();
if (options.FetchOptions != null && options.FetchOptions.CustomHeaders != null)
{
gitFetchOptions.CustomHeaders =
GitStrArrayManaged.BuildFrom(options.FetchOptions.CustomHeaders);
}

var cloneOpts = new GitCloneOptions
{
Version = 1,
Bare = options.IsBare ? 1 : 0,
CheckoutOpts = gitCheckoutOptions,
FetchOpts = new GitFetchOptions { ProxyOptions = gitProxyOptions, RemoteCallbacks = gitRemoteCallbacks },
FetchOpts = gitFetchOptions,
};

string clonedRepoPath;
Expand Down