diff options
author | Edward Thomson <ethomson@github.com> | 2016-04-19 19:48:52 -0400 |
---|---|---|
committer | Edward Thomson <ethomson@github.com> | 2016-04-19 19:48:52 -0400 |
commit | 1dc449105b329ea4f8ea9982bc2da869d231c04a (patch) | |
tree | 3dede94adc9297468b3f5b2294b300b028a0b34f | |
parent | 95fbc81dafd64400d51637a27ecd49de5ea63145 (diff) | |
parent | 2638df771172a18dc5da89f039076fcc05ceb4ac (diff) | |
download | libgit2-1dc449105b329ea4f8ea9982bc2da869d231c04a.tar.gz |
Merge pull request #3110 from libgit2/cmn/proxy-config
Proxy configuration
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | appveyor.yml | 11 | ||||
-rw-r--r-- | examples/network/ls-remote.c | 2 | ||||
-rw-r--r-- | include/git2.h | 1 | ||||
-rw-r--r-- | include/git2/proxy.h | 92 | ||||
-rw-r--r-- | include/git2/remote.h | 19 | ||||
-rw-r--r-- | include/git2/sys/remote.h | 16 | ||||
-rw-r--r-- | include/git2/sys/stream.h | 3 | ||||
-rw-r--r-- | include/git2/sys/transport.h | 4 | ||||
-rwxr-xr-x | script/cibuild.sh | 15 | ||||
-rw-r--r-- | src/curl_stream.c | 95 | ||||
-rw-r--r-- | src/netops.c | 22 | ||||
-rw-r--r-- | src/openssl_stream.c | 4 | ||||
-rw-r--r-- | src/proxy.c | 32 | ||||
-rw-r--r-- | src/proxy.h | 14 | ||||
-rw-r--r-- | src/push.c | 2 | ||||
-rw-r--r-- | src/remote.c | 26 | ||||
-rw-r--r-- | src/stream.h | 4 | ||||
-rw-r--r-- | src/transports/http.c | 41 | ||||
-rw-r--r-- | src/transports/local.c | 5 | ||||
-rw-r--r-- | src/transports/smart.c | 6 | ||||
-rw-r--r-- | src/transports/smart.h | 1 | ||||
-rw-r--r-- | src/transports/winhttp.c | 141 | ||||
-rw-r--r-- | tests/network/remote/defaultbranch.c | 6 | ||||
-rw-r--r-- | tests/network/remote/local.c | 8 | ||||
-rw-r--r-- | tests/network/remote/remotes.c | 6 | ||||
-rw-r--r-- | tests/network/urlparse.c | 9 | ||||
-rw-r--r-- | tests/online/clone.c | 44 | ||||
-rw-r--r-- | tests/online/fetch.c | 12 | ||||
-rw-r--r-- | tests/online/push.c | 2 |
30 files changed, 574 insertions, 71 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 17b5fba7b..ba3a5184a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -690,6 +690,8 @@ IF (BUILD_CLAR) # Add a test target which runs the cred callback tests, to be # called after setting the url and user ADD_TEST(libgit2_clar-cred_callback libgit2_clar -v -sonline::clone::cred_callback) + ADD_TEST(libgit2_clar-proxy_credentials_in_url libgit2_clar -v -sonline::clone::proxy_credentials_in_url) + ADD_TEST(libgit2_clar-proxy_credentials_request libgit2_clar -v -sonline::clone::proxy_credentials_request) ENDIF () IF (TAGS) diff --git a/appveyor.yml b/appveyor.yml index 5ba8aaabd..4f51aa89c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,6 +19,7 @@ environment: cache: - i686-4.9.2-release-win32-sjlj-rt_v3-rev1.7z - x86_64-4.9.2-release-win32-seh-rt_v3-rev1.7z + build_script: - ps: | mkdir build @@ -32,7 +33,17 @@ build_script: test_script: - ps: | $ErrorActionPreference="Stop" + Invoke-WebRequest https://github.com/ethomson/poxyproxy/releases/download/v0.1.0/poxyproxy-0.1.0.jar -OutFile poxyproxy.jar + # Run this early so we know it's ready by the time we need it + $proxyJob = Start-Job { java -jar $Env:APPVEYOR_BUILD_FOLDER\build\poxyproxy.jar -d --port 8080 --credentials foo:bar } ctest -V -R libgit2_clar $env:GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent" $env:GITTEST_REMOTE_USER="libgit2test" ctest -V -R libgit2_clar-cred_callback + Receive-Job -Job $proxyJob + $env:GITTEST_REMOTE_PROXY_URL = "http://foo:bar@localhost:8080" + ctest -V -R libgit2_clar-proxy_credentials_in_url + $env:GITTEST_REMOTE_PROXY_URL = "http://localhost:8080" + $env:GITTEST_REMOTE_PROXY_USER = "foo" + $env:GITTEST_REMOTE_PROXY_PASS = "bar" + ctest -V -R libgit2_clar-proxy_credentials_request diff --git a/examples/network/ls-remote.c b/examples/network/ls-remote.c index c9da79f5f..9329da5d9 100644 --- a/examples/network/ls-remote.c +++ b/examples/network/ls-remote.c @@ -26,7 +26,7 @@ static int use_remote(git_repository *repo, char *name) */ callbacks.credentials = cred_acquire_cb; - error = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, NULL); + error = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, NULL, NULL); if (error < 0) goto cleanup; diff --git a/include/git2.h b/include/git2.h index ac4a63160..bc4333cc9 100644 --- a/include/git2.h +++ b/include/git2.h @@ -40,6 +40,7 @@ #include "git2/pack.h" #include "git2/patch.h" #include "git2/pathspec.h" +#include "git2/proxy.h" #include "git2/rebase.h" #include "git2/refdb.h" #include "git2/reflog.h" diff --git a/include/git2/proxy.h b/include/git2/proxy.h new file mode 100644 index 000000000..dcd615633 --- /dev/null +++ b/include/git2/proxy.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_git_proxy_h__ +#define INCLUDE_git_proxy_h__ + +#include "common.h" +#include "transport.h" + +GIT_BEGIN_DECL + +/** + * The type of proxy to use. + */ +typedef enum { + /** + * Do not attempt to connect through a proxy + * + * If built against lbicurl, it itself may attempt to connect + * to a proxy if the environment variables specify it. + */ + GIT_PROXY_NONE, + /** + * Try to auto-detect the proxy from the git configuration. + */ + GIT_PROXY_AUTO, + /** + * Connect via the URL given in the options + */ + GIT_PROXY_SPECIFIED, +} git_proxy_t; + +/** + * Options for connecting through a proxy + * + * Note that not all types may be supported, depending on the platform + * and compilation options. + */ +typedef struct { + unsigned int version; + + /** + * The type of proxy to use, by URL, auto-detect. + */ + git_proxy_t type; + + /** + * The URL of the proxy. + */ + const char *url; + + /** + * This will be called if the remote host requires + * authentication in order to connect to it. + * + * Returning GIT_PASSTHROUGH will make libgit2 behave as + * though this field isn't set. + */ + git_cred_acquire_cb credentials; + + /** + * If cert verification fails, this will be called to let the + * user make the final decision of whether to allow the + * connection to proceed. Returns 1 to allow the connection, 0 + * to disallow it or a negative value to indicate an error. + */ + git_transport_certificate_check_cb certificate_check; + + /** + * Payload to be provided to the credentials and certificate + * check callbacks. + */ + void *payload; +} git_proxy_options; + +#define GIT_PROXY_OPTIONS_VERSION 1 +#define GIT_PROXY_OPTIONS_INIT {GIT_PROXY_OPTIONS_VERSION} + +/** + * Initialize a proxy options structure + * + * @param opts the options struct to initialize + * @param version the version of the struct, use `GIT_PROXY_OPTIONS_VERSION` + */ +GIT_EXTERN(int) git_proxy_init_options(git_proxy_options *opts, unsigned int version); + +GIT_END_DECL + +#endif diff --git a/include/git2/remote.h b/include/git2/remote.h index 4f345d30c..c459f42cc 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -15,6 +15,7 @@ #include "strarray.h" #include "transport.h" #include "pack.h" +#include "proxy.h" /** * @file git2/remote.h @@ -241,10 +242,11 @@ GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, * @param direction GIT_DIRECTION_FETCH if you want to fetch or * GIT_DIRECTION_PUSH if you want to push * @param callbacks the callbacks to use for this connection + * @param proxy_opts proxy settings * @param custom_headers extra HTTP headers to use in this connection * @return 0 or an error code */ -GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_strarray *custom_headers); +GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy_opts, const git_strarray *custom_headers); /** * Get the remote repository's reference advertisement list @@ -549,13 +551,19 @@ typedef struct { git_remote_autotag_option_t download_tags; /** + * Proxy options to use, by default no proxy is used. + */ + git_proxy_options proxy_opts; + + /** * Extra headers for this fetch operation */ git_strarray custom_headers; } git_fetch_options; #define GIT_FETCH_OPTIONS_VERSION 1 -#define GIT_FETCH_OPTIONS_INIT { GIT_FETCH_OPTIONS_VERSION, GIT_REMOTE_CALLBACKS_INIT, GIT_FETCH_PRUNE_UNSPECIFIED, 1 } +#define GIT_FETCH_OPTIONS_INIT { GIT_FETCH_OPTIONS_VERSION, GIT_REMOTE_CALLBACKS_INIT, GIT_FETCH_PRUNE_UNSPECIFIED, 1, \ + GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED, GIT_PROXY_OPTIONS_INIT } /** * Initializes a `git_fetch_options` with default values. Equivalent to @@ -593,13 +601,18 @@ typedef struct { git_remote_callbacks callbacks; /** + * Proxy options to use, by default no proxy is used. + */ + git_proxy_options proxy_opts; + + /** * Extra headers for this push operation */ git_strarray custom_headers; } git_push_options; #define GIT_PUSH_OPTIONS_VERSION 1 -#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION, 0, GIT_REMOTE_CALLBACKS_INIT } +#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION, 0, GIT_REMOTE_CALLBACKS_INIT, GIT_PROXY_OPTIONS_INIT } /** * Initializes a `git_push_options` with default values. Equivalent to diff --git a/include/git2/sys/remote.h b/include/git2/sys/remote.h new file mode 100644 index 000000000..3037b411c --- /dev/null +++ b/include/git2/sys/remote.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#ifndef INCLUDE_sys_git_transport_h +#define INCLUDE_sys_git_transport_h + +#include "git2/net.h" +#include "git2/types.h" + +GIT_BEGIN_DECL + +GIT_END_DECL diff --git a/include/git2/sys/stream.h b/include/git2/sys/stream.h index 2b4ff7fd8..eeeb68dae 100644 --- a/include/git2/sys/stream.h +++ b/include/git2/sys/stream.h @@ -9,6 +9,7 @@ #include "git2/common.h" #include "git2/types.h" +#include "git2/proxy.h" GIT_BEGIN_DECL @@ -32,7 +33,7 @@ typedef struct git_stream { int proxy_support; int (*connect)(struct git_stream *); int (*certificate)(git_cert **, struct git_stream *); - int (*set_proxy)(struct git_stream *, const char *proxy_url); + int (*set_proxy)(struct git_stream *, const git_proxy_options *proxy_opts); ssize_t (*read)(struct git_stream *, void *, size_t); ssize_t (*write)(struct git_stream *, const char *, size_t, int); int (*close)(struct git_stream *); diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h index ce0234a18..60e38b21a 100644 --- a/include/git2/sys/transport.h +++ b/include/git2/sys/transport.h @@ -11,6 +11,7 @@ #include "git2/net.h" #include "git2/types.h" #include "git2/strarray.h" +#include "git2/proxy.h" /** * @file git2/sys/transport.h @@ -53,6 +54,7 @@ struct git_transport { const char *url, git_cred_acquire_cb cred_acquire_cb, void *cred_acquire_payload, + const git_proxy_options *proxy_opts, int direction, int flags); @@ -65,7 +67,7 @@ struct git_transport { git_transport *transport); /* Executes the push whose context is in the git_push object. */ - int (*push)(git_transport *transport, git_push *push, const git_remote_callbacks *callbacks); + int(*push)(git_transport *transport, git_push *push, const git_remote_callbacks *callbacks); /* This function may be called after a successful call to connect(), when * the direction is FETCH. The function performs a negotiation to calculate diff --git a/script/cibuild.sh b/script/cibuild.sh index 00cde0ada..92e926490 100755 --- a/script/cibuild.sh +++ b/script/cibuild.sh @@ -6,6 +6,11 @@ then exit $?; fi +# Should we ask Travis to cache this file? +curl -L https://github.com/ethomson/poxyproxy/releases/download/v0.1.0/poxyproxy-0.1.0.jar >poxyproxy.jar || exit $? +# Run this early so we know it's ready by the time we need it +java -jar poxyproxy.jar -d --port 8080 --credentials foo:bar & + mkdir _build cd _build # shellcheck disable=SC2086 @@ -49,12 +54,22 @@ export GITTEST_REMOTE_SSH_KEY="$HOME/.ssh/id_rsa" export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub" export GITTEST_REMOTE_SSH_PASSPHRASE="" + if [ -e ./libgit2_clar ]; then ./libgit2_clar -sonline::push -sonline::clone::ssh_cert && ./libgit2_clar -sonline::clone::ssh_with_paths || exit $? if [ "$TRAVIS_OS_NAME" = "linux" ]; then ./libgit2_clar -sonline::clone::cred_callback || exit $? fi + + # Use the proxy we started at the beginning + export GITTEST_REMOTE_PROXY_URL="http://foo:bar@localhost:8080/" + ./libgit2_clar -sonline::clone::proxy_credentials_in_url || exit $? + export GITTEST_REMOTE_PROXY_URL="http://localhost:8080/" + export GITTEST_REMOTE_PROXY_USER="foo" + export GITTEST_REMOTE_PROXY_PASS="bar" + ./libgit2_clar -sonline::clone::proxy_credentials_request || exit $? + fi export GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent" diff --git a/src/curl_stream.c b/src/curl_stream.c index 9963d94cc..98de187dd 100644 --- a/src/curl_stream.c +++ b/src/curl_stream.c @@ -13,6 +13,7 @@ #include "git2/transport.h" #include "buffer.h" #include "vector.h" +#include "proxy.h" typedef struct { git_stream parent; @@ -21,6 +22,8 @@ typedef struct { char curl_error[CURL_ERROR_SIZE + 1]; git_cert_x509 cert_info; git_strarray cert_info_strings; + git_proxy_options proxy; + git_cred *proxy_cred; } curl_stream; static int seterr_curl(curl_stream *s) @@ -29,21 +32,94 @@ static int seterr_curl(curl_stream *s) return -1; } +GIT_INLINE(int) error_no_credentials(void) +{ + giterr_set(GITERR_NET, "proxy authentication required, but no callback provided"); + return GIT_EAUTH; +} + +static int apply_proxy_creds(curl_stream *s) +{ + CURLcode res; + git_cred_userpass_plaintext *userpass; + + if (!s->proxy_cred) + return GIT_ENOTFOUND; + + userpass = (git_cred_userpass_plaintext *) s->proxy_cred; + if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK) + return seterr_curl(s); + if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK) + return seterr_curl(s); + + return 0; +} + +static int ask_and_apply_proxy_creds(curl_stream *s) +{ + int error; + git_proxy_options *opts = &s->proxy; + + if (!opts->credentials) + return error_no_credentials(); + + /* TODO: see if PROXYAUTH_AVAIL helps us here */ + git_cred_free(s->proxy_cred); + s->proxy_cred = NULL; + giterr_clear(); + error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload); + if (error == GIT_PASSTHROUGH) + return error_no_credentials(); + if (error < 0) { + if (!giterr_last()) + giterr_set(GITERR_NET, "proxy authentication was aborted by the user"); + return error; + } + + if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) { + giterr_set(GITERR_NET, "credentials callback returned invalid credential type"); + return -1; + } + + return apply_proxy_creds(s); +} + static int curls_connect(git_stream *stream) { curl_stream *s = (curl_stream *) stream; - long sockextr; - int failed_cert = 0; + long sockextr, connect_last = 0; + int failed_cert = 0, error; + bool retry_connect; CURLcode res; - res = curl_easy_perform(s->handle); + + /* Apply any credentials we've already established */ + error = apply_proxy_creds(s); + if (error < 0 && error != GIT_ENOTFOUND) + return seterr_curl(s); + + do { + retry_connect = 0; + res = curl_easy_perform(s->handle); + + curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last); + + /* HTTP 407 Proxy Authentication Required */ + if (connect_last == 407) { + if ((error = ask_and_apply_proxy_creds(s)) < 0) + return error; + + retry_connect = true; + } + } while (retry_connect); if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION) return seterr_curl(s); if (res == CURLE_PEER_FAILED_VERIFICATION) failed_cert = 1; - if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK) + if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK) { return seterr_curl(s); + } s->socket = sockextr; @@ -95,12 +171,19 @@ static int curls_certificate(git_cert **out, git_stream *stream) return 0; } -static int curls_set_proxy(git_stream *stream, const char *proxy_url) +static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts) { + int error; CURLcode res; curl_stream *s = (curl_stream *) stream; - if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, proxy_url)) != CURLE_OK) + if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0) + return error; + + if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK) + return seterr_curl(s); + + if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK) return seterr_curl(s); return 0; diff --git a/src/netops.c b/src/netops.c index c4241989f..90326ea59 100644 --- a/src/netops.c +++ b/src/netops.c @@ -257,16 +257,18 @@ int gitno_extract_url_parts( *port = git__strdup(default_port); GITERR_CHECK_ALLOC(*port); - if (u.field_set & (1 << UF_PATH)) { - *path = git__substrdup(_path, u.field_data[UF_PATH].len); - GITERR_CHECK_ALLOC(*path); - } else { - git__free(*port); - *port = NULL; - git__free(*host); - *host = NULL; - giterr_set(GITERR_NET, "invalid url, missing path"); - return GIT_EINVALIDSPEC; + if (path) { + if (u.field_set & (1 << UF_PATH)) { + *path = git__substrdup(_path, u.field_data[UF_PATH].len); + GITERR_CHECK_ALLOC(*path); + } else { + git__free(*port); + *port = NULL; + git__free(*host); + *host = NULL; + giterr_set(GITERR_NET, "invalid url, missing path"); + return GIT_EINVALIDSPEC; + } } if (u.field_set & (1 << UF_USERINFO)) { diff --git a/src/openssl_stream.c b/src/openssl_stream.c index a65f5586e..edea8fef7 100644 --- a/src/openssl_stream.c +++ b/src/openssl_stream.c @@ -496,11 +496,11 @@ int openssl_certificate(git_cert **out, git_stream *stream) return 0; } -static int openssl_set_proxy(git_stream *stream, const char *proxy_url) +static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts) { openssl_stream *st = (openssl_stream *) stream; - return git_stream_set_proxy(st->io, proxy_url); + return git_stream_set_proxy(st->io, proxy_opts); } ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags) diff --git a/src/proxy.c b/src/proxy.c new file mode 100644 index 000000000..f53ac1151 --- /dev/null +++ b/src/proxy.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "common.h" +#include "git2/proxy.h" + +int git_proxy_init_options(git_proxy_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_proxy_options, GIT_PROXY_OPTIONS_INIT); + return 0; +} + +int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src) +{ + if (!src) { + git_proxy_init_options(tgt, GIT_PROXY_OPTIONS_VERSION); + return 0; + } + + memcpy(tgt, src, sizeof(git_proxy_options)); + if (src->url) { + tgt->url = git__strdup(src->url); + GITERR_CHECK_ALLOC(tgt->url); + } + + return 0; +} diff --git a/src/proxy.h b/src/proxy.h new file mode 100644 index 000000000..bf9382737 --- /dev/null +++ b/src/proxy.h @@ -0,0 +1,14 @@ +/* +* Copyright (C) the libgit2 contributors. All rights reserved. +* +* This file is part of libgit2, distributed under the GNU GPL v2 with +* a Linking Exception. For full terms see the included COPYING file. +*/ +#ifndef INCLUDE_proxy_h__ +#define INCLUDE_proxy_h__ + +#include "git2/proxy.h" + +extern int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src); + +#endif
\ No newline at end of file diff --git a/src/push.c b/src/push.c index 0747259c8..b4901388b 100644 --- a/src/push.c +++ b/src/push.c @@ -639,7 +639,7 @@ int git_push_finish(git_push *push, const git_remote_callbacks *callbacks) int error; if (!git_remote_connected(push->remote) && - (error = git_remote_connect(push->remote, GIT_DIRECTION_PUSH, callbacks, push->custom_headers)) < 0) + (error = git_remote_connect(push->remote, GIT_DIRECTION_PUSH, callbacks, NULL, push->custom_headers)) < 0) return error; if ((error = filter_refs(push->remote)) < 0 || diff --git a/src/remote.c b/src/remote.c index 8b7203ee2..5ff7f6826 100644 --- a/src/remote.c +++ b/src/remote.c @@ -695,7 +695,7 @@ static int set_transport_custom_headers(git_transport *t, const git_strarray *cu return t->set_custom_headers(t, custom_headers); } -int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_strarray *custom_headers) +int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy, const git_strarray *custom_headers) { git_transport *t; const char *url; @@ -714,6 +714,9 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re payload = callbacks->payload; } + if (proxy) + GITERR_CHECK_VERSION(proxy, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); + t = remote->transport; url = git_remote__urlfordirection(remote, direction); @@ -738,7 +741,7 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re goto on_error; if ((error = set_transport_callbacks(t, callbacks)) < 0 || - (error = t->connect(t, url, credentials, payload, direction, flags)) != 0) + (error = t->connect(t, url, credentials, payload, proxy, direction, flags)) != 0) goto on_error; remote->transport = t; @@ -896,6 +899,7 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT; const git_remote_callbacks *cbs = NULL; const git_strarray *custom_headers = NULL; + const git_proxy_options *proxy = NULL; assert(remote); @@ -903,10 +907,12 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; custom_headers = &opts->custom_headers; + GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); + proxy = &opts->proxy_opts; } if (!git_remote_connected(remote) && - (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, custom_headers)) < 0) + (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) < 0) goto on_error; if (ls_to_vector(&refs, remote) < 0) @@ -971,6 +977,7 @@ int git_remote_fetch( git_buf reflog_msg_buf = GIT_BUF_INIT; const git_remote_callbacks *cbs = NULL; const git_strarray *custom_headers = NULL; + const git_proxy_options *proxy = NULL; if (opts) { GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); @@ -978,10 +985,12 @@ int git_remote_fetch( custom_headers = &opts->custom_headers; update_fetchhead = opts->update_fetchhead; tagopt = opts->download_tags; + GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); + proxy = &opts->proxy_opts; } /* Connect and download everything */ - if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, custom_headers)) != 0) + if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) != 0) return error; error = git_remote_download(remote, refspecs, opts); @@ -2393,16 +2402,18 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi git_refspec *spec; const git_remote_callbacks *cbs = NULL; const git_strarray *custom_headers = NULL; + const git_proxy_options *proxy = NULL; assert(remote); if (opts) { cbs = &opts->callbacks; custom_headers = &opts->custom_headers; + proxy = &opts->proxy_opts; } if (!git_remote_connected(remote) && - (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, custom_headers)) < 0) + (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0) goto cleanup; free_refspecs(&remote->active_refspecs); @@ -2452,16 +2463,19 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_ int error; const git_remote_callbacks *cbs = NULL; const git_strarray *custom_headers = NULL; + const git_proxy_options *proxy = NULL; if (opts) { GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; custom_headers = &opts->custom_headers; + GITERR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options"); + proxy = &opts->proxy_opts; } assert(remote && refspecs); - if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, custom_headers)) < 0) + if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0) return error; if ((error = git_remote_upload(remote, refspecs, opts)) < 0) diff --git a/src/stream.h b/src/stream.h index 4692c7115..d35477591 100644 --- a/src/stream.h +++ b/src/stream.h @@ -35,14 +35,14 @@ GIT_INLINE(int) git_stream_supports_proxy(git_stream *st) return st->proxy_support; } -GIT_INLINE(int) git_stream_set_proxy(git_stream *st, const char *proxy_url) +GIT_INLINE(int) git_stream_set_proxy(git_stream *st, const git_proxy_options *proxy_opts) { if (!st->proxy_support) { giterr_set(GITERR_INVALID, "proxy not supported on this stream"); return -1; } - return st->set_proxy(st, proxy_url); + return st->set_proxy(st, proxy_opts); } GIT_INLINE(ssize_t) git_stream_read(git_stream *st, void *data, size_t len) diff --git a/src/transports/http.c b/src/transports/http.c index 88b124bf7..7bb3374a0 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -555,10 +555,40 @@ static int write_chunk(git_stream *io, const char *buffer, size_t len) return 0; } +static int apply_proxy_config(http_subtransport *t) +{ + int error; + git_proxy_t proxy_type; + + if (!git_stream_supports_proxy(t->io)) + return 0; + + proxy_type = t->owner->proxy.type; + + if (proxy_type == GIT_PROXY_NONE) + return 0; + + if (proxy_type == GIT_PROXY_AUTO) { + char *url; + git_proxy_options opts = GIT_PROXY_OPTIONS_INIT; + + if ((error = git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &url)) < 0) + return error; + + opts.type = GIT_PROXY_SPECIFIED; + opts.url = url; + error = git_stream_set_proxy(t->io, &opts); + git__free(url); + + return error; + } + + return git_stream_set_proxy(t->io, &t->owner->proxy); +} + static int http_connect(http_subtransport *t) { int error; - char *proxy_url; if (t->connected && http_should_keep_alive(&t->parser) && @@ -586,14 +616,7 @@ static int http_connect(http_subtransport *t) GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream"); - if (git_stream_supports_proxy(t->io) && - !git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url)) { - error = git_stream_set_proxy(t->io, proxy_url); - git__free(proxy_url); - - if (error < 0) - return error; - } + apply_proxy_config(t); error = git_stream_connect(t->io); diff --git a/src/transports/local.c b/src/transports/local.c index 1c6e5f01e..4eae9dead 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -25,6 +25,7 @@ #include "odb.h" #include "push.h" #include "remote.h" +#include "proxy.h" typedef struct { git_transport parent; @@ -199,6 +200,7 @@ static int local_connect( const char *url, git_cred_acquire_cb cred_acquire_cb, void *cred_acquire_payload, + const git_proxy_options *proxy, int direction, int flags) { git_repository *repo; @@ -209,6 +211,7 @@ static int local_connect( GIT_UNUSED(cred_acquire_cb); GIT_UNUSED(cred_acquire_payload); + GIT_UNUSED(proxy); if (t->connected) return 0; @@ -439,7 +442,7 @@ static int local_push( if (!url || t->parent.close(&t->parent) < 0 || t->parent.connect(&t->parent, url, - NULL, NULL, GIT_DIRECTION_PUSH, flags)) + NULL, NULL, NULL, GIT_DIRECTION_PUSH, flags)) goto on_error; } diff --git a/src/transports/smart.c b/src/transports/smart.c index b0611c35e..11b4b09a4 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -8,6 +8,7 @@ #include "smart.h" #include "refs.h" #include "refspec.h" +#include "proxy.h" static int git_smart__recv_cb(gitno_buffer *buf) { @@ -199,6 +200,7 @@ static int git_smart__connect( const char *url, git_cred_acquire_cb cred_acquire_cb, void *cred_acquire_payload, + const git_proxy_options *proxy, int direction, int flags) { @@ -216,6 +218,9 @@ static int git_smart__connect( t->url = git__strdup(url); GITERR_CHECK_ALLOC(t->url); + if (git_proxy_options_dup(&t->proxy, proxy) < 0) + return -1; + t->direction = direction; t->flags = flags; t->cred_acquire_cb = cred_acquire_cb; @@ -439,6 +444,7 @@ static void git_smart__free(git_transport *transport) git_pkt_free(p); git_vector_free(refs); + git__free(t->proxy.url); git_strarray_free(&t->custom_headers); diff --git a/src/transports/smart.h b/src/transports/smart.h index 800466adf..0a0c3fc1b 100644 --- a/src/transports/smart.h +++ b/src/transports/smart.h @@ -133,6 +133,7 @@ typedef struct { char *url; git_cred_acquire_cb cred_acquire_cb; void *cred_acquire_payload; + git_proxy_options proxy; int direction; int flags; git_transport_message_cb progress_cb; diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 32b838084..580c3b91b 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -91,13 +91,39 @@ typedef struct { git_smart_subtransport parent; transport_smart *owner; gitno_connection_data connection_data; + gitno_connection_data proxy_connection_data; git_cred *cred; git_cred *url_cred; + git_cred *proxy_cred; int auth_mechanism; HINTERNET session; HINTERNET connection; } winhttp_subtransport; +static int apply_basic_credential_proxy(HINTERNET request, git_cred *cred) +{ + git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; + wchar_t *user, *pass; + int error; + + if ((error = git__utf8_to_16_alloc(&user, c->username)) < 0) + return error; + + if ((error = git__utf8_to_16_alloc(&pass, c->password)) < 0) + return error; + + if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_PROXY, WINHTTP_AUTH_SCHEME_BASIC, + user, pass, NULL)) { + giterr_set(GITERR_OS, "failed to set proxy auth"); + error = -1; + } + + git__free(user); + git__free(pass); + + return error; +} + static int apply_basic_credential(HINTERNET request, git_cred *cred) { git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; @@ -271,6 +297,37 @@ static void winhttp_stream_close(winhttp_stream *s) s->sent_request = 0; } +/** + * Extract the url and password from a URL. The outputs are pointers + * into the input. + */ +static int userpass_from_url(wchar_t **user, int *user_len, + wchar_t **pass, int *pass_len, + const wchar_t *url, int url_len) +{ + URL_COMPONENTS components = { 0 }; + + components.dwStructSize = sizeof(components); + /* These tell WinHttpCrackUrl that we're interested in the fields */ + components.dwUserNameLength = 1; + components.dwPasswordLength = 1; + + if (!WinHttpCrackUrl(url, url_len, 0, &components)) { + giterr_set(GITERR_OS, "failed to extract user/pass from url"); + return -1; + } + + *user = components.lpszUserName; + *user_len = components.dwUserNameLength; + *pass = components.lpszPassword; + *pass_len = components.dwPasswordLength; + + return 0; +} + +#define SCHEME_HTTP "http://" +#define SCHEME_HTTPS "https://" + static int winhttp_stream_connect(winhttp_stream *s) { winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); @@ -284,6 +341,7 @@ static int winhttp_stream_connect(winhttp_stream *s) int default_timeout = TIMEOUT_INFINITE; int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT; size_t i; + const git_proxy_options *proxy_opts; /* Prepare URL */ git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url); @@ -317,26 +375,59 @@ static int winhttp_stream_connect(winhttp_stream *s) goto on_error; } - /* Set proxy if necessary */ - if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0) - goto on_error; + proxy_opts = &t->owner->proxy; + if (proxy_opts->type == GIT_PROXY_AUTO) { + /* Set proxy if necessary */ + if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0) + goto on_error; + } + else if (proxy_opts->type == GIT_PROXY_SPECIFIED) { + proxy_url = git__strdup(proxy_opts->url); + GITERR_CHECK_ALLOC(proxy_url); + } if (proxy_url) { + git_buf processed_url = GIT_BUF_INIT; WINHTTP_PROXY_INFO proxy_info; wchar_t *proxy_wide; - /* Convert URL to wide characters */ - int proxy_wide_len = git__utf8_to_16_alloc(&proxy_wide, proxy_url); + if (!git__prefixcmp(proxy_url, SCHEME_HTTP)) { + t->proxy_connection_data.use_ssl = false; + } else if (!git__prefixcmp(proxy_url, SCHEME_HTTPS)) { + t->proxy_connection_data.use_ssl = true; + } else { + giterr_set(GITERR_NET, "invalid URL: '%s'", proxy_url); + return -1; + } - if (proxy_wide_len < 0) { - giterr_set(GITERR_OS, "Failed to convert string to wide form"); + if ((error = gitno_extract_url_parts(&t->proxy_connection_data.host, &t->proxy_connection_data.port, NULL, + &t->proxy_connection_data.user, &t->proxy_connection_data.pass, proxy_url, NULL)) < 0) + goto on_error; + + if (t->proxy_connection_data.user && t->proxy_connection_data.pass) { + if ((error = git_cred_userpass_plaintext_new(&t->proxy_cred, t->proxy_connection_data.user, t->proxy_connection_data.pass)) < 0) + goto on_error; + } + + if (t->proxy_connection_data.use_ssl) + git_buf_PUTS(&processed_url, SCHEME_HTTPS); + else + git_buf_PUTS(&processed_url, SCHEME_HTTP); + + git_buf_puts(&processed_url, t->proxy_connection_data.host); + if (t->proxy_connection_data.port) + git_buf_printf(&processed_url, ":%s", t->proxy_connection_data.port); + + if (git_buf_oom(&processed_url)) { + giterr_set_oom(); + error = -1; goto on_error; } - /* Strip any trailing forward slash on the proxy URL; - * WinHTTP doesn't like it if one is present */ - if (proxy_wide_len > 1 && L'/' == proxy_wide[proxy_wide_len - 2]) - proxy_wide[proxy_wide_len - 2] = L'\0'; + /* Convert URL to wide characters */ + if ((error = git__utf8_to_16_alloc(&proxy_wide, processed_url.ptr)) < 0) + goto on_error; + proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; proxy_info.lpszProxy = proxy_wide; @@ -352,6 +443,14 @@ static int winhttp_stream_connect(winhttp_stream *s) } git__free(proxy_wide); + + if (t->proxy_cred) { + if (t->proxy_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT) { + if ((error = apply_basic_credential_proxy(s->request, t->proxy_cred)) < 0) + goto on_error; + } + } + } /* Disable WinHTTP redirects so we can handle them manually. Why, you ask? @@ -919,6 +1018,26 @@ replay: goto replay; } + /* Handle proxy authentication failures */ + if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) { + int allowed_types; + + if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0) + return -1; + + /* TODO: extract the username from the url, no payload? */ + if (t->owner->proxy.credentials) { + int cred_error = 1; + cred_error = t->owner->proxy.credentials(&t->proxy_cred, t->owner->proxy.url, NULL, allowed_types, NULL); + + if (cred_error < 0) + return cred_error; + } + + winhttp_stream_close(s); + goto replay; + } + /* Handle authentication failures */ if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) { int allowed_types; diff --git a/tests/network/remote/defaultbranch.c b/tests/network/remote/defaultbranch.c index 5edd79fb8..9ab0d4095 100644 --- a/tests/network/remote/defaultbranch.c +++ b/tests/network/remote/defaultbranch.c @@ -26,7 +26,7 @@ static void assert_default_branch(const char *should) { git_buf name = GIT_BUF_INIT; - cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH, NULL, NULL)); + cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); cl_git_pass(git_remote_default_branch(&name, g_remote)); cl_assert_equal_s(should, name.ptr); git_buf_free(&name); @@ -57,7 +57,7 @@ void test_network_remote_defaultbranch__no_default_branch(void) git_buf buf = GIT_BUF_INIT; cl_git_pass(git_remote_create(&remote_b, g_repo_b, "self", git_repository_path(g_repo_b))); - cl_git_pass(git_remote_connect(remote_b, GIT_DIRECTION_FETCH, NULL, NULL)); + cl_git_pass(git_remote_connect(remote_b, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); cl_git_pass(git_remote_ls(&heads, &len, remote_b)); cl_assert_equal_i(0, len); @@ -80,7 +80,7 @@ void test_network_remote_defaultbranch__detached_sharing_nonbranch_id(void) cl_git_pass(git_reference_create(&ref, g_repo_a, "refs/foo/bar", &id, 1, NULL)); git_reference_free(ref); - cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH, NULL, NULL)); + cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, g_remote)); cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./local-detached", NULL)); diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c index 4d990ab71..6194802af 100644 --- a/tests/network/remote/local.c +++ b/tests/network/remote/local.c @@ -40,7 +40,7 @@ static void connect_to_local_repository(const char *local_repository) git_buf_sets(&file_path_buf, cl_git_path_url(local_repository)); cl_git_pass(git_remote_create_anonymous(&remote, repo, git_buf_cstr(&file_path_buf))); - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); } void test_network_remote_local__connected(void) @@ -214,7 +214,7 @@ void test_network_remote_local__push_to_bare_remote(void) /* Connect to the bare repo */ cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localbare.git")); - cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL)); + cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL, NULL)); /* Try to push */ cl_git_pass(git_remote_upload(localremote, &push_array, NULL)); @@ -253,7 +253,7 @@ void test_network_remote_local__push_to_bare_remote_with_file_url(void) /* Connect to the bare repo */ cl_git_pass(git_remote_create_anonymous(&localremote, repo, url)); - cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL)); + cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL, NULL)); /* Try to push */ cl_git_pass(git_remote_upload(localremote, &push_array, NULL)); @@ -290,7 +290,7 @@ void test_network_remote_local__push_to_non_bare_remote(void) /* Connect to the bare repo */ cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localnonbare")); - cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL)); + cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL, NULL)); /* Try to push */ cl_git_fail_with(GIT_EBAREREPO, git_remote_upload(localremote, &push_array, NULL)); diff --git a/tests/network/remote/remotes.c b/tests/network/remote/remotes.c index 46abc6d33..9c7e6b299 100644 --- a/tests/network/remote/remotes.c +++ b/tests/network/remote/remotes.c @@ -93,7 +93,7 @@ void test_network_remote_remotes__error_when_no_push_available(void) cl_git_pass(git_remote_create_anonymous(&r, _repo, cl_fixture("testrepo.git"))); callbacks.transport = git_transport_local; - cl_git_pass(git_remote_connect(r, GIT_DIRECTION_PUSH, &callbacks, NULL)); + cl_git_pass(git_remote_connect(r, GIT_DIRECTION_PUSH, &callbacks, NULL, NULL)); /* Make sure that push is really not available */ r->transport->push = NULL; @@ -359,7 +359,7 @@ void test_network_remote_remotes__can_load_with_an_empty_url(void) cl_assert(remote->url == NULL); cl_assert(remote->pushurl == NULL); - cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); + cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); cl_assert(giterr_last() != NULL); cl_assert(giterr_last()->klass == GITERR_INVALID); @@ -376,7 +376,7 @@ void test_network_remote_remotes__can_load_with_only_an_empty_pushurl(void) cl_assert(remote->url == NULL); cl_assert(remote->pushurl == NULL); - cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); + cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); git_remote_free(remote); } diff --git a/tests/network/urlparse.c b/tests/network/urlparse.c index b3ac8ae60..4a3096baa 100644 --- a/tests/network/urlparse.c +++ b/tests/network/urlparse.c @@ -121,6 +121,15 @@ void test_network_urlparse__user_pass_port(void) cl_assert_equal_s(pass, "pass"); } +void test_network_urlparse__optional_path(void) +{ + cl_git_fail(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://user:pass@example.com:9191", "8080")); + + cl_git_pass(gitno_extract_url_parts(&host, &port, NULL, &user, &pass, + "https://user:pass@example.com:9191", "8080")); +} + void test_network_urlparse__connection_data_http(void) { cl_git_pass(gitno_connection_data_from_url(&conndata, diff --git a/tests/online/clone.c b/tests/online/clone.c index b84be405c..0fc8d4271 100644 --- a/tests/online/clone.c +++ b/tests/online/clone.c @@ -24,6 +24,9 @@ static char *_remote_ssh_pubkey = NULL; static char *_remote_ssh_privkey = NULL; static char *_remote_ssh_passphrase = NULL; static char *_remote_ssh_fingerprint = NULL; +static char *_remote_proxy_url = NULL; +static char *_remote_proxy_user = NULL; +static char *_remote_proxy_pass = NULL; void test_online_clone__initialize(void) @@ -46,6 +49,9 @@ void test_online_clone__initialize(void) _remote_ssh_privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY"); _remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE"); _remote_ssh_fingerprint = cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT"); + _remote_proxy_url = cl_getenv("GITTEST_REMOTE_PROXY_URL"); + _remote_proxy_user = cl_getenv("GITTEST_REMOTE_PROXY_USER"); + _remote_proxy_pass = cl_getenv("GITTEST_REMOTE_PROXY_PASS"); } void test_online_clone__cleanup(void) @@ -63,6 +69,9 @@ void test_online_clone__cleanup(void) git__free(_remote_ssh_privkey); git__free(_remote_ssh_passphrase); git__free(_remote_ssh_fingerprint); + git__free(_remote_proxy_url); + git__free(_remote_proxy_user); + git__free(_remote_proxy_pass); } void test_online_clone__network_full(void) @@ -653,3 +662,38 @@ void test_online_clone__start_with_http(void) cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options)); } + +static int called_proxy_creds; +static int proxy_creds(git_cred **out, const char *url, const char *username, unsigned int allowed, void *payload) +{ + GIT_UNUSED(payload); + GIT_UNUSED(username); + + called_proxy_creds = 1; + return git_cred_userpass_plaintext_new(out, _remote_proxy_user, _remote_proxy_pass); +} + +void test_online_clone__proxy_credentials_request(void) +{ + if (!_remote_proxy_url || !_remote_proxy_user || !_remote_proxy_pass) + cl_skip(); + + g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED; + g_options.fetch_opts.proxy_opts.url = _remote_proxy_url; + g_options.fetch_opts.proxy_opts.credentials = proxy_creds; + called_proxy_creds = 0; + cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options)); + cl_assert(called_proxy_creds); +} + +void test_online_clone__proxy_credentials_in_url(void) +{ + if (!_remote_proxy_url) + cl_skip(); + + g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED; + g_options.fetch_opts.proxy_opts.url = _remote_proxy_url; + called_proxy_creds = 0; + cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options)); + cl_assert(called_proxy_creds == 0); +} diff --git a/tests/online/fetch.c b/tests/online/fetch.c index c12df069f..827cb23c1 100644 --- a/tests/online/fetch.c +++ b/tests/online/fetch.c @@ -81,11 +81,11 @@ void test_online_fetch__fetch_twice(void) { git_remote *remote; cl_git_pass(git_remote_create(&remote, _repo, "test", "git://github.com/libgit2/TestGitRepository.git")); - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); cl_git_pass(git_remote_download(remote, NULL, NULL)); git_remote_disconnect(remote); - git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL); + git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL); cl_git_pass(git_remote_download(remote, NULL, NULL)); git_remote_disconnect(remote); @@ -117,7 +117,7 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date cl_git_pass(git_repository_open(&_repository, "./fetch/lg2")); cl_git_pass(git_remote_lookup(&remote, _repository, "origin")); - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); cl_assert_equal_i(false, invoked); @@ -155,7 +155,7 @@ void test_online_fetch__can_cancel(void) options.callbacks.transfer_progress = cancel_at_half; options.callbacks.payload = &bytes_received; - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); cl_git_fail_with(git_remote_download(remote, NULL, &options), -4321); git_remote_disconnect(remote); git_remote_free(remote); @@ -169,7 +169,7 @@ void test_online_fetch__ls_disconnected(void) cl_git_pass(git_remote_create(&remote, _repo, "test", "http://github.com/libgit2/TestGitRepository.git")); - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); cl_git_pass(git_remote_ls(&refs, &refs_len_before, remote)); git_remote_disconnect(remote); cl_git_pass(git_remote_ls(&refs, &refs_len_after, remote)); @@ -187,7 +187,7 @@ void test_online_fetch__remote_symrefs(void) cl_git_pass(git_remote_create(&remote, _repo, "test", "http://github.com/libgit2/TestGitRepository.git")); - cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); git_remote_disconnect(remote); cl_git_pass(git_remote_ls(&refs, &refs_len, remote)); diff --git a/tests/online/push.c b/tests/online/push.c index 77c437622..f72b4f8cb 100644 --- a/tests/online/push.c +++ b/tests/online/push.c @@ -372,7 +372,7 @@ void test_online_push__initialize(void) record_callbacks_data_clear(&_record_cbs_data); - cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH, &_record_cbs, NULL)); + cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH, &_record_cbs, NULL, NULL)); /* Clean up previously pushed branches. Fails if receive.denyDeletes is * set on the remote. Also, on Git 1.7.0 and newer, you must run |