Unverified Commit 59647e1a by Erik Aigner Committed by Etienne Samson

remote: add callback to resolve URLs before connecting

Since libssh2 doesn't read host configuration from the config file,
this callback can be used to hand over URL resolving to the client
without touching the SSH implementation itself.
parent 4aa36ff2
......@@ -475,6 +475,20 @@ typedef int GIT_CALLBACK(git_push_negotiation)(const git_push_update **updates,
typedef int GIT_CALLBACK(git_push_update_reference_cb)(const char *refname, const char *status, void *data);
/**
* Callback to resolve URLs before connecting to remote
*
* If you return GIT_PASSTHROUGH, you don't need to write anything to
* url_resolved.
*
* @param url_resolved The buffer to write the resolved URL to
* @param url The URL to resolve
* @param direction GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
* @param payload Payload provided by the caller
* @return 0 on success, GIT_PASSTHROUGH or an error
*/
typedef int GIT_CALLBACK(git_url_resolve_cb)(git_buf *url_resolved, const char *url, int direction, void *payload);
/**
* The callback settings structure
*
* Set the callbacks to be called by the remote when informing the user
......@@ -562,6 +576,12 @@ struct git_remote_callbacks {
* as the last parameter.
*/
void *payload;
/**
* Resolve URL before connecting to remote.
* The returned URL will be used to connect to the remote instead.
*/
git_url_resolve_cb resolve_url;
};
#define GIT_REMOTE_CALLBACKS_VERSION 1
......
......@@ -670,21 +670,44 @@ int git_remote_set_pushurl(git_repository *repo, const char *remote, const char*
return set_url(repo, remote, CONFIG_PUSHURL_FMT, url);
}
const char* git_remote__urlfordirection(git_remote *remote, int direction)
static int resolve_url(git_buf *resolved_url, const char *url, int direction, const git_remote_callbacks *callbacks)
{
assert(remote);
int status;
if (callbacks && callbacks->resolve_url) {
git_buf_clear(resolved_url);
status = callbacks->resolve_url(resolved_url, url, direction, callbacks->payload);
if (status != GIT_PASSTHROUGH) {
git_error_set_after_callback_function(status, "git_resolve_url_cb");
git_buf_sanitize(resolved_url);
return status;
}
}
return git_buf_sets(resolved_url, url);
}
int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks)
{
const char *url = NULL;
assert(remote);
assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
if (direction == GIT_DIRECTION_FETCH) {
return remote->url;
url = remote->url;
} else if (direction == GIT_DIRECTION_PUSH) {
url = remote->pushurl ? remote->pushurl : remote->url;
}
if (direction == GIT_DIRECTION_PUSH) {
return remote->pushurl ? remote->pushurl : remote->url;
if (!url) {
git_error_set(GIT_ERROR_INVALID,
"malformed remote '%s' - missing %s URL",
remote->name ? remote->name : "(anonymous)",
direction == GIT_DIRECTION_FETCH ? "fetch" : "push");
return GIT_EINVALID;
}
return NULL;
return resolve_url(url_out, url, direction, callbacks);
}
int set_transport_callbacks(git_transport *t, const git_remote_callbacks *cbs)
......@@ -707,7 +730,7 @@ static int set_transport_custom_headers(git_transport *t, const git_strarray *cu
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn)
{
git_transport *t;
const char *url;
git_buf url = GIT_BUF_INIT;
int flags = GIT_TRANSPORTFLAGS_NONE;
int error;
void *payload = NULL;
......@@ -728,39 +751,38 @@ int git_remote__connect(git_remote *remote, git_direction direction, const git_r
t = remote->transport;
url = git_remote__urlfordirection(remote, direction);
if (url == NULL) {
git_error_set(GIT_ERROR_INVALID,
"Malformed remote '%s' - missing %s URL",
remote->name ? remote->name : "(anonymous)",
direction == GIT_DIRECTION_FETCH ? "fetch" : "push");
return -1;
}
if ((error = git_remote__urlfordirection(&url, remote, direction, callbacks)) < 0)
goto on_error;
/* If we don't have a transport object yet, and the caller specified a
* custom transport factory, use that */
if (!t && transport &&
(error = transport(&t, remote, payload)) < 0)
return error;
goto on_error;
/* If we still don't have a transport, then use the global
* transport registrations which map URI schemes to transport factories */
if (!t && (error = git_transport_new(&t, remote, url)) < 0)
return error;
if (!t && (error = git_transport_new(&t, remote, url.ptr)) < 0)
goto on_error;
if ((error = set_transport_custom_headers(t, conn->custom_headers)) != 0)
goto on_error;
if ((error = set_transport_callbacks(t, callbacks)) < 0 ||
(error = t->connect(t, url, credentials, payload, conn->proxy, direction, flags)) != 0)
(error = t->connect(t, url.ptr, credentials, payload, conn->proxy, direction, flags)) != 0)
goto on_error;
remote->transport = t;
git_buf_dispose(&url);
return 0;
on_error:
t->free(t);
if (t)
t->free(t);
git_buf_dispose(&url);
if (t == remote->transport)
remote->transport = NULL;
......
......@@ -45,7 +45,7 @@ typedef struct git_remote_connection_opts {
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn);
const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks);
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url);
git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname);
......
......@@ -28,28 +28,97 @@ void test_network_remote_remotes__cleanup(void)
void test_network_remote_remotes__parsing(void)
{
git_buf url = GIT_BUF_INIT;
git_remote *_remote2 = NULL;
cl_assert_equal_s(git_remote_name(_remote), "test");
cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
cl_assert(git_remote_pushurl(_remote) == NULL);
cl_assert_equal_s(git_remote__urlfordirection(_remote, GIT_DIRECTION_FETCH),
"git://github.com/libgit2/libgit2");
cl_assert_equal_s(git_remote__urlfordirection(_remote, GIT_DIRECTION_PUSH),
"git://github.com/libgit2/libgit2");
cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, NULL));
cl_assert_equal_s(url.ptr, "git://github.com/libgit2/libgit2");
cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, NULL));
cl_assert_equal_s(url.ptr, "git://github.com/libgit2/libgit2");
cl_git_pass(git_remote_lookup(&_remote2, _repo, "test_with_pushurl"));
cl_assert_equal_s(git_remote_name(_remote2), "test_with_pushurl");
cl_assert_equal_s(git_remote_url(_remote2), "git://github.com/libgit2/fetchlibgit2");
cl_assert_equal_s(git_remote_pushurl(_remote2), "git://github.com/libgit2/pushlibgit2");
cl_assert_equal_s(git_remote__urlfordirection(_remote2, GIT_DIRECTION_FETCH),
"git://github.com/libgit2/fetchlibgit2");
cl_assert_equal_s(git_remote__urlfordirection(_remote2, GIT_DIRECTION_PUSH),
"git://github.com/libgit2/pushlibgit2");
cl_git_pass(git_remote__urlfordirection(&url, _remote2, GIT_DIRECTION_FETCH, NULL));
cl_assert_equal_s(url.ptr, "git://github.com/libgit2/fetchlibgit2");
cl_git_pass(git_remote__urlfordirection(&url, _remote2, GIT_DIRECTION_PUSH, NULL));
cl_assert_equal_s(url.ptr, "git://github.com/libgit2/pushlibgit2");
git_remote_free(_remote2);
git_buf_dispose(&url);
}
static int urlresolve_callback(git_buf *url_resolved, const char *url, int direction, void *payload)
{
cl_assert(strcmp(url, "git://github.com/libgit2/libgit2") == 0);
cl_assert(strcmp(payload, "payload") == 0);
cl_assert(url_resolved->size == 0);
if (direction == GIT_DIRECTION_PUSH)
git_buf_sets(url_resolved, "pushresolve");
if (direction == GIT_DIRECTION_FETCH)
git_buf_sets(url_resolved, "fetchresolve");
return GIT_OK;
}
void test_network_remote_remotes__urlresolve(void)
{
git_buf url = GIT_BUF_INIT;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
callbacks.resolve_url = urlresolve_callback;
callbacks.payload = "payload";
cl_assert_equal_s(git_remote_name(_remote), "test");
cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
cl_assert(git_remote_pushurl(_remote) == NULL);
cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, &callbacks));
cl_assert_equal_s(url.ptr, "fetchresolve");
cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, &callbacks));
cl_assert_equal_s(url.ptr, "pushresolve");
git_buf_dispose(&url);
}
static int urlresolve_passthrough_callback(git_buf *url_resolved, const char *url, int direction, void *payload)
{
GIT_UNUSED(url_resolved);
GIT_UNUSED(url);
GIT_UNUSED(direction);
GIT_UNUSED(payload);
return GIT_PASSTHROUGH;
}
void test_network_remote_remotes__urlresolve_passthrough(void)
{
git_buf url = GIT_BUF_INIT;
const char *orig_url = "git://github.com/libgit2/libgit2";
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
callbacks.resolve_url = urlresolve_passthrough_callback;
cl_assert_equal_s(git_remote_name(_remote), "test");
cl_assert_equal_s(git_remote_url(_remote), orig_url);
cl_assert(git_remote_pushurl(_remote) == NULL);
cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, &callbacks));
cl_assert_equal_s(url.ptr, orig_url);
cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, &callbacks));
cl_assert_equal_s(url.ptr, orig_url);
git_buf_dispose(&url);
}
void test_network_remote_remotes__pushurl(void)
......
......@@ -12,7 +12,7 @@ extern const git_oid OID_ZERO;
* @param data pointer to a record_callbacks_data instance
*/
#define RECORD_CALLBACKS_INIT(data) \
{ GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, NULL, NULL, NULL, NULL, NULL, data }
{ GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, NULL, NULL, NULL, NULL, NULL, data, NULL }
typedef struct {
char *name;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment