Unverified Commit 2bfd8ddc by Edward Thomson Committed by GitHub

Merge pull request #6175 from libgit2/ethomson/follow_redirects_initial

remote: support `http.followRedirects` (`false` and `initial`) and follow initial redirects by default
parents 6723edc7 f4fec251
......@@ -83,7 +83,7 @@ echo "##########################################################################
if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
echo "Starting git daemon..."
GITDAEMON_DIR=`mktemp -d ${TMPDIR}/gitdaemon.XXXXXXXX`
git init --bare "${GITDAEMON_DIR}/test.git"
git init --bare "${GITDAEMON_DIR}/test.git" >/dev/null
git daemon --listen=localhost --export-all --enable=receive-pack --base-path="${GITDAEMON_DIR}" "${GITDAEMON_DIR}" 2>/dev/null &
GITDAEMON_PID=$!
disown $GITDAEMON_PID
......@@ -101,8 +101,8 @@ if [ -z "$SKIP_PROXY_TESTS" ]; then
java -jar poxyproxy.jar --address 127.0.0.1 --port 8090 --credentials foo:bar --auth-type ntlm --quiet &
fi
if [ -z "$SKIP_NTLM_TESTS" ]; then
curl --location --silent --show-error https://github.com/ethomson/poxygit/releases/download/v0.4.0/poxygit-0.4.0.jar >poxygit.jar
if [ -z "$SKIP_NTLM_TESTS" -o -z "$SKIP_ONLINE_TESTS" ]; then
curl --location --silent --show-error https://github.com/ethomson/poxygit/releases/download/v0.5.1/poxygit-0.5.1.jar >poxygit.jar
echo ""
echo "Starting HTTP server..."
......@@ -112,10 +112,11 @@ if [ -z "$SKIP_NTLM_TESTS" ]; then
fi
if [ -z "$SKIP_SSH_TESTS" ]; then
echo ""
echo "Starting ssh daemon..."
HOME=`mktemp -d ${TMPDIR}/home.XXXXXXXX`
SSHD_DIR=`mktemp -d ${TMPDIR}/sshd.XXXXXXXX`
git init --bare "${SSHD_DIR}/test.git"
git init --bare "${SSHD_DIR}/test.git" >/dev/null
cat >"${SSHD_DIR}/sshd_config" <<-EOF
Port 2222
ListenAddress 0.0.0.0
......@@ -188,9 +189,11 @@ if [ -z "$SKIP_ONLINE_TESTS" ]; then
echo "## Running (online) tests"
echo "##############################################################################"
export GITTEST_FLAKY_RETRY=5
export GITTEST_REMOTE_REDIRECT_INITIAL="http://localhost:9000/initial-redirect/libgit2/TestGitRepository"
export GITTEST_REMOTE_REDIRECT_SUBSEQUENT="http://localhost:9000/subsequent-redirect/libgit2/TestGitRepository"
run_test online
unset GITTEST_FLAKY_RETRY
unset GITTEST_REMOTE_REDIRECT_INITIAL
unset GITTEST_REMOTE_REDIRECT_SUBSEQUENT
# Run the online tests that immutably change global state separately
# to avoid polluting the test environment.
......@@ -231,9 +234,7 @@ if [ -z "$SKIP_PROXY_TESTS" ]; then
export GITTEST_REMOTE_PROXY_HOST="localhost:8090"
export GITTEST_REMOTE_PROXY_USER="foo"
export GITTEST_REMOTE_PROXY_PASS="bar"
export GITTEST_FLAKY_RETRY=5
run_test proxy
unset GITTEST_FLAKY_RETRY
unset GITTEST_REMOTE_PROXY_HOST
unset GITTEST_REMOTE_PROXY_USER
unset GITTEST_REMOTE_PROXY_PASS
......
The maintainers of the libgit2 project believe that having a stable API
to program against is important for our users and the ecosystem - whether
you're building against the libgit2 C APIs directly, creating a wrapper to
a managed language, or programming against one of those managed wrappers
like LibGit2Sharp or Rugged.
Our API stability considerations are:
* Our standard API is considered stable through a major release.
* We define our "standard API" to be anything included in the "git2.h"
header - in other words, anything defined in a header in the `git2`
directory.
* APIs will maintain their signature and will not be removed within a
major release, but new APIs may be added.
* Any APIs may be marked as deprecated within a major release, but will
not be removed until the next major release (at the earliest). You
may define `GIT_DEPRECATE_HARD` to produce compiler warnings if you
target these deprecated APIs.
* We consider API compatibility to be against the C APIs. That means
that we may use macros to keep API compatibility - for example, if we
rename a structure from `git_widget_options` to `git_foobar_options`
then we would `#define git_widget_options git_foobar_options` to retain
API compatibility. Note that this does _not_ provide ABI compatibility.
* Our systems API is only considered stable through a _minor_ release.
* We define our "systems API" to be anything included in the `git2/sys`
directory. These are not "standard" APIs but are mechanisms to extend
libgit2 by adding new extensions - for example, a custom HTTPS transport,
TLS engine, or merge strategy.
* Additionally, the cmake options and the resulting constants that it
produces to be "systems API".
* Generally these mechanism are well defined and will not need significant
changes, but are considered a part of the library itself and may need
* Systems API changes will be noted specially within a release's changelog.
* Our ABI is only considered stable through a _minor_ release.
* Our ABI consists of actual symbol names in the library, the function
signatures, and the actual layout of structures. These are only
stable within minor releases, they are not stable within major releases
(yet).
* Since many FFIs use ABIs directly (for example, .NET P/Invoke or Rust),
this instability is unfortunate.
* In a future major release, we will begin providing ABI stability
throughout the major release cycle.
* ABI changes will be noted specially within a release's changelog.
* Point releases are _generally_ only for bugfixes, and generally do _not_
include new features. This means that point releases generally do _not_
include new APIs. Point releases will never break API, systems API or
ABI compatibility.
......@@ -24,31 +24,9 @@
GIT_BEGIN_DECL
/**
* Flags to pass to transport
*
* Currently unused.
*/
typedef enum {
GIT_TRANSPORTFLAGS_NONE = 0
} git_transport_flags_t;
struct git_transport {
unsigned int version; /**< The struct version */
/** Set progress and error callbacks */
int GIT_CALLBACK(set_callbacks)(
git_transport *transport,
git_transport_message_cb progress_cb,
git_transport_message_cb error_cb,
git_transport_certificate_check_cb certificate_check_cb,
void *payload);
/** Set custom headers for HTTP requests */
int GIT_CALLBACK(set_custom_headers)(
git_transport *transport,
const git_strarray *custom_headers);
/**
* Connect the transport to the remote repository, using the given
* direction.
......@@ -56,11 +34,17 @@ struct git_transport {
int GIT_CALLBACK(connect)(
git_transport *transport,
const char *url,
git_credential_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
const git_proxy_options *proxy_opts,
int direction,
int flags);
const git_remote_connect_options *connect_opts);
/**
* Resets the connect options for the given transport. This
* is useful for updating settings or callbacks for an already
* connected transport.
*/
int GIT_CALLBACK(set_connect_opts)(
git_transport *transport,
const git_remote_connect_options *connect_opts);
/**
* Get the list of available references in the remote repository.
......@@ -75,7 +59,9 @@ struct git_transport {
git_transport *transport);
/** Executes the push whose context is in the git_push object. */
int GIT_CALLBACK(push)(git_transport *transport, git_push *push, const git_remote_callbacks *callbacks);
int GIT_CALLBACK(push)(
git_transport *transport,
git_push *push);
/**
* Negotiate a fetch with the remote repository.
......@@ -99,16 +85,11 @@ struct git_transport {
int GIT_CALLBACK(download_pack)(
git_transport *transport,
git_repository *repo,
git_indexer_progress *stats,
git_indexer_progress_cb progress_cb,
void *progress_payload);
git_indexer_progress *stats);
/** Checks to see if the transport is connected */
int GIT_CALLBACK(is_connected)(git_transport *transport);
/** Reads the flags value previously passed into connect() */
int GIT_CALLBACK(read_flags)(git_transport *transport, int *flags);
/** Cancels any outstanding transport operation */
void GIT_CALLBACK(cancel)(git_transport *transport);
......
......@@ -87,10 +87,10 @@ static int filter_wants(git_remote *remote, const git_fetch_options *opts)
goto cleanup;
}
if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0)
goto cleanup;
if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
if ((error = git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote)) < 0)
goto cleanup;
for (i = 0; i < heads_len; i++) {
......@@ -134,21 +134,14 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts)
remote->refs.length);
}
int git_fetch_download_pack(git_remote *remote, const git_remote_callbacks *callbacks)
int git_fetch_download_pack(git_remote *remote)
{
git_transport *t = remote->transport;
git_indexer_progress_cb progress = NULL;
void *payload = NULL;
if (!remote->need_pack)
return 0;
if (callbacks) {
progress = callbacks->transfer_progress;
payload = callbacks->payload;
}
return t->download_pack(t, remote->repo, &remote->stats, progress, payload);
return t->download_pack(t, remote->repo, &remote->stats);
}
int git_fetch_options_init(git_fetch_options *opts, unsigned int version)
......
......@@ -15,7 +15,7 @@
int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts);
int git_fetch_download_pack(git_remote *remote, const git_remote_callbacks *callbacks);
int git_fetch_download_pack(git_remote *remote);
int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
......
......@@ -315,6 +315,7 @@ static void remove_service_suffix(
int git_net_url_apply_redirect(
git_net_url *url,
const char *redirect_location,
bool allow_offsite,
const char *service_suffix)
{
git_net_url tmp = GIT_NET_URL_INIT;
......@@ -339,8 +340,8 @@ int git_net_url_apply_redirect(
/* Validate that this is a legal redirection */
if (original->scheme &&
strcmp(original->scheme, tmp.scheme) != 0 &&
strcmp(tmp.scheme, "https") != 0) {
strcmp(original->scheme, tmp.scheme) != 0 &&
strcmp(tmp.scheme, "https") != 0) {
git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
original->scheme, tmp.scheme);
......@@ -349,6 +350,7 @@ int git_net_url_apply_redirect(
}
if (original->host &&
!allow_offsite &&
git__strcasecmp(original->host, tmp.host) != 0) {
git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
original->host, tmp.host);
......
......@@ -46,6 +46,7 @@ extern bool git_net_url_is_ipv6(git_net_url *url);
extern int git_net_url_apply_redirect(
git_net_url *url,
const char *redirect_location,
bool allow_offsite,
const char *service_suffix);
/** Swaps the contents of one URL for another. */
......
......@@ -39,8 +39,11 @@ int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src)
return 0;
}
void git_proxy_options_clear(git_proxy_options *opts)
void git_proxy_options_dispose(git_proxy_options *opts)
{
if (!opts)
return;
git__free((char *) opts->url);
opts->url = NULL;
}
......@@ -12,6 +12,6 @@
#include "git2/proxy.h"
extern int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src);
extern void git_proxy_options_clear(git_proxy_options *opts);
extern void git_proxy_options_dispose(git_proxy_options *opts);
#endif
......@@ -29,19 +29,26 @@ static int push_status_ref_cmp(const void *a, const void *b)
return strcmp(push_status_a->ref, push_status_b->ref);
}
int git_push_new(git_push **out, git_remote *remote)
int git_push_new(git_push **out, git_remote *remote, const git_push_options *opts)
{
git_push *p;
*out = NULL;
GIT_ERROR_CHECK_VERSION(opts, GIT_PUSH_OPTIONS_VERSION, "git_push_options");
p = git__calloc(1, sizeof(*p));
GIT_ERROR_CHECK_ALLOC(p);
p->repo = remote->repo;
p->remote = remote;
p->report_status = 1;
p->pb_parallelism = 1;
p->pb_parallelism = opts ? opts->pb_parallelism : 1;
if (opts) {
GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
memcpy(&p->callbacks, &opts->callbacks, sizeof(git_remote_callbacks));
}
if (git_vector_init(&p->specs, 0, push_spec_rref_cmp) < 0) {
git__free(p);
......@@ -65,20 +72,6 @@ int git_push_new(git_push **out, git_remote *remote)
return 0;
}
int git_push_set_options(git_push *push, const git_push_options *opts)
{
if (!push || !opts)
return -1;
GIT_ERROR_CHECK_VERSION(opts, GIT_PUSH_OPTIONS_VERSION, "git_push_options");
push->pb_parallelism = opts->pb_parallelism;
push->connection.custom_headers = &opts->custom_headers;
push->connection.proxy = &opts->proxy_opts;
return 0;
}
static void free_refspec(push_spec *spec)
{
if (spec == NULL)
......@@ -291,7 +284,7 @@ static int queue_objects(git_push *push)
if (git_oid_equal(&spec->loid, &spec->roid))
continue; /* up-to-date */
if (git_odb_read_header(&size, &type, push->repo->_odb, &spec->loid) < 0)
if ((error = git_odb_read_header(&size, &type, push->repo->_odb, &spec->loid)) < 0)
goto on_error;
if (type == GIT_OBJECT_TAG) {
......@@ -301,19 +294,19 @@ static int queue_objects(git_push *push)
goto on_error;
if (git_object_type(target) == GIT_OBJECT_COMMIT) {
if (git_revwalk_push(rw, git_object_id(target)) < 0) {
if ((error = git_revwalk_push(rw, git_object_id(target))) < 0) {
git_object_free(target);
goto on_error;
}
} else {
if (git_packbuilder_insert(
push->pb, git_object_id(target), NULL) < 0) {
if ((error = git_packbuilder_insert(
push->pb, git_object_id(target), NULL)) < 0) {
git_object_free(target);
goto on_error;
}
}
git_object_free(target);
} else if (git_revwalk_push(rw, &spec->loid) < 0)
} else if ((error = git_revwalk_push(rw, &spec->loid)) < 0)
goto on_error;
if (!spec->refspec.force) {
......@@ -411,10 +404,11 @@ static int calculate_work(git_push *push)
return 0;
}
static int do_push(git_push *push, const git_remote_callbacks *callbacks)
static int do_push(git_push *push)
{
int error = 0;
git_transport *transport = push->remote->transport;
git_remote_callbacks *callbacks = &push->callbacks;
if (!transport->push) {
git_error_set(GIT_ERROR_NET, "remote transport doesn't support push");
......@@ -446,7 +440,7 @@ static int do_push(git_push *push, const git_remote_callbacks *callbacks)
goto on_error;
if ((error = queue_objects(push)) < 0 ||
(error = transport->push(transport, push, callbacks)) < 0)
(error = transport->push(transport, push)) < 0)
goto on_error;
on_error:
......@@ -472,16 +466,17 @@ static int filter_refs(git_remote *remote)
return 0;
}
int git_push_finish(git_push *push, const git_remote_callbacks *callbacks)
int git_push_finish(git_push *push)
{
int error;
if (!git_remote_connected(push->remote) &&
(error = git_remote__connect(push->remote, GIT_DIRECTION_PUSH, callbacks, &push->connection)) < 0)
return error;
if (!git_remote_connected(push->remote)) {
git_error_set(GIT_ERROR_NET, "remote is disconnected");
return -1;
}
if ((error = filter_refs(push->remote)) < 0 ||
(error = do_push(push, callbacks)) < 0)
(error = do_push(push)) < 0)
return error;
if (!push->unpack_ok) {
......
......@@ -41,7 +41,7 @@ struct git_push {
/* options */
unsigned pb_parallelism;
git_remote_connection_opts connection;
git_remote_callbacks callbacks;
};
/**
......@@ -56,22 +56,11 @@ void git_push_status_free(push_status *status);
*
* @param out New push object
* @param remote Remote instance
* @param opts Push options or NULL
*
* @return 0 or an error code
*/
int git_push_new(git_push **out, git_remote *remote);
/**
* Set options on a push object
*
* @param push The push object
* @param opts The options to set on the push object
*
* @return 0 or an error code
*/
int git_push_set_options(
git_push *push,
const git_push_options *opts);
int git_push_new(git_push **out, git_remote *remote, const git_push_options *opts);
/**
* Add a refspec to be pushed
......@@ -104,11 +93,10 @@ int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks);
* order to find out which updates were accepted or rejected.
*
* @param push The push object
* @param callbacks the callbacks to use for this connection
*
* @return 0 or an error code
*/
int git_push_finish(git_push *push, const git_remote_callbacks *callbacks);
int git_push_finish(git_push *push);
/**
* Invoke callback `cb' on each status entry
......
......@@ -37,15 +37,6 @@ struct git_remote {
int passed_refspecs;
};
typedef struct git_remote_connection_opts {
const git_strarray *custom_headers;
const git_proxy_options *proxy;
} git_remote_connection_opts;
#define GIT_REMOTE_CONNECTION_OPTIONS_INIT { NULL, NULL }
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn);
int git_remote__urlfordirection(git_str *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks);
int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url);
......@@ -54,4 +45,13 @@ git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *re
int git_remote__default_branch(git_str *out, git_remote *remote);
int git_remote_connect_options_dup(
git_remote_connect_options *dst,
const git_remote_connect_options *src);
int git_remote_connect_options_normalize(
git_remote_connect_options *dst,
git_repository *repo,
const git_remote_connect_options *src);
void git_remote_connect_options_dispose(git_remote_connect_options *opts);
#endif
......@@ -38,7 +38,8 @@ typedef struct {
const char *url;
const char *request_type;
const char *response_type;
unsigned chunked : 1;
unsigned int initial : 1,
chunked : 1;
} http_service;
typedef struct {
......@@ -70,24 +71,28 @@ static const http_service upload_pack_ls_service = {
GIT_HTTP_METHOD_GET, "/info/refs?service=git-upload-pack",
NULL,
"application/x-git-upload-pack-advertisement",
1,
0
};
static const http_service upload_pack_service = {
GIT_HTTP_METHOD_POST, "/git-upload-pack",
"application/x-git-upload-pack-request",
"application/x-git-upload-pack-result",
0,
0
};
static const http_service receive_pack_ls_service = {
GIT_HTTP_METHOD_GET, "/info/refs?service=git-receive-pack",
NULL,
"application/x-git-receive-pack-advertisement",
1,
0
};
static const http_service receive_pack_service = {
GIT_HTTP_METHOD_POST, "/git-receive-pack",
"application/x-git-receive-pack-request",
"application/x-git-receive-pack-result",
0,
1
};
......@@ -174,6 +179,7 @@ GIT_INLINE(int) handle_remote_auth(
git_http_response *response)
{
http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
if (response->server_auth_credtypes == 0) {
git_error_set(GIT_ERROR_HTTP, "server requires authentication that we do not support");
......@@ -187,8 +193,8 @@ GIT_INLINE(int) handle_remote_auth(
transport->owner->url,
response->server_auth_schemetypes,
response->server_auth_credtypes,
transport->owner->cred_acquire_cb,
transport->owner->cred_acquire_payload);
connect_opts->callbacks.credentials,
connect_opts->callbacks.payload);
}
GIT_INLINE(int) handle_proxy_auth(
......@@ -196,6 +202,7 @@ GIT_INLINE(int) handle_proxy_auth(
git_http_response *response)
{
http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
if (response->proxy_auth_credtypes == 0) {
git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support");
......@@ -206,13 +213,26 @@ GIT_INLINE(int) handle_proxy_auth(
return handle_auth(
&transport->proxy,
SERVER_TYPE_PROXY,
transport->owner->proxy.url,
connect_opts->proxy_opts.url,
response->server_auth_schemetypes,
response->proxy_auth_credtypes,
transport->owner->proxy.credentials,
transport->owner->proxy.payload);
connect_opts->proxy_opts.credentials,
connect_opts->proxy_opts.payload);
}
static bool allow_redirect(http_stream *stream)
{
http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
switch (transport->owner->connect_opts.follow_redirects) {
case GIT_REMOTE_REDIRECT_INITIAL:
return (stream->service->initial == 1);
case GIT_REMOTE_REDIRECT_ALL:
return true;
default:
return false;
}
}
static int handle_response(
bool *complete,
......@@ -231,7 +251,7 @@ static int handle_response(
return -1;
}
if (git_net_url_apply_redirect(&transport->server.url, response->location, stream->service->url) < 0) {
if (git_net_url_apply_redirect(&transport->server.url, response->location, allow_redirect(stream), stream->service->url) < 0) {
return -1;
}
......@@ -286,6 +306,7 @@ static int lookup_proxy(
bool *out_use,
http_subtransport *transport)
{
git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
const char *proxy;
git_remote *remote;
char *config = NULL;
......@@ -294,9 +315,9 @@ static int lookup_proxy(
*out_use = false;
git_net_url_dispose(&transport->proxy.url);
switch (transport->owner->proxy.type) {
switch (connect_opts->proxy_opts.type) {
case GIT_PROXY_SPECIFIED:
proxy = transport->owner->proxy.url;
proxy = connect_opts->proxy_opts.url;
break;
case GIT_PROXY_AUTO:
......@@ -345,7 +366,7 @@ static int generate_request(
request->credentials = transport->server.cred;
request->proxy = use_proxy ? &transport->proxy.url : NULL;
request->proxy_credentials = transport->proxy.cred;
request->custom_headers = &transport->owner->custom_headers;
request->custom_headers = &transport->owner->connect_opts.custom_headers;
if (stream->service->method == GIT_HTTP_METHOD_POST) {
request->chunked = stream->service->chunked;
......@@ -633,6 +654,7 @@ static int http_action(
git_smart_service_t action)
{
http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
http_stream *stream;
const http_service *service;
int error;
......@@ -664,10 +686,10 @@ static int http_action(
if (!transport->http_client) {
git_http_client_options opts = {0};
opts.server_certificate_check_cb = transport->owner->certificate_check_cb;
opts.server_certificate_check_payload = transport->owner->message_cb_payload;
opts.proxy_certificate_check_cb = transport->owner->proxy.certificate_check;
opts.proxy_certificate_check_payload = transport->owner->proxy.payload;
opts.server_certificate_check_cb = connect_opts->callbacks.certificate_check;
opts.server_certificate_check_payload = connect_opts->callbacks.payload;
opts.proxy_certificate_check_cb = connect_opts->proxy_opts.certificate_check;
opts.proxy_certificate_check_payload = connect_opts->proxy_opts.payload;
if (git_http_client_new(&transport->http_client, &opts) < 0)
return -1;
......
......@@ -34,12 +34,9 @@ typedef struct {
git_remote *owner;
char *url;
int direction;
int flags;
git_atomic32 cancelled;
git_repository *repo;
git_transport_message_cb progress_cb;
git_transport_message_cb error_cb;
void *message_cb_payload;
git_remote_connect_options connect_opts;
git_vector refs;
unsigned connected : 1,
have_refs : 1;
......@@ -200,30 +197,26 @@ on_error:
static int local_connect(
git_transport *transport,
const char *url,
git_credential_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
const git_proxy_options *proxy,
int direction, int flags)
int direction,
const git_remote_connect_options *connect_opts)
{
git_repository *repo;
int error;
transport_local *t = (transport_local *) transport;
transport_local *t = (transport_local *)transport;
const char *path;
git_str buf = GIT_STR_INIT;
GIT_UNUSED(cred_acquire_cb);
GIT_UNUSED(cred_acquire_payload);
GIT_UNUSED(proxy);
if (t->connected)
return 0;
if (git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts) < 0)
return -1;
free_heads(&t->refs);
t->url = git__strdup(url);
GIT_ERROR_CHECK_ALLOC(t->url);
t->direction = direction;
t->flags = flags;
/* 'url' may be a url or path; convert to a path */
if ((error = git_fs_path_from_url_or_path(&buf, url)) < 0) {
......@@ -249,6 +242,20 @@ static int local_connect(
return 0;
}
static int local_set_connect_opts(
git_transport *transport,
const git_remote_connect_options *connect_opts)
{
transport_local *t = (transport_local *)transport;
if (!t->connected) {
git_error_set(GIT_ERROR_NET, "cannot reconfigure a transport that is not connected");
return -1;
}
return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts);
}
static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_local *t = (transport_local *)transport;
......@@ -337,10 +344,10 @@ static int transfer_to_push_transfer(const git_indexer_progress *stats, void *pa
static int local_push(
git_transport *transport,
git_push *push,
const git_remote_callbacks *cbs)
git_push *push)
{
transport_local *t = (transport_local *)transport;
git_remote_callbacks *cbs = &t->connect_opts.callbacks;
git_repository *remote_repo = NULL;
push_spec *spec;
char *url = NULL;
......@@ -349,8 +356,6 @@ static int local_push(
int error;
size_t j;
GIT_UNUSED(cbs);
/* 'push->remote->url' may be a url or path; convert to a path */
if ((error = git_fs_path_from_url_or_path(&buf, push->remote->url)) < 0) {
git_str_dispose(&buf);
......@@ -440,12 +445,11 @@ static int local_push(
}
if (push->specs.length) {
int flags = t->flags;
url = git__strdup(t->url);
if (!url || t->parent.close(&t->parent) < 0 ||
t->parent.connect(&t->parent, url,
NULL, NULL, NULL, GIT_DIRECTION_PUSH, flags))
GIT_DIRECTION_PUSH, NULL))
goto on_error;
}
......@@ -482,7 +486,7 @@ static int local_counting(int stage, unsigned int current, unsigned int total, v
transport_local *t = payload;
int error;
if (!t->progress_cb)
if (!t->connect_opts.callbacks.sideband_progress)
return 0;
if (stage == GIT_PACKBUILDER_ADDING_OBJECTS) {
......@@ -500,9 +504,19 @@ static int local_counting(int stage, unsigned int current, unsigned int total, v
if (git_str_oom(&progress_info))
return -1;
error = t->progress_cb(git_str_cstr(&progress_info), (int)git_str_len(&progress_info), t->message_cb_payload);
git_str_dispose(&progress_info);
if (progress_info.size > INT_MAX) {
git_error_set(GIT_ERROR_NET, "remote sent overly large progress data");
git_str_dispose(&progress_info);
return -1;
}
error = t->connect_opts.callbacks.sideband_progress(
progress_info.ptr,
(int)progress_info.size,
t->connect_opts.callbacks.payload);
git_str_dispose(&progress_info);
return error;
}
......@@ -532,9 +546,7 @@ static int foreach_reference_cb(git_reference *reference, void *payload)
static int local_download_pack(
git_transport *transport,
git_repository *repo,
git_indexer_progress *stats,
git_indexer_progress_cb progress_cb,
void *progress_payload)
git_indexer_progress *stats)
{
transport_local *t = (transport_local*)transport;
git_revwalk *walk = NULL;
......@@ -545,9 +557,11 @@ static int local_download_pack(
git_odb_writepack *writepack = NULL;
git_odb *odb = NULL;
git_str progress_info = GIT_STR_INIT;
foreach_data data = {0};
if ((error = git_revwalk_new(&walk, t->repo)) < 0)
goto cleanup;
git_revwalk_sorting(walk, GIT_SORT_TIME);
if ((error = git_packbuilder_new(&pack, t->repo)) < 0)
......@@ -583,44 +597,56 @@ static int local_download_pack(
if ((error = git_packbuilder_insert_walk(pack, walk)))
goto cleanup;
if ((error = git_str_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0)
goto cleanup;
if (t->progress_cb &&
(error = t->progress_cb(git_str_cstr(&progress_info), (int)git_str_len(&progress_info), t->message_cb_payload)) < 0)
goto cleanup;
if (t->connect_opts.callbacks.sideband_progress) {
if ((error = git_str_printf(
&progress_info,
counting_objects_fmt,
git_packbuilder_object_count(pack))) < 0 ||
(error = t->connect_opts.callbacks.sideband_progress(
progress_info.ptr,
(int)progress_info.size,
t->connect_opts.callbacks.payload)) < 0)
goto cleanup;
}
/* Walk the objects, building a packfile */
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
goto cleanup;
/* One last one with the newline */
git_str_clear(&progress_info);
git_str_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack));
if ((error = git_str_putc(&progress_info, '\n')) < 0)
goto cleanup;
if (t->progress_cb &&
(error = t->progress_cb(git_str_cstr(&progress_info), (int)git_str_len(&progress_info), t->message_cb_payload)) < 0)
goto cleanup;
if (t->connect_opts.callbacks.sideband_progress) {
git_str_clear(&progress_info);
if ((error = git_str_printf(
&progress_info,
counting_objects_fmt,
git_packbuilder_object_count(pack))) < 0 ||
(error = git_str_putc(&progress_info, '\n')) < 0 ||
(error = t->connect_opts.callbacks.sideband_progress(
progress_info.ptr,
(int)progress_info.size,
t->connect_opts.callbacks.payload)) < 0)
goto cleanup;
}
if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0)
if ((error = git_odb_write_pack(
&writepack,
odb,
t->connect_opts.callbacks.transfer_progress,
t->connect_opts.callbacks.payload)) < 0)
goto cleanup;
/* Write the data to the ODB */
{
foreach_data data = {0};
data.stats = stats;
data.progress_cb = progress_cb;
data.progress_payload = progress_payload;
data.writepack = writepack;
data.stats = stats;
data.progress_cb = t->connect_opts.callbacks.transfer_progress;
data.progress_payload = t->connect_opts.callbacks.payload;
data.writepack = writepack;
/* autodetect */
git_packbuilder_set_threads(pack, 0);
/* autodetect */
git_packbuilder_set_threads(pack, 0);
if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0)
goto cleanup;
}
if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0)
goto cleanup;
error = writepack->commit(writepack, stats);
......@@ -632,24 +658,6 @@ cleanup:
return error;
}
static int local_set_callbacks(
git_transport *transport,
git_transport_message_cb progress_cb,
git_transport_message_cb error_cb,
git_transport_certificate_check_cb certificate_check_cb,
void *message_cb_payload)
{
transport_local *t = (transport_local *)transport;
GIT_UNUSED(certificate_check_cb);
t->progress_cb = progress_cb;
t->error_cb = error_cb;
t->message_cb_payload = message_cb_payload;
return 0;
}
static int local_is_connected(git_transport *transport)
{
transport_local *t = (transport_local *)transport;
......@@ -657,15 +665,6 @@ static int local_is_connected(git_transport *transport)
return t->connected;
}
static int local_read_flags(git_transport *transport, int *flags)
{
transport_local *t = (transport_local *)transport;
*flags = t->flags;
return 0;
}
static void local_cancel(git_transport *transport)
{
transport_local *t = (transport_local *)transport;
......@@ -720,8 +719,8 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
GIT_ERROR_CHECK_ALLOC(t);
t->parent.version = GIT_TRANSPORT_VERSION;
t->parent.set_callbacks = local_set_callbacks;
t->parent.connect = local_connect;
t->parent.set_connect_opts = local_set_connect_opts;
t->parent.negotiate_fetch = local_negotiate_fetch;
t->parent.download_pack = local_download_pack;
t->parent.push = local_push;
......@@ -729,7 +728,6 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
t->parent.free = local_free;
t->parent.ls = local_ls;
t->parent.is_connected = local_is_connected;
t->parent.read_flags = local_read_flags;
t->parent.cancel = local_cancel;
if ((error = git_vector_init(&t->refs, 0, NULL)) < 0) {
......
......@@ -56,101 +56,6 @@ GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransp
return 0;
}
static int git_smart__set_callbacks(
git_transport *transport,
git_transport_message_cb progress_cb,
git_transport_message_cb error_cb,
git_transport_certificate_check_cb certificate_check_cb,
void *message_cb_payload)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
t->progress_cb = progress_cb;
t->error_cb = error_cb;
t->certificate_check_cb = certificate_check_cb;
t->message_cb_payload = message_cb_payload;
return 0;
}
static size_t http_header_name_length(const char *http_header)
{
const char *colon = strchr(http_header, ':');
if (!colon)
return 0;
return colon - http_header;
}
static bool is_malformed_http_header(const char *http_header)
{
const char *c;
size_t name_len;
/* Disallow \r and \n */
c = strchr(http_header, '\r');
if (c)
return true;
c = strchr(http_header, '\n');
if (c)
return true;
/* Require a header name followed by : */
name_len = http_header_name_length(http_header);
if (name_len < 1)
return true;
return false;
}
static char *forbidden_custom_headers[] = {
"User-Agent",
"Host",
"Accept",
"Content-Type",
"Transfer-Encoding",
"Content-Length",
};
static bool is_forbidden_custom_header(const char *custom_header)
{
unsigned long i;
size_t name_len = http_header_name_length(custom_header);
/* Disallow headers that we set */
for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++)
if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0)
return true;
return false;
}
static int git_smart__set_custom_headers(
git_transport *transport,
const git_strarray *custom_headers)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
size_t i;
if (t->custom_headers.count)
git_strarray_dispose(&t->custom_headers);
if (!custom_headers)
return 0;
for (i = 0; i < custom_headers->count; i++) {
if (is_malformed_http_header(custom_headers->strings[i])) {
git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]);
return -1;
}
if (is_forbidden_custom_header(custom_headers->strings[i])) {
git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is already set by libgit2", custom_headers->strings[i]);
return -1;
}
}
return git_strarray_copy(&t->custom_headers, custom_headers);
}
int git_smart__update_heads(transport_smart *t, git_vector *symrefs)
{
size_t i;
......@@ -206,11 +111,8 @@ static void free_symrefs(git_vector *symrefs)
static int git_smart__connect(
git_transport *transport,
const char *url,
git_credential_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
const git_proxy_options *proxy,
int direction,
int flags)
const git_remote_connect_options *connect_opts)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
git_smart_subtransport_stream *stream;
......@@ -223,24 +125,19 @@ static int git_smart__connect(
if (git_smart__reset_stream(t, true) < 0)
return -1;
if (git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts) < 0)
return -1;
t->url = git__strdup(url);
GIT_ERROR_CHECK_ALLOC(t->url);
git_proxy_options_clear(&t->proxy);
if (git_proxy_options_dup(&t->proxy, proxy) < 0)
return -1;
t->direction = direction;
t->flags = flags;
t->cred_acquire_cb = cred_acquire_cb;
t->cred_acquire_payload = cred_acquire_payload;
if (GIT_DIRECTION_FETCH == t->direction)
if (GIT_DIRECTION_FETCH == t->direction) {
service = GIT_SERVICE_UPLOADPACK_LS;
else if (GIT_DIRECTION_PUSH == t->direction)
} else if (GIT_DIRECTION_PUSH == t->direction) {
service = GIT_SERVICE_RECEIVEPACK_LS;
else {
} else {
git_error_set(GIT_ERROR_NET, "invalid direction");
return -1;
}
......@@ -315,6 +212,20 @@ cleanup:
return error;
}
static int git_smart__set_connect_opts(
git_transport *transport,
const git_remote_connect_options *opts)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
if (!t->connected) {
git_error_set(GIT_ERROR_NET, "cannot reconfigure a transport that is not connected");
return -1;
}
return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, opts);
}
static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
......@@ -401,15 +312,6 @@ static int git_smart__is_connected(git_transport *transport)
return t->connected;
}
static int git_smart__read_flags(git_transport *transport, int *flags)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
*flags = t->flags;
return 0;
}
static int git_smart__close(git_transport *transport)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
......@@ -465,9 +367,8 @@ static void git_smart__free(git_transport *transport)
git_pkt_free(p);
git_vector_free(refs);
git__free((char *)t->proxy.url);
git_strarray_dispose(&t->custom_headers);
git_remote_connect_options_dispose(&t->connect_opts);
git__free(t);
}
......@@ -482,34 +383,30 @@ static int ref_name_cmp(const void *a, const void *b)
int git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
git_remote_connect_options *connect_opts = &t->connect_opts;
GIT_ASSERT_ARG(transport);
GIT_ASSERT_ARG(cert);
GIT_ASSERT_ARG(hostname);
if (!t->certificate_check_cb)
if (!connect_opts->callbacks.certificate_check)
return GIT_PASSTHROUGH;
return t->certificate_check_cb(cert, valid, hostname, t->message_cb_payload);
return connect_opts->callbacks.certificate_check(cert, valid, hostname, connect_opts->callbacks.payload);
}
int git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
git_remote_connect_options *connect_opts = &t->connect_opts;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(transport);
if (!t->cred_acquire_cb)
if (!connect_opts->callbacks.credentials)
return GIT_PASSTHROUGH;
return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload);
}
int git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
return git_proxy_options_dup(out, &t->proxy);
return connect_opts->callbacks.credentials(out, t->url, user, methods, connect_opts->callbacks.payload);
}
int git_transport_smart(git_transport **out, git_remote *owner, void *param)
......@@ -524,9 +421,8 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
GIT_ERROR_CHECK_ALLOC(t);
t->parent.version = GIT_TRANSPORT_VERSION;
t->parent.set_callbacks = git_smart__set_callbacks;
t->parent.set_custom_headers = git_smart__set_custom_headers;
t->parent.connect = git_smart__connect;
t->parent.set_connect_opts = git_smart__set_connect_opts;
t->parent.close = git_smart__close;
t->parent.free = git_smart__free;
t->parent.negotiate_fetch = git_smart__negotiate_fetch;
......@@ -534,7 +430,6 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
t->parent.push = git_smart__push;
t->parent.ls = git_smart__ls;
t->parent.is_connected = git_smart__is_connected;
t->parent.read_flags = git_smart__read_flags;
t->parent.cancel = git_smart__cancel;
t->owner = owner;
......
......@@ -137,16 +137,8 @@ typedef struct {
git_transport parent;
git_remote *owner;
char *url;
git_credential_acquire_cb cred_acquire_cb;
void *cred_acquire_payload;
git_proxy_options proxy;
git_remote_connect_options connect_opts;
int direction;
int flags;
git_transport_message_cb progress_cb;
git_transport_message_cb error_cb;
git_transport_certificate_check_cb certificate_check_cb;
void *message_cb_payload;
git_strarray custom_headers;
git_smart_subtransport *wrapped;
git_smart_subtransport_stream *current_stream;
transport_smart_caps caps;
......@@ -157,8 +149,8 @@ typedef struct {
packetsize_cb packetsize_cb;
void *packetsize_payload;
unsigned rpc : 1,
have_refs : 1,
connected : 1;
have_refs : 1,
connected : 1;
gitno_buffer buffer;
char buffer_data[65536];
} transport_smart;
......@@ -166,7 +158,7 @@ typedef struct {
/* smart_protocol.c */
int git_smart__store_refs(transport_smart *t, int flushes);
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs);
int git_smart__push(git_transport *transport, git_push *push, const git_remote_callbacks *cbs);
int git_smart__push(git_transport *transport, git_push *push);
int git_smart__negotiate_fetch(
git_transport *transport,
......@@ -177,9 +169,7 @@ int git_smart__negotiate_fetch(
int git_smart__download_pack(
git_transport *transport,
git_repository *repo,
git_indexer_progress *stats,
git_indexer_progress_cb progress_cb,
void *progress_payload);
git_indexer_progress *stats);
/* smart.c */
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
......
......@@ -512,9 +512,7 @@ static int network_packetsize(size_t received, void *payload)
int git_smart__download_pack(
git_transport *transport,
git_repository *repo,
git_indexer_progress *stats,
git_indexer_progress_cb progress_cb,
void *progress_payload)
git_indexer_progress *stats)
{
transport_smart *t = (transport_smart *)transport;
gitno_buffer *buf = &t->buffer;
......@@ -523,6 +521,10 @@ int git_smart__download_pack(
int error = 0;
struct network_packetsize_payload npp = {0};
// TODO
git_indexer_progress_cb progress_cb = t->connect_opts.callbacks.transfer_progress;
void *progress_payload = t->connect_opts.callbacks.payload;
memset(stats, 0, sizeof(git_indexer_progress));
if (progress_cb) {
......@@ -568,7 +570,7 @@ int git_smart__download_pack(
git_error_clear();
error = GIT_EUSER;
} else if (pkt->type == GIT_PKT_PROGRESS) {
if (t->progress_cb) {
if (t->connect_opts.callbacks.sideband_progress) {
git_pkt_progress *p = (git_pkt_progress *) pkt;
if (p->len > INT_MAX) {
......@@ -577,7 +579,7 @@ int git_smart__download_pack(
goto done;
}
error = t->progress_cb(p->data, (int)p->len, t->message_cb_payload);
error = t->connect_opts.callbacks.sideband_progress(p->data, (int)p->len, t->connect_opts.callbacks.payload);
}
} else if (pkt->type == GIT_PKT_DATA) {
git_pkt_data *p = (git_pkt_data *) pkt;
......@@ -811,7 +813,7 @@ static int parse_report(transport_smart *transport, git_push *push)
error = -1;
break;
case GIT_PKT_PROGRESS:
if (transport->progress_cb) {
if (transport->connect_opts.callbacks.sideband_progress) {
git_pkt_progress *p = (git_pkt_progress *) pkt;
if (p->len > INT_MAX) {
......@@ -820,7 +822,7 @@ static int parse_report(transport_smart *transport, git_push *push)
goto done;
}
error = transport->progress_cb(p->data, (int)p->len, transport->message_cb_payload);
error = transport->connect_opts.callbacks.sideband_progress(p->data, (int)p->len, transport->connect_opts.callbacks.payload);
}
break;
default:
......@@ -987,9 +989,10 @@ static int stream_thunk(void *buf, size_t size, void *data)
return error;
}
int git_smart__push(git_transport *transport, git_push *push, const git_remote_callbacks *cbs)
int git_smart__push(git_transport *transport, git_push *push)
{
transport_smart *t = (transport_smart *)transport;
git_remote_callbacks *cbs = &t->connect_opts.callbacks;
struct push_packbuilder_payload packbuilder_payload = {0};
git_str pktline = GIT_STR_INIT;
int error = 0, need_pack = 0;
......
......@@ -441,11 +441,15 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char *
int error, no_callback = 0;
git_credential *cred = NULL;
if (!t->owner->cred_acquire_cb) {
if (!t->owner->connect_opts.callbacks.credentials) {
no_callback = 1;
} else {
error = t->owner->cred_acquire_cb(&cred, t->owner->url, user, auth_methods,
t->owner->cred_acquire_payload);
error = t->owner->connect_opts.callbacks.credentials(
&cred,
t->owner->url,
user,
auth_methods,
t->owner->connect_opts.callbacks.payload);
if (error == GIT_PASSTHROUGH) {
no_callback = 1;
......@@ -558,7 +562,7 @@ post_extract:
if ((error = _git_ssh_session_create(&session, s->io)) < 0)
goto done;
if (t->owner->certificate_check_cb != NULL) {
if (t->owner->connect_opts.callbacks.certificate_check != NULL) {
git_cert_hostkey cert = {{ 0 }}, *cert_ptr;
const char *key;
size_t cert_len;
......@@ -632,7 +636,11 @@ post_extract:
cert_ptr = &cert;
error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 0, urldata.host, t->owner->message_cb_payload);
error = t->owner->connect_opts.callbacks.certificate_check(
(git_cert *)cert_ptr,
0,
urldata.host,
t->owner->connect_opts.callbacks.payload);
if (error < 0 && error != GIT_PASSTHROUGH) {
if (!git_error_last())
......
......@@ -293,14 +293,14 @@ static int certificate_check(winhttp_stream *s, int valid)
git_cert_x509 cert;
/* If there is no override, we should fail if WinHTTP doesn't think it's fine */
if (t->owner->certificate_check_cb == NULL && !valid) {
if (t->owner->connect_opts.callbacks.certificate_check == NULL && !valid) {
if (!git_error_last())
git_error_set(GIT_ERROR_HTTP, "unknown certificate check failure");
return GIT_ECERTIFICATE;
}
if (t->owner->certificate_check_cb == NULL || git__strcmp(t->server.url.scheme, "https") != 0)
if (t->owner->connect_opts.callbacks.certificate_check == NULL || git__strcmp(t->server.url.scheme, "https") != 0)
return 0;
if (!WinHttpQueryOption(s->request, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert_ctx, &cert_ctx_size)) {
......@@ -312,7 +312,7 @@ static int certificate_check(winhttp_stream *s, int valid)
cert.parent.cert_type = GIT_CERT_X509;
cert.data = cert_ctx->pbCertEncoded;
cert.len = cert_ctx->cbCertEncoded;
error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->server.url.host, t->owner->message_cb_payload);
error = t->owner->connect_opts.callbacks.certificate_check((git_cert *) &cert, valid, t->server.url.host, t->owner->connect_opts.callbacks.payload);
CertFreeCertificateContext(cert_ctx);
if (error == GIT_PASSTHROUGH)
......@@ -426,7 +426,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error;
}
proxy_opts = &t->owner->proxy;
proxy_opts = &t->owner->connect_opts.proxy_opts;
if (proxy_opts->type == GIT_PROXY_AUTO) {
/* Set proxy if necessary */
if (git_remote__http_proxy(&proxy_url, t->owner->owner, &t->server.url) < 0)
......@@ -560,10 +560,10 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
}
for (i = 0; i < t->owner->custom_headers.count; i++) {
if (t->owner->custom_headers.strings[i]) {
for (i = 0; i < t->owner->connect_opts.custom_headers.count; i++) {
if (t->owner->connect_opts.custom_headers.strings[i]) {
git_str_clear(&buf);
git_str_puts(&buf, t->owner->custom_headers.strings[i]);
git_str_puts(&buf, t->owner->connect_opts.custom_headers.strings[i]);
if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert custom header to wide characters");
goto on_error;
......@@ -577,14 +577,6 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
}
/* If requested, disable certificate validation */
if (strcmp(t->server.url.scheme, "https") == 0) {
int flags;
if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
goto on_error;
}
if ((error = apply_credentials(s->request, &t->server.url, WINHTTP_AUTH_TARGET_SERVER, t->server.cred, t->server.auth_mechanisms)) < 0)
goto on_error;
......@@ -1197,8 +1189,10 @@ replay:
winhttp_stream_close(s);
if (!git__prefixcmp_icase(location8, prefix_https)) {
bool follow = (t->owner->connect_opts.follow_redirects != GIT_REMOTE_REDIRECT_NONE);
/* Upgrade to secure connection; disconnect and start over */
if (git_net_url_apply_redirect(&t->server.url, location8, s->service_url) < 0) {
if (git_net_url_apply_redirect(&t->server.url, location8, follow, s->service_url) < 0) {
git__free(location8);
return -1;
}
......@@ -1218,8 +1212,8 @@ replay:
int error = acquire_credentials(s->request,
&t->server,
t->owner->url,
t->owner->cred_acquire_cb,
t->owner->cred_acquire_payload);
t->owner->connect_opts.callbacks.credentials,
t->owner->connect_opts.callbacks.payload);
if (error < 0) {
return error;
......@@ -1231,9 +1225,9 @@ replay:
} else if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) {
int error = acquire_credentials(s->request,
&t->proxy,
t->owner->proxy.url,
t->owner->proxy.credentials,
t->owner->proxy.payload);
t->owner->connect_opts.proxy_opts.url,
t->owner->connect_opts.proxy_opts.credentials,
t->owner->connect_opts.proxy_opts.payload);
if (error < 0) {
return error;
......
......@@ -17,9 +17,9 @@ void test_network_url_redirect__cleanup(void)
void test_network_url_redirect__redirect_http(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"http://example.com/foo/bar/baz"));
"http://example.com/foo/bar/baz"));
cl_git_pass(git_net_url_apply_redirect(&conndata,
"http://example.com/foo/bar/baz", "bar/baz"));
"http://example.com/foo/bar/baz", false, "bar/baz"));
cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "80");
......@@ -31,9 +31,9 @@ void test_network_url_redirect__redirect_http(void)
void test_network_url_redirect__redirect_ssl(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"https://example.com/foo/bar/baz"));
"https://example.com/foo/bar/baz"));
cl_git_pass(git_net_url_apply_redirect(&conndata,
"https://example.com/foo/bar/baz", "bar/baz"));
"https://example.com/foo/bar/baz", false, "bar/baz"));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443");
......@@ -45,9 +45,9 @@ void test_network_url_redirect__redirect_ssl(void)
void test_network_url_redirect__redirect_leaves_root_path(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"https://example.com/foo/bar/baz"));
"https://example.com/foo/bar/baz"));
cl_git_pass(git_net_url_apply_redirect(&conndata,
"https://example.com/foo/bar/baz", "/foo/bar/baz"));
"https://example.com/foo/bar/baz", false, "/foo/bar/baz"));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443");
......@@ -59,9 +59,9 @@ void test_network_url_redirect__redirect_leaves_root_path(void)
void test_network_url_redirect__redirect_encoded_username_password(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz"));
"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz"));
cl_git_pass(git_net_url_apply_redirect(&conndata,
"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz"));
"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", false, "bar/baz"));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443");
......@@ -70,27 +70,42 @@ void test_network_url_redirect__redirect_encoded_username_password(void)
cl_assert_equal_s(conndata.password, "pass@word%zyx%v");
}
void test_network_url_redirect__redirect_cross_host_allowed(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"https://bar.com/bar/baz"));
cl_git_pass(git_net_url_apply_redirect(&conndata,
"https://foo.com/bar/baz", true, NULL));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(conndata.path, "/bar/baz");
cl_assert_equal_p(conndata.username, NULL);
cl_assert_equal_p(conndata.password, NULL);
}
void test_network_url_redirect__redirect_cross_host_denied(void)
{
cl_git_pass(git_net_url_parse(&conndata, "https://bar.com/bar/baz"));
cl_git_pass(git_net_url_parse(&conndata,
"https://bar.com/bar/baz"));
cl_git_fail_with(git_net_url_apply_redirect(&conndata,
"https://foo.com/bar/baz", NULL),
-1);
"https://foo.com/bar/baz", false, NULL), -1);
}
void test_network_url_redirect__redirect_http_downgrade_denied(void)
{
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz"));
cl_git_pass(git_net_url_parse(&conndata,
"https://foo.com/bar/baz"));
cl_git_fail_with(git_net_url_apply_redirect(&conndata,
"http://foo.com/bar/baz", NULL),
-1);
"http://foo.com/bar/baz", true, NULL), -1);
}
void test_network_url_redirect__redirect_relative(void)
{
cl_git_pass(git_net_url_parse(&conndata, "http://foo.com/bar/baz/biff"));
cl_git_pass(git_net_url_parse(&conndata,
"http://foo.com/bar/baz/biff"));
cl_git_pass(git_net_url_apply_redirect(&conndata,
"/zap/baz/biff?bam", NULL));
"/zap/baz/biff?bam", true, NULL));
cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "80");
......@@ -101,9 +116,10 @@ void test_network_url_redirect__redirect_relative(void)
void test_network_url_redirect__redirect_relative_ssl(void)
{
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz/biff"));
cl_git_pass(git_net_url_parse(&conndata,
"https://foo.com/bar/baz/biff"));
cl_git_pass(git_net_url_apply_redirect(&conndata,
"/zap/baz/biff?bam", NULL));
"/zap/baz/biff?bam", true, NULL));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "443");
......@@ -114,16 +130,18 @@ void test_network_url_redirect__redirect_relative_ssl(void)
void test_network_url_redirect__service_query_no_query_params_in_location(void)
{
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/info/refs?service=git-upload-pack"));
cl_git_pass(git_net_url_parse(&conndata,
"https://foo.com/bar/info/refs?service=git-upload-pack"));
cl_git_pass(git_net_url_apply_redirect(&conndata,
"/baz/info/refs", "/info/refs?service=git-upload-pack"));
"/baz/info/refs", true, "/info/refs?service=git-upload-pack"));
cl_assert_equal_s(conndata.path, "/baz");
}
void test_network_url_redirect__service_query_with_query_params_in_location(void)
{
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/info/refs?service=git-upload-pack"));
cl_git_pass(git_net_url_parse(&conndata,
"https://foo.com/bar/info/refs?service=git-upload-pack"));
cl_git_pass(git_net_url_apply_redirect(&conndata,
"/baz/info/refs?service=git-upload-pack", "/info/refs?service=git-upload-pack"));
"/baz/info/refs?service=git-upload-pack", true, "/info/refs?service=git-upload-pack"));
cl_assert_equal_s(conndata.path, "/baz");
}
......@@ -32,6 +32,8 @@ static char *_remote_proxy_user = NULL;
static char *_remote_proxy_pass = NULL;
static char *_remote_proxy_selfsigned = NULL;
static char *_remote_expectcontinue = NULL;
static char *_remote_redirect_initial = NULL;
static char *_remote_redirect_subsequent = NULL;
static int _orig_proxies_need_reset = 0;
static char *_orig_http_proxy = NULL;
......@@ -78,6 +80,8 @@ void test_online_clone__initialize(void)
_remote_proxy_pass = cl_getenv("GITTEST_REMOTE_PROXY_PASS");
_remote_proxy_selfsigned = cl_getenv("GITTEST_REMOTE_PROXY_SELFSIGNED");
_remote_expectcontinue = cl_getenv("GITTEST_REMOTE_EXPECTCONTINUE");
_remote_redirect_initial = cl_getenv("GITTEST_REMOTE_REDIRECT_INITIAL");
_remote_redirect_subsequent = cl_getenv("GITTEST_REMOTE_REDIRECT_SUBSEQUENT");
if (_remote_expectcontinue)
git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 1);
......@@ -92,6 +96,8 @@ void test_online_clone__cleanup(void)
g_repo = NULL;
}
cl_fixture_cleanup("./foo");
cl_fixture_cleanup("./initial");
cl_fixture_cleanup("./subsequent");
git__free(_remote_url);
git__free(_remote_user);
......@@ -107,6 +113,8 @@ void test_online_clone__cleanup(void)
git__free(_remote_proxy_pass);
git__free(_remote_proxy_selfsigned);
git__free(_remote_expectcontinue);
git__free(_remote_redirect_initial);
git__free(_remote_redirect_subsequent);
if (_orig_proxies_need_reset) {
cl_setenv("HTTP_PROXY", _orig_http_proxy);
......@@ -938,3 +946,59 @@ void test_online_clone__path_whitespace(void)
cl_git_pass(git_clone(&g_repo, "https://libgit2@dev.azure.com/libgit2/test/_git/spaces%20in%20the%20name", "./foo", &g_options));
cl_assert(git_fs_path_exists("./foo/master.txt"));
}
void test_online_clone__redirect_default_succeeds_for_initial(void)
{
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
if (!_remote_redirect_initial || !_remote_redirect_subsequent)
cl_skip();
cl_git_pass(git_clone(&g_repo, _remote_redirect_initial, "./initial", &options));
}
void test_online_clone__redirect_default_fails_for_subsequent(void)
{
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
if (!_remote_redirect_initial || !_remote_redirect_subsequent)
cl_skip();
cl_git_fail(git_clone(&g_repo, _remote_redirect_subsequent, "./fail", &options));
}
void test_online_clone__redirect_none(void)
{
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
if (!_remote_redirect_initial)
cl_skip();
options.fetch_opts.follow_redirects = GIT_REMOTE_REDIRECT_NONE;
cl_git_fail(git_clone(&g_repo, _remote_redirect_initial, "./fail", &options));
}
void test_online_clone__redirect_initial_succeeds_for_initial(void)
{
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
if (!_remote_redirect_initial || !_remote_redirect_subsequent)
cl_skip();
options.fetch_opts.follow_redirects = GIT_REMOTE_REDIRECT_INITIAL;
cl_git_pass(git_clone(&g_repo, _remote_redirect_initial, "./initial", &options));
}
void test_online_clone__redirect_initial_fails_for_subsequent(void)
{
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
if (!_remote_redirect_initial || !_remote_redirect_subsequent)
cl_skip();
options.fetch_opts.follow_redirects = GIT_REMOTE_REDIRECT_INITIAL;
cl_git_fail(git_clone(&g_repo, _remote_redirect_subsequent, "./fail", &options));
}
......@@ -7,15 +7,19 @@ static char *_remote_proxy_scheme = NULL;
static char *_remote_proxy_host = NULL;
static char *_remote_proxy_user = NULL;
static char *_remote_proxy_pass = NULL;
static char *_remote_redirect_initial = NULL;
static char *_remote_redirect_subsequent = NULL;
void test_online_fetch__initialize(void)
{
cl_git_pass(git_repository_init(&_repo, "./fetch", 0));
_remote_proxy_scheme = cl_getenv("GITTEST_REMOTE_PROXY_SCHEME");
_remote_proxy_host = cl_getenv("GITTEST_REMOTE_PROXY_HOST");
_remote_proxy_user = cl_getenv("GITTEST_REMOTE_PROXY_USER");
_remote_proxy_pass = cl_getenv("GITTEST_REMOTE_PROXY_PASS");
_remote_proxy_scheme = cl_getenv("GITTEST_REMOTE_PROXY_SCHEME");
_remote_proxy_host = cl_getenv("GITTEST_REMOTE_PROXY_HOST");
_remote_proxy_user = cl_getenv("GITTEST_REMOTE_PROXY_USER");
_remote_proxy_pass = cl_getenv("GITTEST_REMOTE_PROXY_PASS");
_remote_redirect_initial = cl_getenv("GITTEST_REMOTE_REDIRECT_INITIAL");
_remote_redirect_subsequent = cl_getenv("GITTEST_REMOTE_REDIRECT_SUBSEQUENT");
}
void test_online_fetch__cleanup(void)
......@@ -24,11 +28,14 @@ void test_online_fetch__cleanup(void)
_repo = NULL;
cl_fixture_cleanup("./fetch");
git__free(_remote_proxy_scheme);
git__free(_remote_proxy_host);
git__free(_remote_proxy_user);
git__free(_remote_proxy_pass);
cl_fixture_cleanup("./redirected");
git__free(_remote_proxy_scheme);
git__free(_remote_proxy_host);
git__free(_remote_proxy_user);
git__free(_remote_proxy_pass);
git__free(_remote_redirect_initial);
git__free(_remote_redirect_subsequent);
}
static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data)
......@@ -247,3 +254,44 @@ void test_online_fetch__proxy(void)
git_remote_free(remote);
git_str_dispose(&url);
}
static int do_redirected_fetch(const char *url, const char *name, const char *config)
{
git_repository *repo;
git_remote *remote;
int error;
cl_git_pass(git_repository_init(&repo, "./redirected", 0));
cl_fixture_cleanup(name);
if (config)
cl_repo_set_string(repo, "http.followRedirects", config);
cl_git_pass(git_remote_create(&remote, repo, name, url));
error = git_remote_fetch(remote, NULL, NULL, NULL);
git_remote_free(remote);
git_repository_free(repo);
cl_fixture_cleanup("./redirected");
return error;
}
void test_online_fetch__redirect_config(void)
{
if (!_remote_redirect_initial || !_remote_redirect_subsequent)
cl_skip();
/* config defaults */
cl_git_pass(do_redirected_fetch(_remote_redirect_initial, "initial", NULL));
cl_git_fail(do_redirected_fetch(_remote_redirect_subsequent, "subsequent", NULL));
/* redirect=initial */
cl_git_pass(do_redirected_fetch(_remote_redirect_initial, "initial", "initial"));
cl_git_fail(do_redirected_fetch(_remote_redirect_subsequent, "subsequent", "initial"));
/* redirect=false */
cl_git_fail(do_redirected_fetch(_remote_redirect_initial, "initial", "false"));
cl_git_fail(do_redirected_fetch(_remote_redirect_subsequent, "subsequent", "false"));
}
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