Commit 1dc44910 by Edward Thomson

Merge pull request #3110 from libgit2/cmn/proxy-config

Proxy configuration
parents 95fbc81d 2638df77
...@@ -690,6 +690,8 @@ IF (BUILD_CLAR) ...@@ -690,6 +690,8 @@ IF (BUILD_CLAR)
# Add a test target which runs the cred callback tests, to be # Add a test target which runs the cred callback tests, to be
# called after setting the url and user # called after setting the url and user
ADD_TEST(libgit2_clar-cred_callback libgit2_clar -v -sonline::clone::cred_callback) 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 () ENDIF ()
IF (TAGS) IF (TAGS)
......
...@@ -19,6 +19,7 @@ environment: ...@@ -19,6 +19,7 @@ environment:
cache: cache:
- i686-4.9.2-release-win32-sjlj-rt_v3-rev1.7z - i686-4.9.2-release-win32-sjlj-rt_v3-rev1.7z
- x86_64-4.9.2-release-win32-seh-rt_v3-rev1.7z - x86_64-4.9.2-release-win32-seh-rt_v3-rev1.7z
build_script: build_script:
- ps: | - ps: |
mkdir build mkdir build
...@@ -32,7 +33,17 @@ build_script: ...@@ -32,7 +33,17 @@ build_script:
test_script: test_script:
- ps: | - ps: |
$ErrorActionPreference="Stop" $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 ctest -V -R libgit2_clar
$env:GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent" $env:GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
$env:GITTEST_REMOTE_USER="libgit2test" $env:GITTEST_REMOTE_USER="libgit2test"
ctest -V -R libgit2_clar-cred_callback 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
...@@ -26,7 +26,7 @@ static int use_remote(git_repository *repo, char *name) ...@@ -26,7 +26,7 @@ static int use_remote(git_repository *repo, char *name)
*/ */
callbacks.credentials = cred_acquire_cb; 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) if (error < 0)
goto cleanup; goto cleanup;
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "git2/pack.h" #include "git2/pack.h"
#include "git2/patch.h" #include "git2/patch.h"
#include "git2/pathspec.h" #include "git2/pathspec.h"
#include "git2/proxy.h"
#include "git2/rebase.h" #include "git2/rebase.h"
#include "git2/refdb.h" #include "git2/refdb.h"
#include "git2/reflog.h" #include "git2/reflog.h"
......
/*
* 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
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "strarray.h" #include "strarray.h"
#include "transport.h" #include "transport.h"
#include "pack.h" #include "pack.h"
#include "proxy.h"
/** /**
* @file git2/remote.h * @file git2/remote.h
...@@ -241,10 +242,11 @@ GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, ...@@ -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 * @param direction GIT_DIRECTION_FETCH if you want to fetch or
* GIT_DIRECTION_PUSH if you want to push * GIT_DIRECTION_PUSH if you want to push
* @param callbacks the callbacks to use for this connection * @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 * @param custom_headers extra HTTP headers to use in this connection
* @return 0 or an error code * @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 * Get the remote repository's reference advertisement list
...@@ -549,13 +551,19 @@ typedef struct { ...@@ -549,13 +551,19 @@ typedef struct {
git_remote_autotag_option_t download_tags; 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 * Extra headers for this fetch operation
*/ */
git_strarray custom_headers; git_strarray custom_headers;
} git_fetch_options; } git_fetch_options;
#define GIT_FETCH_OPTIONS_VERSION 1 #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 * Initializes a `git_fetch_options` with default values. Equivalent to
...@@ -593,13 +601,18 @@ typedef struct { ...@@ -593,13 +601,18 @@ typedef struct {
git_remote_callbacks callbacks; 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 * Extra headers for this push operation
*/ */
git_strarray custom_headers; git_strarray custom_headers;
} git_push_options; } git_push_options;
#define GIT_PUSH_OPTIONS_VERSION 1 #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 * Initializes a `git_push_options` with default values. Equivalent to
......
/*
* 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
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "git2/common.h" #include "git2/common.h"
#include "git2/types.h" #include "git2/types.h"
#include "git2/proxy.h"
GIT_BEGIN_DECL GIT_BEGIN_DECL
...@@ -32,7 +33,7 @@ typedef struct git_stream { ...@@ -32,7 +33,7 @@ typedef struct git_stream {
int proxy_support; int proxy_support;
int (*connect)(struct git_stream *); int (*connect)(struct git_stream *);
int (*certificate)(git_cert **, 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 (*read)(struct git_stream *, void *, size_t);
ssize_t (*write)(struct git_stream *, const char *, size_t, int); ssize_t (*write)(struct git_stream *, const char *, size_t, int);
int (*close)(struct git_stream *); int (*close)(struct git_stream *);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "git2/net.h" #include "git2/net.h"
#include "git2/types.h" #include "git2/types.h"
#include "git2/strarray.h" #include "git2/strarray.h"
#include "git2/proxy.h"
/** /**
* @file git2/sys/transport.h * @file git2/sys/transport.h
...@@ -53,6 +54,7 @@ struct git_transport { ...@@ -53,6 +54,7 @@ struct git_transport {
const char *url, const char *url,
git_cred_acquire_cb cred_acquire_cb, git_cred_acquire_cb cred_acquire_cb,
void *cred_acquire_payload, void *cred_acquire_payload,
const git_proxy_options *proxy_opts,
int direction, int direction,
int flags); int flags);
...@@ -65,7 +67,7 @@ struct git_transport { ...@@ -65,7 +67,7 @@ struct git_transport {
git_transport *transport); git_transport *transport);
/* Executes the push whose context is in the git_push object. */ /* 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 /* This function may be called after a successful call to connect(), when
* the direction is FETCH. The function performs a negotiation to calculate * the direction is FETCH. The function performs a negotiation to calculate
......
...@@ -6,6 +6,11 @@ then ...@@ -6,6 +6,11 @@ then
exit $?; exit $?;
fi 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 mkdir _build
cd _build cd _build
# shellcheck disable=SC2086 # shellcheck disable=SC2086
...@@ -49,12 +54,22 @@ export GITTEST_REMOTE_SSH_KEY="$HOME/.ssh/id_rsa" ...@@ -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_PUBKEY="$HOME/.ssh/id_rsa.pub"
export GITTEST_REMOTE_SSH_PASSPHRASE="" export GITTEST_REMOTE_SSH_PASSPHRASE=""
if [ -e ./libgit2_clar ]; then if [ -e ./libgit2_clar ]; then
./libgit2_clar -sonline::push -sonline::clone::ssh_cert && ./libgit2_clar -sonline::push -sonline::clone::ssh_cert &&
./libgit2_clar -sonline::clone::ssh_with_paths || exit $? ./libgit2_clar -sonline::clone::ssh_with_paths || exit $?
if [ "$TRAVIS_OS_NAME" = "linux" ]; then if [ "$TRAVIS_OS_NAME" = "linux" ]; then
./libgit2_clar -sonline::clone::cred_callback || exit $? ./libgit2_clar -sonline::clone::cred_callback || exit $?
fi 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 fi
export GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent" export GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "git2/transport.h" #include "git2/transport.h"
#include "buffer.h" #include "buffer.h"
#include "vector.h" #include "vector.h"
#include "proxy.h"
typedef struct { typedef struct {
git_stream parent; git_stream parent;
...@@ -21,6 +22,8 @@ typedef struct { ...@@ -21,6 +22,8 @@ typedef struct {
char curl_error[CURL_ERROR_SIZE + 1]; char curl_error[CURL_ERROR_SIZE + 1];
git_cert_x509 cert_info; git_cert_x509 cert_info;
git_strarray cert_info_strings; git_strarray cert_info_strings;
git_proxy_options proxy;
git_cred *proxy_cred;
} curl_stream; } curl_stream;
static int seterr_curl(curl_stream *s) static int seterr_curl(curl_stream *s)
...@@ -29,21 +32,94 @@ static int seterr_curl(curl_stream *s) ...@@ -29,21 +32,94 @@ static int seterr_curl(curl_stream *s)
return -1; 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) static int curls_connect(git_stream *stream)
{ {
curl_stream *s = (curl_stream *) stream; curl_stream *s = (curl_stream *) stream;
long sockextr; long sockextr, connect_last = 0;
int failed_cert = 0; int failed_cert = 0, error;
bool retry_connect;
CURLcode res; CURLcode res;
/* 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); 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) if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
return seterr_curl(s); return seterr_curl(s);
if (res == CURLE_PEER_FAILED_VERIFICATION) if (res == CURLE_PEER_FAILED_VERIFICATION)
failed_cert = 1; 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); return seterr_curl(s);
}
s->socket = sockextr; s->socket = sockextr;
...@@ -95,12 +171,19 @@ static int curls_certificate(git_cert **out, git_stream *stream) ...@@ -95,12 +171,19 @@ static int curls_certificate(git_cert **out, git_stream *stream)
return 0; 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; CURLcode res;
curl_stream *s = (curl_stream *) stream; 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 seterr_curl(s);
return 0; return 0;
......
...@@ -257,6 +257,7 @@ int gitno_extract_url_parts( ...@@ -257,6 +257,7 @@ int gitno_extract_url_parts(
*port = git__strdup(default_port); *port = git__strdup(default_port);
GITERR_CHECK_ALLOC(*port); GITERR_CHECK_ALLOC(*port);
if (path) {
if (u.field_set & (1 << UF_PATH)) { if (u.field_set & (1 << UF_PATH)) {
*path = git__substrdup(_path, u.field_data[UF_PATH].len); *path = git__substrdup(_path, u.field_data[UF_PATH].len);
GITERR_CHECK_ALLOC(*path); GITERR_CHECK_ALLOC(*path);
...@@ -268,6 +269,7 @@ int gitno_extract_url_parts( ...@@ -268,6 +269,7 @@ int gitno_extract_url_parts(
giterr_set(GITERR_NET, "invalid url, missing path"); giterr_set(GITERR_NET, "invalid url, missing path");
return GIT_EINVALIDSPEC; return GIT_EINVALIDSPEC;
} }
}
if (u.field_set & (1 << UF_USERINFO)) { if (u.field_set & (1 << UF_USERINFO)) {
const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len); const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len);
......
...@@ -496,11 +496,11 @@ int openssl_certificate(git_cert **out, git_stream *stream) ...@@ -496,11 +496,11 @@ int openssl_certificate(git_cert **out, git_stream *stream)
return 0; 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; 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) ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags)
......
/*
* 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;
}
/*
* 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
...@@ -639,7 +639,7 @@ int git_push_finish(git_push *push, const git_remote_callbacks *callbacks) ...@@ -639,7 +639,7 @@ int git_push_finish(git_push *push, const git_remote_callbacks *callbacks)
int error; int error;
if (!git_remote_connected(push->remote) && 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; return error;
if ((error = filter_refs(push->remote)) < 0 || if ((error = filter_refs(push->remote)) < 0 ||
......
...@@ -695,7 +695,7 @@ static int set_transport_custom_headers(git_transport *t, const git_strarray *cu ...@@ -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); 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; git_transport *t;
const char *url; const char *url;
...@@ -714,6 +714,9 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re ...@@ -714,6 +714,9 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re
payload = callbacks->payload; payload = callbacks->payload;
} }
if (proxy)
GITERR_CHECK_VERSION(proxy, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
t = remote->transport; t = remote->transport;
url = git_remote__urlfordirection(remote, direction); url = git_remote__urlfordirection(remote, direction);
...@@ -738,7 +741,7 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re ...@@ -738,7 +741,7 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re
goto on_error; goto on_error;
if ((error = set_transport_callbacks(t, callbacks)) < 0 || 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; goto on_error;
remote->transport = t; remote->transport = t;
...@@ -896,6 +899,7 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const ...@@ -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; git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT;
const git_remote_callbacks *cbs = NULL; const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL; const git_strarray *custom_headers = NULL;
const git_proxy_options *proxy = NULL;
assert(remote); assert(remote);
...@@ -903,10 +907,12 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const ...@@ -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"); GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks; cbs = &opts->callbacks;
custom_headers = &opts->custom_headers; 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) && 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; goto on_error;
if (ls_to_vector(&refs, remote) < 0) if (ls_to_vector(&refs, remote) < 0)
...@@ -971,6 +977,7 @@ int git_remote_fetch( ...@@ -971,6 +977,7 @@ int git_remote_fetch(
git_buf reflog_msg_buf = GIT_BUF_INIT; git_buf reflog_msg_buf = GIT_BUF_INIT;
const git_remote_callbacks *cbs = NULL; const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL; const git_strarray *custom_headers = NULL;
const git_proxy_options *proxy = NULL;
if (opts) { if (opts) {
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
...@@ -978,10 +985,12 @@ int git_remote_fetch( ...@@ -978,10 +985,12 @@ int git_remote_fetch(
custom_headers = &opts->custom_headers; custom_headers = &opts->custom_headers;
update_fetchhead = opts->update_fetchhead; update_fetchhead = opts->update_fetchhead;
tagopt = opts->download_tags; 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 */ /* 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; return error;
error = git_remote_download(remote, refspecs, opts); error = git_remote_download(remote, refspecs, opts);
...@@ -2393,16 +2402,18 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi ...@@ -2393,16 +2402,18 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi
git_refspec *spec; git_refspec *spec;
const git_remote_callbacks *cbs = NULL; const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL; const git_strarray *custom_headers = NULL;
const git_proxy_options *proxy = NULL;
assert(remote); assert(remote);
if (opts) { if (opts) {
cbs = &opts->callbacks; cbs = &opts->callbacks;
custom_headers = &opts->custom_headers; custom_headers = &opts->custom_headers;
proxy = &opts->proxy_opts;
} }
if (!git_remote_connected(remote) && 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; goto cleanup;
free_refspecs(&remote->active_refspecs); free_refspecs(&remote->active_refspecs);
...@@ -2452,16 +2463,19 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_ ...@@ -2452,16 +2463,19 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_
int error; int error;
const git_remote_callbacks *cbs = NULL; const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL; const git_strarray *custom_headers = NULL;
const git_proxy_options *proxy = NULL;
if (opts) { if (opts) {
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks; cbs = &opts->callbacks;
custom_headers = &opts->custom_headers; 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); 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; return error;
if ((error = git_remote_upload(remote, refspecs, opts)) < 0) if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
......
...@@ -35,14 +35,14 @@ GIT_INLINE(int) git_stream_supports_proxy(git_stream *st) ...@@ -35,14 +35,14 @@ GIT_INLINE(int) git_stream_supports_proxy(git_stream *st)
return st->proxy_support; 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) { if (!st->proxy_support) {
giterr_set(GITERR_INVALID, "proxy not supported on this stream"); giterr_set(GITERR_INVALID, "proxy not supported on this stream");
return -1; 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) GIT_INLINE(ssize_t) git_stream_read(git_stream *st, void *data, size_t len)
......
...@@ -555,10 +555,40 @@ static int write_chunk(git_stream *io, const char *buffer, size_t len) ...@@ -555,10 +555,40 @@ static int write_chunk(git_stream *io, const char *buffer, size_t len)
return 0; 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) static int http_connect(http_subtransport *t)
{ {
int error; int error;
char *proxy_url;
if (t->connected && if (t->connected &&
http_should_keep_alive(&t->parser) && http_should_keep_alive(&t->parser) &&
...@@ -586,14 +616,7 @@ static int http_connect(http_subtransport *t) ...@@ -586,14 +616,7 @@ static int http_connect(http_subtransport *t)
GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream"); GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream");
if (git_stream_supports_proxy(t->io) && apply_proxy_config(t);
!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;
}
error = git_stream_connect(t->io); error = git_stream_connect(t->io);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "odb.h" #include "odb.h"
#include "push.h" #include "push.h"
#include "remote.h" #include "remote.h"
#include "proxy.h"
typedef struct { typedef struct {
git_transport parent; git_transport parent;
...@@ -199,6 +200,7 @@ static int local_connect( ...@@ -199,6 +200,7 @@ static int local_connect(
const char *url, const char *url,
git_cred_acquire_cb cred_acquire_cb, git_cred_acquire_cb cred_acquire_cb,
void *cred_acquire_payload, void *cred_acquire_payload,
const git_proxy_options *proxy,
int direction, int flags) int direction, int flags)
{ {
git_repository *repo; git_repository *repo;
...@@ -209,6 +211,7 @@ static int local_connect( ...@@ -209,6 +211,7 @@ static int local_connect(
GIT_UNUSED(cred_acquire_cb); GIT_UNUSED(cred_acquire_cb);
GIT_UNUSED(cred_acquire_payload); GIT_UNUSED(cred_acquire_payload);
GIT_UNUSED(proxy);
if (t->connected) if (t->connected)
return 0; return 0;
...@@ -439,7 +442,7 @@ static int local_push( ...@@ -439,7 +442,7 @@ static int local_push(
if (!url || t->parent.close(&t->parent) < 0 || if (!url || t->parent.close(&t->parent) < 0 ||
t->parent.connect(&t->parent, url, t->parent.connect(&t->parent, url,
NULL, NULL, GIT_DIRECTION_PUSH, flags)) NULL, NULL, NULL, GIT_DIRECTION_PUSH, flags))
goto on_error; goto on_error;
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "smart.h" #include "smart.h"
#include "refs.h" #include "refs.h"
#include "refspec.h" #include "refspec.h"
#include "proxy.h"
static int git_smart__recv_cb(gitno_buffer *buf) static int git_smart__recv_cb(gitno_buffer *buf)
{ {
...@@ -199,6 +200,7 @@ static int git_smart__connect( ...@@ -199,6 +200,7 @@ static int git_smart__connect(
const char *url, const char *url,
git_cred_acquire_cb cred_acquire_cb, git_cred_acquire_cb cred_acquire_cb,
void *cred_acquire_payload, void *cred_acquire_payload,
const git_proxy_options *proxy,
int direction, int direction,
int flags) int flags)
{ {
...@@ -216,6 +218,9 @@ static int git_smart__connect( ...@@ -216,6 +218,9 @@ static int git_smart__connect(
t->url = git__strdup(url); t->url = git__strdup(url);
GITERR_CHECK_ALLOC(t->url); GITERR_CHECK_ALLOC(t->url);
if (git_proxy_options_dup(&t->proxy, proxy) < 0)
return -1;
t->direction = direction; t->direction = direction;
t->flags = flags; t->flags = flags;
t->cred_acquire_cb = cred_acquire_cb; t->cred_acquire_cb = cred_acquire_cb;
...@@ -439,6 +444,7 @@ static void git_smart__free(git_transport *transport) ...@@ -439,6 +444,7 @@ static void git_smart__free(git_transport *transport)
git_pkt_free(p); git_pkt_free(p);
git_vector_free(refs); git_vector_free(refs);
git__free(t->proxy.url);
git_strarray_free(&t->custom_headers); git_strarray_free(&t->custom_headers);
......
...@@ -133,6 +133,7 @@ typedef struct { ...@@ -133,6 +133,7 @@ typedef struct {
char *url; char *url;
git_cred_acquire_cb cred_acquire_cb; git_cred_acquire_cb cred_acquire_cb;
void *cred_acquire_payload; void *cred_acquire_payload;
git_proxy_options proxy;
int direction; int direction;
int flags; int flags;
git_transport_message_cb progress_cb; git_transport_message_cb progress_cb;
......
...@@ -91,13 +91,39 @@ typedef struct { ...@@ -91,13 +91,39 @@ typedef struct {
git_smart_subtransport parent; git_smart_subtransport parent;
transport_smart *owner; transport_smart *owner;
gitno_connection_data connection_data; gitno_connection_data connection_data;
gitno_connection_data proxy_connection_data;
git_cred *cred; git_cred *cred;
git_cred *url_cred; git_cred *url_cred;
git_cred *proxy_cred;
int auth_mechanism; int auth_mechanism;
HINTERNET session; HINTERNET session;
HINTERNET connection; HINTERNET connection;
} winhttp_subtransport; } 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) static int apply_basic_credential(HINTERNET request, git_cred *cred)
{ {
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
...@@ -271,6 +297,37 @@ static void winhttp_stream_close(winhttp_stream *s) ...@@ -271,6 +297,37 @@ static void winhttp_stream_close(winhttp_stream *s)
s->sent_request = 0; 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) static int winhttp_stream_connect(winhttp_stream *s)
{ {
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
...@@ -284,6 +341,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -284,6 +341,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
int default_timeout = TIMEOUT_INFINITE; int default_timeout = TIMEOUT_INFINITE;
int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT; int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
size_t i; size_t i;
const git_proxy_options *proxy_opts;
/* Prepare URL */ /* Prepare URL */
git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_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) ...@@ -317,26 +375,59 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error; goto on_error;
} }
proxy_opts = &t->owner->proxy;
if (proxy_opts->type == GIT_PROXY_AUTO) {
/* Set proxy if necessary */ /* Set proxy if necessary */
if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0) if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
goto on_error; 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) { if (proxy_url) {
git_buf processed_url = GIT_BUF_INIT;
WINHTTP_PROXY_INFO proxy_info; WINHTTP_PROXY_INFO proxy_info;
wchar_t *proxy_wide; wchar_t *proxy_wide;
/* Convert URL to wide characters */ if (!git__prefixcmp(proxy_url, SCHEME_HTTP)) {
int proxy_wide_len = git__utf8_to_16_alloc(&proxy_wide, proxy_url); 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) { if ((error = gitno_extract_url_parts(&t->proxy_connection_data.host, &t->proxy_connection_data.port, NULL,
giterr_set(GITERR_OS, "Failed to convert string to wide form"); &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; goto on_error;
} }
/* Strip any trailing forward slash on the proxy URL; /* Convert URL to wide characters */
* WinHTTP doesn't like it if one is present */ if ((error = git__utf8_to_16_alloc(&proxy_wide, processed_url.ptr)) < 0)
if (proxy_wide_len > 1 && L'/' == proxy_wide[proxy_wide_len - 2]) goto on_error;
proxy_wide[proxy_wide_len - 2] = L'\0';
proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
proxy_info.lpszProxy = proxy_wide; proxy_info.lpszProxy = proxy_wide;
...@@ -352,6 +443,14 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -352,6 +443,14 @@ static int winhttp_stream_connect(winhttp_stream *s)
} }
git__free(proxy_wide); 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? /* Disable WinHTTP redirects so we can handle them manually. Why, you ask?
...@@ -919,6 +1018,26 @@ replay: ...@@ -919,6 +1018,26 @@ replay:
goto 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 */ /* Handle authentication failures */
if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) { if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) {
int allowed_types; int allowed_types;
......
...@@ -26,7 +26,7 @@ static void assert_default_branch(const char *should) ...@@ -26,7 +26,7 @@ static void assert_default_branch(const char *should)
{ {
git_buf name = GIT_BUF_INIT; 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_git_pass(git_remote_default_branch(&name, g_remote));
cl_assert_equal_s(should, name.ptr); cl_assert_equal_s(should, name.ptr);
git_buf_free(&name); git_buf_free(&name);
...@@ -57,7 +57,7 @@ void test_network_remote_defaultbranch__no_default_branch(void) ...@@ -57,7 +57,7 @@ void test_network_remote_defaultbranch__no_default_branch(void)
git_buf buf = GIT_BUF_INIT; 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_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_git_pass(git_remote_ls(&heads, &len, remote_b));
cl_assert_equal_i(0, len); cl_assert_equal_i(0, len);
...@@ -80,7 +80,7 @@ void test_network_remote_defaultbranch__detached_sharing_nonbranch_id(void) ...@@ -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)); cl_git_pass(git_reference_create(&ref, g_repo_a, "refs/foo/bar", &id, 1, NULL));
git_reference_free(ref); 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_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)); cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./local-detached", NULL));
......
...@@ -40,7 +40,7 @@ static void connect_to_local_repository(const char *local_repository) ...@@ -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)); 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_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) void test_network_remote_local__connected(void)
...@@ -214,7 +214,7 @@ void test_network_remote_local__push_to_bare_remote(void) ...@@ -214,7 +214,7 @@ void test_network_remote_local__push_to_bare_remote(void)
/* Connect to the bare repo */ /* Connect to the bare repo */
cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localbare.git")); 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 */ /* Try to push */
cl_git_pass(git_remote_upload(localremote, &push_array, NULL)); 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) ...@@ -253,7 +253,7 @@ void test_network_remote_local__push_to_bare_remote_with_file_url(void)
/* Connect to the bare repo */ /* Connect to the bare repo */
cl_git_pass(git_remote_create_anonymous(&localremote, repo, url)); 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 */ /* Try to push */
cl_git_pass(git_remote_upload(localremote, &push_array, NULL)); 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) ...@@ -290,7 +290,7 @@ void test_network_remote_local__push_to_non_bare_remote(void)
/* Connect to the bare repo */ /* Connect to the bare repo */
cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localnonbare")); 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 */ /* Try to push */
cl_git_fail_with(GIT_EBAREREPO, git_remote_upload(localremote, &push_array, NULL)); cl_git_fail_with(GIT_EBAREREPO, git_remote_upload(localremote, &push_array, NULL));
......
...@@ -93,7 +93,7 @@ void test_network_remote_remotes__error_when_no_push_available(void) ...@@ -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"))); cl_git_pass(git_remote_create_anonymous(&r, _repo, cl_fixture("testrepo.git")));
callbacks.transport = git_transport_local; 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 */ /* Make sure that push is really not available */
r->transport->push = NULL; r->transport->push = NULL;
...@@ -359,7 +359,7 @@ void test_network_remote_remotes__can_load_with_an_empty_url(void) ...@@ -359,7 +359,7 @@ void test_network_remote_remotes__can_load_with_an_empty_url(void)
cl_assert(remote->url == NULL); cl_assert(remote->url == NULL);
cl_assert(remote->pushurl == 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() != NULL);
cl_assert(giterr_last()->klass == GITERR_INVALID); cl_assert(giterr_last()->klass == GITERR_INVALID);
...@@ -376,7 +376,7 @@ void test_network_remote_remotes__can_load_with_only_an_empty_pushurl(void) ...@@ -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->url == NULL);
cl_assert(remote->pushurl == 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); git_remote_free(remote);
} }
......
...@@ -121,6 +121,15 @@ void test_network_urlparse__user_pass_port(void) ...@@ -121,6 +121,15 @@ void test_network_urlparse__user_pass_port(void)
cl_assert_equal_s(pass, "pass"); 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) void test_network_urlparse__connection_data_http(void)
{ {
cl_git_pass(gitno_connection_data_from_url(&conndata, cl_git_pass(gitno_connection_data_from_url(&conndata,
......
...@@ -24,6 +24,9 @@ static char *_remote_ssh_pubkey = NULL; ...@@ -24,6 +24,9 @@ static char *_remote_ssh_pubkey = NULL;
static char *_remote_ssh_privkey = NULL; static char *_remote_ssh_privkey = NULL;
static char *_remote_ssh_passphrase = NULL; static char *_remote_ssh_passphrase = NULL;
static char *_remote_ssh_fingerprint = 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) void test_online_clone__initialize(void)
...@@ -46,6 +49,9 @@ 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_privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY");
_remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE"); _remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
_remote_ssh_fingerprint = cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT"); _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) void test_online_clone__cleanup(void)
...@@ -63,6 +69,9 @@ 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_privkey);
git__free(_remote_ssh_passphrase); git__free(_remote_ssh_passphrase);
git__free(_remote_ssh_fingerprint); 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) void test_online_clone__network_full(void)
...@@ -653,3 +662,38 @@ void test_online_clone__start_with_http(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)); 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);
}
...@@ -81,11 +81,11 @@ void test_online_fetch__fetch_twice(void) ...@@ -81,11 +81,11 @@ void test_online_fetch__fetch_twice(void)
{ {
git_remote *remote; git_remote *remote;
cl_git_pass(git_remote_create(&remote, _repo, "test", "git://github.com/libgit2/TestGitRepository.git")); 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)); cl_git_pass(git_remote_download(remote, NULL, NULL));
git_remote_disconnect(remote); 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)); cl_git_pass(git_remote_download(remote, NULL, NULL));
git_remote_disconnect(remote); git_remote_disconnect(remote);
...@@ -117,7 +117,7 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date ...@@ -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_repository_open(&_repository, "./fetch/lg2"));
cl_git_pass(git_remote_lookup(&remote, _repository, "origin")); 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); cl_assert_equal_i(false, invoked);
...@@ -155,7 +155,7 @@ void test_online_fetch__can_cancel(void) ...@@ -155,7 +155,7 @@ void test_online_fetch__can_cancel(void)
options.callbacks.transfer_progress = cancel_at_half; options.callbacks.transfer_progress = cancel_at_half;
options.callbacks.payload = &bytes_received; 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); cl_git_fail_with(git_remote_download(remote, NULL, &options), -4321);
git_remote_disconnect(remote); git_remote_disconnect(remote);
git_remote_free(remote); git_remote_free(remote);
...@@ -169,7 +169,7 @@ void test_online_fetch__ls_disconnected(void) ...@@ -169,7 +169,7 @@ void test_online_fetch__ls_disconnected(void)
cl_git_pass(git_remote_create(&remote, _repo, "test", cl_git_pass(git_remote_create(&remote, _repo, "test",
"http://github.com/libgit2/TestGitRepository.git")); "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)); cl_git_pass(git_remote_ls(&refs, &refs_len_before, remote));
git_remote_disconnect(remote); git_remote_disconnect(remote);
cl_git_pass(git_remote_ls(&refs, &refs_len_after, remote)); cl_git_pass(git_remote_ls(&refs, &refs_len_after, remote));
...@@ -187,7 +187,7 @@ void test_online_fetch__remote_symrefs(void) ...@@ -187,7 +187,7 @@ void test_online_fetch__remote_symrefs(void)
cl_git_pass(git_remote_create(&remote, _repo, "test", cl_git_pass(git_remote_create(&remote, _repo, "test",
"http://github.com/libgit2/TestGitRepository.git")); "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); git_remote_disconnect(remote);
cl_git_pass(git_remote_ls(&refs, &refs_len, remote)); cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
......
...@@ -372,7 +372,7 @@ void test_online_push__initialize(void) ...@@ -372,7 +372,7 @@ void test_online_push__initialize(void)
record_callbacks_data_clear(&_record_cbs_data); 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 /* 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 * set on the remote. Also, on Git 1.7.0 and newer, you must run
......
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