mirror of
https://github.com/libgit2/libgit2.git
synced 2026-06-22 06:26:26 +00:00
remote: introduce follow_redirects connect option
Give callers the ability to select how to handle redirects - either supporting redirects during the initial connection (so that, for example, `git.example.com/repo` can redirect to `github.com/example/repo`) or all/no redirects. This is for compatibility with git.
This commit is contained in:
@@ -101,7 +101,7 @@ if [ -z "$SKIP_PROXY_TESTS" ]; then
|
||||
java -jar poxyproxy.jar --address 127.0.0.1 --port 8090 --credentials foo:bar --auth-type ntlm --quiet &
|
||||
fi
|
||||
|
||||
if [ -z "$SKIP_NTLM_TESTS" ]; then
|
||||
if [ -z "$SKIP_NTLM_TESTS" -o -z "$SKIP_ONLINE_TESTS" ]; then
|
||||
curl --location --silent --show-error https://github.com/ethomson/poxygit/releases/download/v0.5.1/poxygit-0.5.1.jar >poxygit.jar
|
||||
|
||||
echo ""
|
||||
@@ -188,7 +188,11 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
|
||||
echo "## Running (online) tests"
|
||||
echo "##############################################################################"
|
||||
|
||||
export GITTEST_REMOTE_REDIRECT_INITIAL="http://localhost:9000/initial-redirect/libgit2/TestGitRepository"
|
||||
export GITTEST_REMOTE_REDIRECT_SUBSEQUENT="http://localhost:9000/subsequent-redirect/libgit2/TestGitRepository"
|
||||
run_test online
|
||||
unset GITTEST_REMOTE_REDIRECT_INITIAL
|
||||
unset GITTEST_REMOTE_REDIRECT_SUBSEQUENT
|
||||
|
||||
# Run the online tests that immutably change global state separately
|
||||
# to avoid polluting the test environment.
|
||||
|
||||
@@ -41,6 +41,30 @@ GIT_EXTERN(int) git_remote_create(
|
||||
const char *name,
|
||||
const char *url);
|
||||
|
||||
/**
|
||||
* Remote redirection settings; whether redirects to another host
|
||||
* are permitted. By default, git will follow a redirect on the
|
||||
* initial request (`/info/refs`), but not subsequent requests.
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* Do not follow any off-site redirects at any stage of
|
||||
* the fetch or push.
|
||||
*/
|
||||
GIT_REMOTE_REDIRECT_NONE = (1 << 0),
|
||||
|
||||
/**
|
||||
* Allow off-site redirects only upon the initial request.
|
||||
* This is the default.
|
||||
*/
|
||||
GIT_REMOTE_REDIRECT_INITIAL = (1 << 1),
|
||||
|
||||
/**
|
||||
* Allow redirects at any stage in the fetch or push.
|
||||
*/
|
||||
GIT_REMOTE_REDIRECT_ALL = (1 << 2)
|
||||
} git_remote_redirect_t;
|
||||
|
||||
/**
|
||||
* Remote creation options flags
|
||||
*/
|
||||
@@ -717,6 +741,13 @@ typedef struct {
|
||||
*/
|
||||
git_proxy_options proxy_opts;
|
||||
|
||||
/**
|
||||
* Whether to allow off-site redirects. If this is not
|
||||
* specified, the `http.followRedirects` configuration setting
|
||||
* will be consulted.
|
||||
*/
|
||||
git_remote_redirect_t follow_redirects;
|
||||
|
||||
/**
|
||||
* Extra headers for this fetch operation
|
||||
*/
|
||||
@@ -768,6 +799,13 @@ typedef struct {
|
||||
*/
|
||||
git_proxy_options proxy_opts;
|
||||
|
||||
/**
|
||||
* Whether to allow off-site redirects. If this is not
|
||||
* specified, the `http.followRedirects` configuration setting
|
||||
* will be consulted.
|
||||
*/
|
||||
git_remote_redirect_t follow_redirects;
|
||||
|
||||
/**
|
||||
* Extra headers for this push operation
|
||||
*/
|
||||
@@ -807,6 +845,13 @@ typedef struct {
|
||||
/** HTTP Proxy settings */
|
||||
git_proxy_options proxy_opts;
|
||||
|
||||
/**
|
||||
* Whether to allow off-site redirects. If this is not
|
||||
* specified, the `http.followRedirects` configuration setting
|
||||
* will be consulted.
|
||||
*/
|
||||
git_remote_redirect_t follow_redirects;
|
||||
|
||||
/** Extra HTTP headers to use in this connection */
|
||||
git_strarray custom_headers;
|
||||
} git_remote_connect_options;
|
||||
|
||||
@@ -1171,6 +1171,7 @@ static int ls_to_vector(git_vector *out, git_remote *remote)
|
||||
(out)->callbacks = (in)->callbacks; \
|
||||
(out)->proxy_opts = (in)->proxy_opts; \
|
||||
(out)->custom_headers = (in)->custom_headers; \
|
||||
(out)->follow_redirects = (in)->follow_redirects; \
|
||||
}
|
||||
|
||||
GIT_INLINE(int) connect_opts_from_fetch_opts(
|
||||
|
||||
@@ -38,7 +38,8 @@ typedef struct {
|
||||
const char *url;
|
||||
const char *request_type;
|
||||
const char *response_type;
|
||||
unsigned chunked : 1;
|
||||
unsigned int initial : 1,
|
||||
chunked : 1;
|
||||
} http_service;
|
||||
|
||||
typedef struct {
|
||||
@@ -70,24 +71,28 @@ static const http_service upload_pack_ls_service = {
|
||||
GIT_HTTP_METHOD_GET, "/info/refs?service=git-upload-pack",
|
||||
NULL,
|
||||
"application/x-git-upload-pack-advertisement",
|
||||
1,
|
||||
0
|
||||
};
|
||||
static const http_service upload_pack_service = {
|
||||
GIT_HTTP_METHOD_POST, "/git-upload-pack",
|
||||
"application/x-git-upload-pack-request",
|
||||
"application/x-git-upload-pack-result",
|
||||
0,
|
||||
0
|
||||
};
|
||||
static const http_service receive_pack_ls_service = {
|
||||
GIT_HTTP_METHOD_GET, "/info/refs?service=git-receive-pack",
|
||||
NULL,
|
||||
"application/x-git-receive-pack-advertisement",
|
||||
1,
|
||||
0
|
||||
};
|
||||
static const http_service receive_pack_service = {
|
||||
GIT_HTTP_METHOD_POST, "/git-receive-pack",
|
||||
"application/x-git-receive-pack-request",
|
||||
"application/x-git-receive-pack-result",
|
||||
0,
|
||||
1
|
||||
};
|
||||
|
||||
@@ -215,6 +220,19 @@ GIT_INLINE(int) handle_proxy_auth(
|
||||
connect_opts->proxy_opts.payload);
|
||||
}
|
||||
|
||||
static bool allow_redirect(http_stream *stream)
|
||||
{
|
||||
http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
|
||||
|
||||
switch (transport->owner->connect_opts.follow_redirects) {
|
||||
case GIT_REMOTE_REDIRECT_INITIAL:
|
||||
return (stream->service->initial == 1);
|
||||
case GIT_REMOTE_REDIRECT_ALL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_response(
|
||||
bool *complete,
|
||||
@@ -233,7 +251,7 @@ static int handle_response(
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (git_net_url_apply_redirect(&transport->server.url, response->location, false, stream->service->url) < 0) {
|
||||
if (git_net_url_apply_redirect(&transport->server.url, response->location, allow_redirect(stream), stream->service->url) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1189,8 +1189,10 @@ replay:
|
||||
winhttp_stream_close(s);
|
||||
|
||||
if (!git__prefixcmp_icase(location8, prefix_https)) {
|
||||
bool follow = (t->owner->connect_opts.follow_redirects != GIT_REMOTE_REDIRECT_NONE);
|
||||
|
||||
/* Upgrade to secure connection; disconnect and start over */
|
||||
if (git_net_url_apply_redirect(&t->server.url, location8, false, s->service_url) < 0) {
|
||||
if (git_net_url_apply_redirect(&t->server.url, location8, follow, s->service_url) < 0) {
|
||||
git__free(location8);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ static char *_remote_proxy_user = NULL;
|
||||
static char *_remote_proxy_pass = NULL;
|
||||
static char *_remote_proxy_selfsigned = NULL;
|
||||
static char *_remote_expectcontinue = NULL;
|
||||
static char *_remote_redirect_initial = NULL;
|
||||
static char *_remote_redirect_subsequent = NULL;
|
||||
|
||||
static int _orig_proxies_need_reset = 0;
|
||||
static char *_orig_http_proxy = NULL;
|
||||
@@ -78,6 +80,8 @@ void test_online_clone__initialize(void)
|
||||
_remote_proxy_pass = cl_getenv("GITTEST_REMOTE_PROXY_PASS");
|
||||
_remote_proxy_selfsigned = cl_getenv("GITTEST_REMOTE_PROXY_SELFSIGNED");
|
||||
_remote_expectcontinue = cl_getenv("GITTEST_REMOTE_EXPECTCONTINUE");
|
||||
_remote_redirect_initial = cl_getenv("GITTEST_REMOTE_REDIRECT_INITIAL");
|
||||
_remote_redirect_subsequent = cl_getenv("GITTEST_REMOTE_REDIRECT_SUBSEQUENT");
|
||||
|
||||
if (_remote_expectcontinue)
|
||||
git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 1);
|
||||
@@ -92,6 +96,8 @@ void test_online_clone__cleanup(void)
|
||||
g_repo = NULL;
|
||||
}
|
||||
cl_fixture_cleanup("./foo");
|
||||
cl_fixture_cleanup("./initial");
|
||||
cl_fixture_cleanup("./subsequent");
|
||||
|
||||
git__free(_remote_url);
|
||||
git__free(_remote_user);
|
||||
@@ -107,6 +113,8 @@ void test_online_clone__cleanup(void)
|
||||
git__free(_remote_proxy_pass);
|
||||
git__free(_remote_proxy_selfsigned);
|
||||
git__free(_remote_expectcontinue);
|
||||
git__free(_remote_redirect_initial);
|
||||
git__free(_remote_redirect_subsequent);
|
||||
|
||||
if (_orig_proxies_need_reset) {
|
||||
cl_setenv("HTTP_PROXY", _orig_http_proxy);
|
||||
@@ -938,3 +946,59 @@ void test_online_clone__path_whitespace(void)
|
||||
cl_git_pass(git_clone(&g_repo, "https://libgit2@dev.azure.com/libgit2/test/_git/spaces%20in%20the%20name", "./foo", &g_options));
|
||||
cl_assert(git_fs_path_exists("./foo/master.txt"));
|
||||
}
|
||||
|
||||
void test_online_clone__redirect_default_succeeds_for_initial(void)
|
||||
{
|
||||
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
|
||||
|
||||
if (!_remote_redirect_initial || !_remote_redirect_subsequent)
|
||||
cl_skip();
|
||||
|
||||
cl_git_pass(git_clone(&g_repo, _remote_redirect_initial, "./initial", &options));
|
||||
}
|
||||
|
||||
void test_online_clone__redirect_default_fails_for_subsequent(void)
|
||||
{
|
||||
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
|
||||
|
||||
if (!_remote_redirect_initial || !_remote_redirect_subsequent)
|
||||
cl_skip();
|
||||
|
||||
cl_git_fail(git_clone(&g_repo, _remote_redirect_subsequent, "./fail", &options));
|
||||
}
|
||||
|
||||
void test_online_clone__redirect_none(void)
|
||||
{
|
||||
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
|
||||
|
||||
if (!_remote_redirect_initial)
|
||||
cl_skip();
|
||||
|
||||
options.fetch_opts.follow_redirects = GIT_REMOTE_REDIRECT_NONE;
|
||||
|
||||
cl_git_fail(git_clone(&g_repo, _remote_redirect_initial, "./fail", &options));
|
||||
}
|
||||
|
||||
void test_online_clone__redirect_initial_succeeds_for_initial(void)
|
||||
{
|
||||
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
|
||||
|
||||
if (!_remote_redirect_initial || !_remote_redirect_subsequent)
|
||||
cl_skip();
|
||||
|
||||
options.fetch_opts.follow_redirects = GIT_REMOTE_REDIRECT_INITIAL;
|
||||
|
||||
cl_git_pass(git_clone(&g_repo, _remote_redirect_initial, "./initial", &options));
|
||||
}
|
||||
|
||||
void test_online_clone__redirect_initial_fails_for_subsequent(void)
|
||||
{
|
||||
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
|
||||
|
||||
if (!_remote_redirect_initial || !_remote_redirect_subsequent)
|
||||
cl_skip();
|
||||
|
||||
options.fetch_opts.follow_redirects = GIT_REMOTE_REDIRECT_INITIAL;
|
||||
|
||||
cl_git_fail(git_clone(&g_repo, _remote_redirect_subsequent, "./fail", &options));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user