Unverified Commit 4460bf40 by Edward Thomson Committed by GitHub

Merge pull request #5286 from libgit2/ethomson/gssapi

HTTP: Support Apache-based servers with Negotiate
parents 9bcf10e9 e9cef7c4
......@@ -19,7 +19,8 @@ jobs:
environmentVariables: |
CC=gcc
CMAKE_GENERATOR=Ninja
CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on
CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on -DUSE_GSSAPI=ON
GITTEST_NEGOTIATE_PASSWORD=$(GITTEST_NEGOTIATE_PASSWORD)
- job: linux_amd64_xenial_gcc_mbedtls
displayName: 'Linux (amd64; Xenial; GCC; mbedTLS)'
......@@ -34,7 +35,8 @@ jobs:
environmentVariables: |
CC=gcc
CMAKE_GENERATOR=Ninja
CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on
CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on -DUSE_GSSAPI=ON
GITTEST_NEGOTIATE_PASSWORD=$(GITTEST_NEGOTIATE_PASSWORD)
- job: linux_amd64_xenial_clang_openssl
displayName: 'Linux (amd64; Xenial; Clang; OpenSSL)'
......@@ -49,7 +51,8 @@ jobs:
environmentVariables: |
CC=clang
CMAKE_GENERATOR=Ninja
CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on
CMAKE_OPTIONS=-DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on -DUSE_GSSAPI=ON
GITTEST_NEGOTIATE_PASSWORD=$(GITTEST_NEGOTIATE_PASSWORD)
- job: linux_amd64_xenial_clang_mbedtls
displayName: 'Linux (amd64; Xenial; Clang; mbedTLS)'
......@@ -64,7 +67,8 @@ jobs:
environmentVariables: |
CC=clang
CMAKE_GENERATOR=Ninja
CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on
CMAKE_OPTIONS=-DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DVALGRIND=on -DUSE_GSSAPI=ON
GITTEST_NEGOTIATE_PASSWORD=$(GITTEST_NEGOTIATE_PASSWORD)
- job: macos
displayName: 'macOS'
......@@ -81,6 +85,7 @@ jobs:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
SKIP_SSH_TESTS: true
GITTEST_NEGOTIATE_PASSWORD: $(GITTEST_NEGOTIATE_PASSWORD)
- job: windows_vs_amd64
displayName: 'Windows (amd64; Visual Studio)'
......@@ -92,6 +97,7 @@ jobs:
CMAKE_GENERATOR: Visual Studio 12 2013 Win64
CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- job: windows_vs_x86
displayName: 'Windows (x86; Visual Studio)'
......@@ -103,6 +109,7 @@ jobs:
CMAKE_GENERATOR: Visual Studio 12 2013
CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- job: windows_mingw_amd64
displayName: 'Windows (amd64; MinGW)'
......@@ -120,6 +127,7 @@ jobs:
CMAKE_GENERATOR: MinGW Makefiles
CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- job: windows_mingw_x86
displayName: 'Windows (x86; MinGW)'
......@@ -138,6 +146,7 @@ jobs:
CMAKE_GENERATOR: MinGW Makefiles
CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- job: documentation
displayName: 'Generate Documentation'
......
ARG BASE
FROM $BASE AS apt
RUN apt-get update && \
apt-get install -y --no-install-recommends \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
bzip2 \
clang \
cmake \
......@@ -9,8 +9,10 @@ RUN apt-get update && \
gcc \
git \
gosu \
krb5-user \
libcurl4-gnutls-dev \
libgcrypt20-dev \
libkrb5-dev \
libpcre3-dev \
libssl-dev \
libz-dev \
......
......@@ -6,6 +6,11 @@ if [ -n "$SKIP_TESTS" ]; then
exit 0
fi
# Windows doesn't run the NTLM tests properly (yet)
if [[ "$(uname -s)" == MINGW* ]]; then
SKIP_NTLM_TESTS=1
fi
SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
BUILD_DIR=$(pwd)
TMPDIR=${TMPDIR:-/tmp}
......@@ -89,6 +94,16 @@ 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 -L https://github.com/ethomson/poxygit/releases/download/v0.4.0/poxygit-0.4.0.jar >poxygit.jar
echo ""
echo "Starting HTTP server..."
NTLM_DIR=`mktemp -d ${TMPDIR}/ntlm.XXXXXXXX`
git init --bare "${NTLM_DIR}/test.git"
java -jar poxygit.jar --address 127.0.0.1 --port 9000 --credentials foo:baz --quiet "${NTLM_DIR}" &
fi
if [ -z "$SKIP_SSH_TESTS" ]; then
echo "Starting ssh daemon..."
HOME=`mktemp -d ${TMPDIR}/home.XXXXXXXX`
......@@ -207,6 +222,65 @@ if [ -z "$SKIP_PROXY_TESTS" ]; then
unset GITTEST_REMOTE_PROXY_PASS
fi
if [ -z "$SKIP_NTLM_TESTS" ]; then
echo ""
echo "Running NTLM tests (IIS emulation)"
echo ""
export GITTEST_REMOTE_URL="http://localhost:9000/ntlm/test.git"
export GITTEST_REMOTE_USER="foo"
export GITTEST_REMOTE_PASS="baz"
run_test auth_clone_and_push
unset GITTEST_REMOTE_URL
unset GITTEST_REMOTE_USER
unset GITTEST_REMOTE_PASS
echo ""
echo "Running NTLM tests (Apache emulation)"
echo ""
export GITTEST_REMOTE_URL="http://localhost:9000/broken-ntlm/test.git"
export GITTEST_REMOTE_USER="foo"
export GITTEST_REMOTE_PASS="baz"
run_test auth_clone_and_push
unset GITTEST_REMOTE_URL
unset GITTEST_REMOTE_USER
unset GITTEST_REMOTE_PASS
fi
if [ -z "$SKIP_NEGOTIATE_TESTS" -a -n "$GITTEST_NEGOTIATE_PASSWORD" ]; then
echo ""
echo "Running SPNEGO tests"
echo ""
if [ "$(uname -s)" = "Darwin" ]; then
KINIT_FLAGS="--password-file=STDIN"
fi
echo $GITTEST_NEGOTIATE_PASSWORD | kinit $KINIT_FLAGS test@LIBGIT2.ORG
klist -5f
export GITTEST_REMOTE_URL="https://test.libgit2.org/kerberos/empty.git"
export GITTEST_REMOTE_DEFAULT="true"
run_test auth_clone
unset GITTEST_REMOTE_URL
unset GITTEST_REMOTE_DEFAULT
echo ""
echo "Running SPNEGO tests (expect/continue)"
echo ""
export GITTEST_REMOTE_URL="https://test.libgit2.org/kerberos/empty.git"
export GITTEST_REMOTE_DEFAULT="true"
export GITTEST_REMOTE_EXPECTCONTINUE="true"
run_test auth_clone
unset GITTEST_REMOTE_URL
unset GITTEST_REMOTE_DEFAULT
unset GITTEST_REMOTE_EXPECTCONTINUE
kdestroy -A
fi
if [ -z "$SKIP_SSH_TESTS" ]; then
echo ""
echo "Running ssh tests"
......
......@@ -49,5 +49,5 @@ IF(GSS_BACKEND)
ENDIF()
ELSE()
SET(GIT_GSSAPI 0)
ADD_FEATURE_INFO(SPNEGO NO "")
ADD_FEATURE_INFO(SPNEGO NO "SPNEGO authentication support")
ENDIF()
......@@ -203,7 +203,8 @@ typedef enum {
GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY,
GIT_OPT_GET_PACK_MAX_OBJECTS,
GIT_OPT_SET_PACK_MAX_OBJECTS,
GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS
GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS,
GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE
} git_libgit2_opt_t;
/**
......@@ -397,6 +398,11 @@ typedef enum {
* > This will cause .keep file existence checks to be skipped when
* > accessing packfiles, which can help performance with remote filesystems.
*
* opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, int enabled)
* > When connecting to a server using NTLM or Negotiate
* > authentication, use expect/continue when POSTing data.
* > This option is not available on Windows.
*
* @param option Option key
* @param ... value to set the option
* @return 0 on success, <0 on failure
......
......@@ -106,7 +106,8 @@ typedef enum {
GIT_ERROR_FILESYSTEM,
GIT_ERROR_PATCH,
GIT_ERROR_WORKTREE,
GIT_ERROR_SHA1
GIT_ERROR_SHA1,
GIT_ERROR_HTTP
} git_error_t;
/**
......
......@@ -567,6 +567,11 @@ void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
data[copylen] = '\0';
}
void git_buf_consume_bytes(git_buf *buf, size_t len)
{
git_buf_consume(buf, buf->ptr + len);
}
void git_buf_consume(git_buf *buf, const char *end)
{
if (end > buf->ptr && end <= buf->ptr + buf->size) {
......
......@@ -113,6 +113,7 @@ int git_buf_puts(git_buf *buf, const char *string);
int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap);
void git_buf_clear(git_buf *buf);
void git_buf_consume_bytes(git_buf *buf, size_t len);
void git_buf_consume(git_buf *buf, const char *end);
void git_buf_truncate(git_buf *buf, size_t len);
void git_buf_shorten(git_buf *buf, size_t amount);
......
......@@ -153,6 +153,187 @@ done:
return error;
}
int git_net_url_joinpath(
git_net_url *out,
git_net_url *one,
const char *two)
{
git_buf path = GIT_BUF_INIT;
const char *query;
size_t one_len, two_len;
git_net_url_dispose(out);
if ((query = strchr(two, '?')) != NULL) {
two_len = query - two;
if (*(++query) != '\0') {
out->query = git__strdup(query);
GIT_ERROR_CHECK_ALLOC(out->query);
}
} else {
two_len = strlen(two);
}
/* Strip all trailing `/`s from the first path */
one_len = one->path ? strlen(one->path) : 0;
while (one_len && one->path[one_len - 1] == '/')
one_len--;
/* Strip all leading `/`s from the second path */
while (*two == '/') {
two++;
two_len--;
}
git_buf_put(&path, one->path, one_len);
git_buf_putc(&path, '/');
git_buf_put(&path, two, two_len);
if (git_buf_oom(&path))
return -1;
out->path = git_buf_detach(&path);
if (one->scheme) {
out->scheme = git__strdup(one->scheme);
GIT_ERROR_CHECK_ALLOC(out->scheme);
}
if (one->host) {
out->host = git__strdup(one->host);
GIT_ERROR_CHECK_ALLOC(out->host);
}
if (one->port) {
out->port = git__strdup(one->port);
GIT_ERROR_CHECK_ALLOC(out->port);
}
if (one->username) {
out->username = git__strdup(one->username);
GIT_ERROR_CHECK_ALLOC(out->username);
}
if (one->password) {
out->password = git__strdup(one->password);
GIT_ERROR_CHECK_ALLOC(out->password);
}
return 0;
}
/*
* Some servers strip the query parameters from the Location header
* when sending a redirect. Others leave it in place.
* Check for both, starting with the stripped case first,
* since it appears to be more common.
*/
static void remove_service_suffix(
git_net_url *url,
const char *service_suffix)
{
const char *service_query = strchr(service_suffix, '?');
size_t full_suffix_len = strlen(service_suffix);
size_t suffix_len = service_query ?
(size_t)(service_query - service_suffix) : full_suffix_len;
size_t path_len = strlen(url->path);
ssize_t truncate = -1;
/*
* Check for a redirect without query parameters,
* like "/newloc/info/refs"'
*/
if (suffix_len && path_len >= suffix_len) {
size_t suffix_offset = path_len - suffix_len;
if (git__strncmp(url->path + suffix_offset, service_suffix, suffix_len) == 0 &&
(!service_query || git__strcmp(url->query, service_query + 1) == 0)) {
truncate = suffix_offset;
}
}
/*
* If we haven't already found where to truncate to remove the
* suffix, check for a redirect with query parameters, like
* "/newloc/info/refs?service=git-upload-pack"
*/
if (truncate < 0 && git__suffixcmp(url->path, service_suffix) == 0)
truncate = path_len - full_suffix_len;
/* Ensure we leave a minimum of '/' as the path */
if (truncate == 0)
truncate++;
if (truncate > 0) {
url->path[truncate] = '\0';
git__free(url->query);
url->query = NULL;
}
}
int git_net_url_apply_redirect(
git_net_url *url,
const char *redirect_location,
const char *service_suffix)
{
git_net_url tmp = GIT_NET_URL_INIT;
int error = 0;
assert(url && redirect_location);
if (redirect_location[0] == '/') {
git__free(url->path);
if ((url->path = git__strdup(redirect_location)) == NULL) {
error = -1;
goto done;
}
} else {
git_net_url *original = url;
if ((error = git_net_url_parse(&tmp, redirect_location)) < 0)
goto done;
/* Validate that this is a legal redirection */
if (original->scheme &&
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);
error = -1;
goto done;
}
if (original->host &&
git__strcasecmp(original->host, tmp.host) != 0) {
git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
original->host, tmp.host);
error = -1;
goto done;
}
git_net_url_swap(url, &tmp);
}
/* Remove the service suffix if it was given to us */
if (service_suffix)
remove_service_suffix(url, service_suffix);
done:
git_net_url_dispose(&tmp);
return error;
}
bool git_net_url_valid(git_net_url *url)
{
return (url->host && url->port && url->path);
}
int git_net_url_is_default_port(git_net_url *url)
{
return (strcmp(url->port, default_port_for_scheme(url->scheme)) == 0);
......@@ -167,6 +348,51 @@ void git_net_url_swap(git_net_url *a, git_net_url *b)
memcpy(b, &tmp, sizeof(git_net_url));
}
int git_net_url_fmt(git_buf *buf, git_net_url *url)
{
git_buf_puts(buf, url->scheme);
git_buf_puts(buf, "://");
if (url->username) {
git_buf_puts(buf, url->username);
if (url->password) {
git_buf_puts(buf, ":");
git_buf_puts(buf, url->password);
}
git_buf_putc(buf, '@');
}
git_buf_puts(buf, url->host);
if (url->port && !git_net_url_is_default_port(url)) {
git_buf_putc(buf, ':');
git_buf_puts(buf, url->port);
}
git_buf_puts(buf, url->path ? url->path : "/");
if (url->query) {
git_buf_putc(buf, '?');
git_buf_puts(buf, url->query);
}
return git_buf_oom(buf) ? -1 : 0;
}
int git_net_url_fmt_path(git_buf *buf, git_net_url *url)
{
git_buf_puts(buf, url->path ? url->path : "/");
if (url->query) {
git_buf_putc(buf, '?');
git_buf_puts(buf, url->query);
}
return git_buf_oom(buf) ? -1 : 0;
}
void git_net_url_dispose(git_net_url *url)
{
if (url->username)
......@@ -179,6 +405,7 @@ void git_net_url_dispose(git_net_url *url)
git__free(url->host); url->host = NULL;
git__free(url->port); url->port = NULL;
git__free(url->path); url->path = NULL;
git__free(url->query); url->query = NULL;
git__free(url->username); url->username = NULL;
git__free(url->password); url->password = NULL;
}
......@@ -22,15 +22,36 @@ typedef struct git_net_url {
#define GIT_NET_URL_INIT { NULL }
/** Parses a string containing a URL into a structure. */
int git_net_url_parse(git_net_url *url, const char *str);
extern int git_net_url_parse(git_net_url *url, const char *str);
/** Appends a path and/or query string to the given URL */
extern int git_net_url_joinpath(
git_net_url *out,
git_net_url *in,
const char *path);
/** Ensures that a URL is minimally valid (contains a host, port and path) */
extern bool git_net_url_valid(git_net_url *url);
/** Returns nonzero if the URL is on the default port. */
int git_net_url_is_default_port(git_net_url *url);
extern int git_net_url_is_default_port(git_net_url *url);
/* Applies a redirect to the URL with a git-aware service suffix. */
extern int git_net_url_apply_redirect(
git_net_url *url,
const char *redirect_location,
const char *service_suffix);
/** Swaps the contents of one URL for another. */
void git_net_url_swap(git_net_url *a, git_net_url *b);
extern void git_net_url_swap(git_net_url *a, git_net_url *b);
/** Places the URL into the given buffer. */
extern int git_net_url_fmt(git_buf *out, git_net_url *url);
/** Place the path and query string into the given buffer. */
extern int git_net_url_fmt_path(git_buf *buf, git_net_url *url);
/** Disposes the contents of the structure. */
void git_net_url_dispose(git_net_url *url);
extern void git_net_url_dispose(git_net_url *url);
#endif
......@@ -121,101 +121,3 @@ int gitno__match_host(const char *pattern, const char *host)
return -1;
}
int gitno_connection_data_handle_redirect(
git_net_url *url,
const char *redirect_str,
const char *service_suffix)
{
git_net_url tmp = GIT_NET_URL_INIT;
int error = 0;
assert(url && redirect_str);
if (redirect_str[0] == '/') {
git__free(url->path);
if ((url->path = git__strdup(redirect_str)) == NULL) {
error = -1;
goto done;
}
} else {
git_net_url *original = url;
if ((error = git_net_url_parse(&tmp, redirect_str)) < 0)
goto done;
/* Validate that this is a legal redirection */
if (original->scheme &&
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);
error = -1;
goto done;
}
if (original->host &&
git__strcasecmp(original->host, tmp.host) != 0) {
git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
original->host, tmp.host);
error = -1;
goto done;
}
git_net_url_swap(url, &tmp);
}
/* Remove the service suffix if it was given to us */
if (service_suffix) {
/*
* Some servers strip the query parameters from the Location header
* when sending a redirect. Others leave it in place.
* Check for both, starting with the stripped case first,
* since it appears to be more common.
*/
const char *service_query = strchr(service_suffix, '?');
size_t full_suffix_len = strlen(service_suffix);
size_t suffix_len = service_query ?
(size_t)(service_query - service_suffix) : full_suffix_len;
size_t path_len = strlen(url->path);
ssize_t truncate = -1;
/* Check for a redirect without query parameters, like "/newloc/info/refs" */
if (suffix_len && path_len >= suffix_len) {
size_t suffix_offset = path_len - suffix_len;
if (git__strncmp(url->path + suffix_offset, service_suffix, suffix_len) == 0 &&
(!service_query || git__strcmp(url->query, service_query + 1) == 0)) {
truncate = suffix_offset;
}
}
/*
* If we haven't already found where to truncate to remove the suffix,
* check for a redirect with query parameters,
* like "/newloc/info/refs?service=git-upload-pack"
*/
if (truncate == -1 && git__suffixcmp(url->path, service_suffix) == 0) {
truncate = path_len - full_suffix_len;
}
if (truncate >= 0) {
/* Ensure we leave a minimum of '/' as the path */
if (truncate == 0)
truncate++;
url->path[truncate] = '\0';
git__free(url->query);
url->query = NULL;
}
}
done:
git_net_url_dispose(&tmp);
return error;
}
......@@ -65,15 +65,4 @@ int gitno_recv(gitno_buffer *buf);
void gitno_consume(gitno_buffer *buf, const char *ptr);
void gitno_consume_n(gitno_buffer *buf, size_t cons);
/*
* This replaces all the pointers in `data` with freshly-allocated strings,
* that the caller is responsible for freeing.
* `gitno_connection_data_free_ptrs` is good for this.
*/
int gitno_connection_data_handle_redirect(
git_net_url *data,
const char *url,
const char *service_suffix);
#endif
......@@ -25,6 +25,7 @@
#include "refs.h"
#include "index.h"
#include "transports/smart.h"
#include "transports/http.h"
#include "streams/openssl.h"
#include "streams/mbedtls.h"
......@@ -284,6 +285,10 @@ int git_libgit2_opts(int key, ...)
git_disable_pack_keep_file_checks = (va_arg(ap, int) != 0);
break;
case GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE:
git_http__expect_continue = (va_arg(ap, int) != 0);
break;
default:
git_error_set(GIT_ERROR_INVALID, "invalid option key");
error = -1;
......
......@@ -56,7 +56,7 @@ GIT_INLINE(void) git_trace__null(
GIT_UNUSED(fmt);
}
#define git_trace_level() ((void)0)
#define git_trace_level() ((git_trace_level_t)0)
#define git_trace git_trace__null
#endif
......
......@@ -75,6 +75,22 @@ static int negotiate_set_challenge(
return 0;
}
static void negotiate_context_dispose(http_auth_negotiate_context *ctx)
{
OM_uint32 status_minor;
if (ctx->gss_context != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(
&status_minor, &ctx->gss_context, GSS_C_NO_BUFFER);
ctx->gss_context = GSS_C_NO_CONTEXT;
}
git_buf_dispose(&ctx->target);
git__free(ctx->challenge);
ctx->challenge = NULL;
}
static int negotiate_next_token(
git_buf *buf,
git_http_auth_context *c,
......@@ -105,18 +121,20 @@ static int negotiate_next_token(
if (GSS_ERROR(status_major)) {
negotiate_err_set(status_major, status_minor,
"Could not parse principal");
"could not parse principal");
error = -1;
goto done;
}
challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
if (challenge_len < 9) {
git_error_set(GIT_ERROR_NET, "no negotiate challenge sent from server");
if (challenge_len < 9 || memcmp(ctx->challenge, "Negotiate", 9) != 0) {
git_error_set(GIT_ERROR_NET, "server did not request negotiate");
error = -1;
goto done;
} else if (challenge_len > 9) {
}
if (challenge_len > 9) {
if (git_buf_decode_base64(&input_buf,
ctx->challenge + 10, challenge_len - 10) < 0) {
git_error_set(GIT_ERROR_NET, "invalid negotiate challenge from server");
......@@ -128,14 +146,12 @@ static int negotiate_next_token(
input_token.length = input_buf.size;
input_token_ptr = &input_token;
} else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
git_error_set(GIT_ERROR_NET, "could not restart authentication");
error = -1;
goto done;
negotiate_context_dispose(ctx);
}
mech = &negotiate_oid_spnego;
if (GSS_ERROR(status_major = gss_init_sec_context(
status_major = gss_init_sec_context(
&status_minor,
GSS_C_NO_CREDENTIAL,
&ctx->gss_context,
......@@ -148,7 +164,9 @@ static int negotiate_next_token(
NULL,
&output_token,
NULL,
NULL))) {
NULL);
if (GSS_ERROR(status_major)) {
negotiate_err_set(status_major, status_minor, "negotiate failure");
error = -1;
goto done;
......@@ -156,10 +174,17 @@ static int negotiate_next_token(
/* This message merely told us auth was complete; we do not respond. */
if (status_major == GSS_S_COMPLETE) {
negotiate_context_dispose(ctx);
ctx->complete = 1;
goto done;
}
if (output_token.length == 0) {
git_error_set(GIT_ERROR_NET, "GSSAPI did not return token");
error = -1;
goto done;
}
git_buf_puts(buf, "Negotiate ");
git_buf_encode_base64(buf, output_token.value, output_token.length);
......@@ -185,17 +210,8 @@ static int negotiate_is_complete(git_http_auth_context *c)
static void negotiate_context_free(git_http_auth_context *c)
{
http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
OM_uint32 status_minor;
if (ctx->gss_context != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(
&status_minor, &ctx->gss_context, GSS_C_NO_BUFFER);
ctx->gss_context = GSS_C_NO_CONTEXT;
}
git_buf_dispose(&ctx->target);
git__free(ctx->challenge);
negotiate_context_dispose(ctx);
ctx->configured = 0;
ctx->complete = 0;
......@@ -214,8 +230,9 @@ static int negotiate_init_context(
size_t i;
/* Query supported mechanisms looking for SPNEGO) */
if (GSS_ERROR(status_major =
gss_indicate_mechs(&status_minor, &mechanism_list))) {
status_major = gss_indicate_mechs(&status_minor, &mechanism_list);
if (GSS_ERROR(status_major)) {
negotiate_err_set(status_major, status_minor,
"could not query mechanisms");
return -1;
......
......@@ -11,6 +11,9 @@
#include "git2.h"
#include "auth.h"
/* NTLM requires a full request/challenge/response */
#define GIT_AUTH_STEPS_NTLM 2
#ifdef GIT_NTLM
#if defined(GIT_OPENSSL)
......
......@@ -9,9 +9,12 @@
#define INCLUDE_transports_http_h__
#include "buffer.h"
#include "httpclient.h"
#define GIT_HTTP_REPLAY_MAX 15
extern bool git_http__expect_continue;
GIT_INLINE(int) git_http__user_agent(git_buf *buf)
{
const char *ua = git_libgit2__user_agent();
......
/*
* 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_transports_httpclient_h__
#define INCLUDE_transports_httpclient_h__
#include "common.h"
#include "net.h"
#define GIT_HTTP_STATUS_CONTINUE 100
#define GIT_HTTP_STATUS_OK 200
#define GIT_HTTP_MOVED_PERMANENTLY 301
#define GIT_HTTP_FOUND 302
#define GIT_HTTP_SEE_OTHER 303
#define GIT_HTTP_TEMPORARY_REDIRECT 307
#define GIT_HTTP_PERMANENT_REDIRECT 308
#define GIT_HTTP_STATUS_UNAUTHORIZED 401
#define GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED 407
typedef struct git_http_client git_http_client;
/** Method for the HTTP request */
typedef enum {
GIT_HTTP_METHOD_GET,
GIT_HTTP_METHOD_POST,
GIT_HTTP_METHOD_CONNECT
} git_http_method;
/** An HTTP request */
typedef struct {
git_http_method method; /**< Method for the request */
git_net_url *url; /**< Full request URL */
git_net_url *proxy; /**< Proxy to use */
/* Headers */
const char *accept; /**< Contents of the Accept header */
const char *content_type; /**< Content-Type header (for POST) */
git_cred *credentials; /**< Credentials to authenticate with */
git_cred *proxy_credentials; /**< Credentials for proxy */
git_strarray *custom_headers; /**< Additional headers to deliver */
/* To POST a payload, either set content_length OR set chunked. */
size_t content_length; /**< Length of the POST body */
unsigned chunked : 1, /**< Post with chunking */
expect_continue : 1; /**< Use expect/continue negotiation */
} git_http_request;
typedef struct {
int status;
/* Headers */
char *content_type;
size_t content_length;
char *location;
/* Authentication headers */
unsigned server_auth_schemetypes; /**< Schemes requested by remote */
unsigned server_auth_credtypes; /**< Supported cred types for remote */
unsigned proxy_auth_schemetypes; /**< Schemes requested by proxy */
unsigned proxy_auth_credtypes; /**< Supported cred types for proxy */
unsigned chunked : 1, /**< Response body is chunked */
resend_credentials : 1; /**< Resend with authentication */
} git_http_response;
typedef struct {
/** Certificate check callback for the remote */
git_transport_certificate_check_cb server_certificate_check_cb;
void *server_certificate_check_payload;
/** Certificate check callback for the proxy */
git_transport_certificate_check_cb proxy_certificate_check_cb;
void *proxy_certificate_check_payload;
} git_http_client_options;
/**
* Create a new httpclient instance with the given options.
*
* @param out pointer to receive the new instance
* @param opts options to create the client with or NULL for defaults
*/
extern int git_http_client_new(
git_http_client **out,
git_http_client_options *opts);
/*
* Sends a request to the host specified by the request URL. If the
* method is POST, either the the content_length or the chunked flag must
* be specified. The body should be provided in subsequent calls to
* git_http_client_send_body.
*
* @param client the client to write the request to
* @param request the request to send
*/
extern int git_http_client_send_request(
git_http_client *client,
git_http_request *request);
/*
* After sending a request, there may already be a response to read --
* either because there was a non-continue response to an expect: continue
* request, or because the server pipelined a response to us before we even
* sent the request. Examine the state.
*
* @param client the client to examine
* @return true if there's already a response to read, false otherwise
*/
extern bool git_http_client_has_response(git_http_client *client);
/**
* Sends the given buffer to the remote as part of the request body. The
* request must have specified either a content_length or the chunked flag.
*
* @param client the client to write the request body to
* @param buffer the request body
* @param buffer_len number of bytes of the buffer to send
*/
extern int git_http_client_send_body(
git_http_client *client,
const char *buffer,
size_t buffer_len);
/**
* Reads the headers of a response to a request. This will consume the
* entirety of the headers of a response from the server. The body (if any)
* can be read by calling git_http_client_read_body. Callers must free
* the response with git_http_response_dispose.
*
* @param response pointer to the response object to fill
* @param client the client to read the response from
*/
extern int git_http_client_read_response(
git_http_response *response,
git_http_client *client);
/**
* Reads some or all of the body of a response. At most buffer_size (or
* INT_MAX) bytes will be read and placed into the buffer provided. The
* number of bytes read will be returned, or 0 to indicate that the end of
* the body has been read.
*
* @param client the client to read the response from
* @param buffer pointer to the buffer to fill
* @param buffer_size the maximum number of bytes to read
* @return the number of bytes read, 0 on end of body, or error code
*/
extern int git_http_client_read_body(
git_http_client *client,
char *buffer,
size_t buffer_size);
/**
* Reads all of the (remainder of the) body of the response and ignores it.
* None of the data from the body will be returned to the caller.
*
* @param client the client to read the response from
* @return 0 or an error code
*/
extern int git_http_client_skip_body(git_http_client *client);
/**
* Examines the status code of the response to determine if it is a
* redirect of any type (eg, 301, 302, etc).
*
* @param response the response to inspect
* @return true if the response is a redirect, false otherwise
*/
extern bool git_http_response_is_redirect(git_http_response *response);
/**
* Frees any memory associated with the response.
*
* @param response the response to free
*/
extern void git_http_response_dispose(git_http_response *response);
/**
* Frees any memory associated with the client. If any sockets are open,
* they will be closed.
*
* @param client the client to free
*/
extern void git_http_client_free(git_http_client *client);
#endif
......@@ -371,7 +371,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
} else if (pkt_type == GIT_PKT_NAK) {
continue;
} else {
git_error_set(GIT_ERROR_NET, "Unexpected pkt type");
git_error_set(GIT_ERROR_NET, "unexpected pkt type");
error = -1;
goto on_error;
}
......@@ -439,7 +439,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
return error;
if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
git_error_set(GIT_ERROR_NET, "Unexpected pkt type");
git_error_set(GIT_ERROR_NET, "unexpected pkt type");
return -1;
}
} else {
......@@ -460,7 +460,7 @@ static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack,
do {
if (t->cancelled.val) {
git_error_set(GIT_ERROR_NET, "The fetch was cancelled by the user");
git_error_set(GIT_ERROR_NET, "the fetch was cancelled by the user");
return GIT_EUSER;
}
......@@ -831,7 +831,7 @@ static int parse_report(transport_smart *transport, git_push *push)
if (data_pkt_buf.size > 0) {
/* If there was data remaining in the pack data buffer,
* then the server sent a partial pkt-line */
git_error_set(GIT_ERROR_NET, "Incomplete pack data pkt-line");
git_error_set(GIT_ERROR_NET, "incomplete pack data pkt-line");
error = GIT_ERROR;
}
goto done;
......
......@@ -57,6 +57,8 @@
# define DWORD_MAX 0xffffffff
#endif
bool git_http__expect_continue = false;
static const char *prefix_https = "https://";
static const char *upload_pack_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
......@@ -143,7 +145,7 @@ static int apply_userpass_credentials(HINTERNET request, DWORD target, int mecha
} else if (mechanisms & GIT_WINHTTP_AUTH_BASIC) {
native_scheme = WINHTTP_AUTH_SCHEME_BASIC;
} else {
git_error_set(GIT_ERROR_NET, "invalid authentication scheme");
git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
error = -1;
goto done;
}
......@@ -182,7 +184,7 @@ static int apply_default_credentials(HINTERNET request, DWORD target, int mechan
} else if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) != 0) {
native_scheme = WINHTTP_AUTH_SCHEME_NTLM;
} else {
git_error_set(GIT_ERROR_NET, "invalid authentication scheme");
git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
return -1;
}
......@@ -285,7 +287,7 @@ static int certificate_check(winhttp_stream *s, int valid)
/* 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 (!git_error_last())
git_error_set(GIT_ERROR_NET, "unknown certificate check failure");
git_error_set(GIT_ERROR_HTTP, "unknown certificate check failure");
return GIT_ECERTIFICATE;
}
......@@ -309,7 +311,7 @@ static int certificate_check(winhttp_stream *s, int valid)
error = valid ? 0 : GIT_ECERTIFICATE;
if (error < 0 && !git_error_last())
git_error_set(GIT_ERROR_NET, "user cancelled certificate check");
git_error_set(GIT_ERROR_HTTP, "user cancelled certificate check");
return error;
}
......@@ -438,7 +440,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error;
if (strcmp(t->proxy.url.scheme, "http") != 0 && strcmp(t->proxy.url.scheme, "https") != 0) {
git_error_set(GIT_ERROR_NET, "invalid URL: '%s'", proxy_url);
git_error_set(GIT_ERROR_HTTP, "invalid URL: '%s'", proxy_url);
error = -1;
goto on_error;
}
......@@ -711,21 +713,21 @@ static void CALLBACK winhttp_status(
status = *((DWORD *)info);
if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID))
git_error_set(GIT_ERROR_NET, "SSL certificate issued for different common name");
git_error_set(GIT_ERROR_HTTP, "SSL certificate issued for different common name");
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID))
git_error_set(GIT_ERROR_NET, "SSL certificate has expired");
git_error_set(GIT_ERROR_HTTP, "SSL certificate has expired");
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA))
git_error_set(GIT_ERROR_NET, "SSL certificate signed by unknown CA");
git_error_set(GIT_ERROR_HTTP, "SSL certificate signed by unknown CA");
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT))
git_error_set(GIT_ERROR_NET, "SSL certificate is invalid");
git_error_set(GIT_ERROR_HTTP, "SSL certificate is invalid");
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED))
git_error_set(GIT_ERROR_NET, "certificate revocation check failed");
git_error_set(GIT_ERROR_HTTP, "certificate revocation check failed");
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED))
git_error_set(GIT_ERROR_NET, "SSL certificate was revoked");
git_error_set(GIT_ERROR_HTTP, "SSL certificate was revoked");
else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR))
git_error_set(GIT_ERROR_NET, "security libraries could not be loaded");
git_error_set(GIT_ERROR_HTTP, "security libraries could not be loaded");
else
git_error_set(GIT_ERROR_NET, "unknown security error %lu", status);
git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status);
}
static int winhttp_connect(
......@@ -830,7 +832,7 @@ on_error:
return error;
}
static int do_send_request(winhttp_stream *s, size_t len, int ignore_length)
static int do_send_request(winhttp_stream *s, size_t len, bool chunked)
{
int attempts;
bool success;
......@@ -841,7 +843,7 @@ static int do_send_request(winhttp_stream *s, size_t len, int ignore_length)
}
for (attempts = 0; attempts < 5; attempts++) {
if (ignore_length) {
if (chunked) {
success = WinHttpSendRequest(s->request,
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0,
......@@ -860,13 +862,13 @@ static int do_send_request(winhttp_stream *s, size_t len, int ignore_length)
return success ? 0 : -1;
}
static int send_request(winhttp_stream *s, size_t len, int ignore_length)
static int send_request(winhttp_stream *s, size_t len, bool chunked)
{
int request_failed = 0, cert_valid = 1, error = 0;
DWORD ignore_flags;
git_error_clear();
if ((error = do_send_request(s, len, ignore_length)) < 0) {
if ((error = do_send_request(s, len, chunked)) < 0) {
if (GetLastError() != ERROR_WINHTTP_SECURE_FAILURE) {
git_error_set(GIT_ERROR_OS, "failed to send request");
return -1;
......@@ -895,7 +897,7 @@ static int send_request(winhttp_stream *s, size_t len, int ignore_length)
return -1;
}
if ((error = do_send_request(s, len, ignore_length)) < 0)
if ((error = do_send_request(s, len, chunked)) < 0)
git_error_set(GIT_ERROR_OS, "failed to send request with unchecked certificate");
return error;
......@@ -969,7 +971,7 @@ static int winhttp_stream_read(
replay:
/* Enforce a reasonable cap on the number of replays */
if (replay_count++ >= GIT_HTTP_REPLAY_MAX) {
git_error_set(GIT_ERROR_NET, "too many redirects or authentication replays");
git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays");
return -1;
}
......@@ -984,7 +986,7 @@ replay:
if (!s->sent_request) {
if ((error = send_request(s, s->post_body_len, 0)) < 0)
if ((error = send_request(s, s->post_body_len, false)) < 0)
return error;
s->sent_request = 1;
......@@ -1128,7 +1130,7 @@ replay:
if (!git__prefixcmp_icase(location8, prefix_https)) {
/* Upgrade to secure connection; disconnect and start over */
if (gitno_connection_data_handle_redirect(&t->server.url, location8, s->service_url) < 0) {
if (git_net_url_apply_redirect(&t->server.url, location8, s->service_url) < 0) {
git__free(location8);
return -1;
}
......@@ -1175,7 +1177,7 @@ replay:
}
if (HTTP_STATUS_OK != status_code) {
git_error_set(GIT_ERROR_NET, "request failed with status code: %lu", status_code);
git_error_set(GIT_ERROR_HTTP, "request failed with status code: %lu", status_code);
return -1;
}
......@@ -1202,7 +1204,7 @@ replay:
}
if (wcscmp(expected_content_type, content_type)) {
git_error_set(GIT_ERROR_NET, "received unexpected content-type");
git_error_set(GIT_ERROR_HTTP, "received unexpected content-type");
return -1;
}
......@@ -1237,11 +1239,11 @@ static int winhttp_stream_write_single(
/* This implementation of write permits only a single call. */
if (s->sent_request) {
git_error_set(GIT_ERROR_NET, "subtransport configured for only one write");
git_error_set(GIT_ERROR_HTTP, "subtransport configured for only one write");
return -1;
}
if ((error = send_request(s, len, 0)) < 0)
if ((error = send_request(s, len, false)) < 0)
return error;
s->sent_request = 1;
......@@ -1268,12 +1270,12 @@ static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch)
if (RPC_S_OK != status &&
RPC_S_UUID_LOCAL_ONLY != status &&
RPC_S_UUID_NO_ADDRESS != status) {
git_error_set(GIT_ERROR_NET, "unable to generate name for temp file");
git_error_set(GIT_ERROR_HTTP, "unable to generate name for temp file");
return -1;
}
if (buffer_len_cch < UUID_LENGTH_CCH + 1) {
git_error_set(GIT_ERROR_NET, "buffer too small for name of temp file");
git_error_set(GIT_ERROR_HTTP, "buffer too small for name of temp file");
return -1;
}
......@@ -1380,7 +1382,7 @@ static int winhttp_stream_write_chunked(
return -1;
}
if ((error = send_request(s, 0, 1)) < 0)
if ((error = send_request(s, 0, true)) < 0)
return error;
s->sent_request = 1;
......
......@@ -66,3 +66,5 @@ ADD_CLAR_TEST(online -v -sonline)
ADD_CLAR_TEST(gitdaemon -v -sonline::push)
ADD_CLAR_TEST(ssh -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths -sonline::clone::path_whitespace_ssh)
ADD_CLAR_TEST(proxy -v -sonline::clone::proxy)
ADD_CLAR_TEST(auth_clone -v -sonline::clone::cred)
ADD_CLAR_TEST(auth_clone_and_push -v -sonline::clone::push -sonline::push)
#include "clar_libgit2_trace.h"
#if defined(GIT_TRACE)
#include "clar_libgit2.h"
#include "clar_libgit2_timer.h"
#include "trace.h"
......@@ -264,15 +261,3 @@ void cl_global_trace_disable(void)
* once.
*/
}
#else /* GIT_TRACE */
void cl_global_trace_register(void)
{
}
void cl_global_trace_disable(void)
{
}
#endif /* GIT_TRACE*/
#include "clar_libgit2.h"
#include "net.h"
#include "netops.h"
static git_net_url source, target;
void test_network_joinpath__initialize(void)
{
memset(&source, 0, sizeof(source));
memset(&target, 0, sizeof(target));
}
void test_network_joinpath__cleanup(void)
{
git_net_url_dispose(&source);
git_net_url_dispose(&target);
}
void test_network_joinpath__target_paths_and_queries(void)
{
cl_git_pass(git_net_url_parse(&source, "http://example.com/a/b"));
cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d"));
cl_assert_equal_s(target.path, "/a/b/c/d");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d?foo"));
cl_assert_equal_s(target.path, "/a/b/c/d");
cl_assert_equal_s(target.query, "foo");
git_net_url_dispose(&target);
}
void test_network_joinpath__source_query_removed(void)
{
cl_git_pass(git_net_url_parse(&source, "http://example.com/a/b?query&one&two"));
cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d"));
cl_assert_equal_s(target.path, "/a/b/c/d");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d?foo"));
cl_assert_equal_s(target.path, "/a/b/c/d");
cl_assert_equal_s(target.query, "foo");
git_net_url_dispose(&target);
}
void test_network_joinpath__source_lacks_path(void)
{
cl_git_pass(git_net_url_parse(&source, "http://example.com"));
cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
cl_assert_equal_s(target.path, "/");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, ""));
cl_assert_equal_s(target.path, "/");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
cl_assert_equal_s(target.path, "/foo/bar");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_s(target.query, "hello");
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_s(target.query, "hello");
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
cl_assert_equal_s(target.path, "/foo/bar");
cl_assert_equal_s(target.query, "hello");
git_net_url_dispose(&target);
}
void test_network_joinpath__source_is_slash(void)
{
cl_git_pass(git_net_url_parse(&source, "http://example.com/"));
cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
cl_assert_equal_s(target.path, "/");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, ""));
cl_assert_equal_s(target.path, "/");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
cl_assert_equal_s(target.path, "/foo/bar");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_s(target.query, "hello");
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_s(target.query, "hello");
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
cl_assert_equal_s(target.path, "/foo/bar");
cl_assert_equal_s(target.query, "hello");
git_net_url_dispose(&target);
}
void test_network_joinpath__source_has_query(void)
{
cl_git_pass(git_net_url_parse(&source, "http://example.com?query"));
cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
cl_assert_equal_s(target.path, "/");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, ""));
cl_assert_equal_s(target.path, "/");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
cl_assert_equal_s(target.path, "/foo/bar");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_s(target.query, "hello");
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
cl_assert_equal_s(target.path, "/asdf");
cl_assert_equal_s(target.query, "hello");
git_net_url_dispose(&target);
cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
cl_assert_equal_s(target.path, "/foo/bar");
cl_assert_equal_s(target.query, "hello");
git_net_url_dispose(&target);
}
void test_network_joinpath__empty_query_ignored(void)
{
cl_git_pass(git_net_url_parse(&source, "http://example.com/foo"));
cl_git_pass(git_net_url_joinpath(&target, &source, "/bar/baz?"));
cl_assert_equal_s(target.path, "/foo/bar/baz");
cl_assert_equal_p(target.query, NULL);
git_net_url_dispose(&target);
}
......@@ -18,7 +18,7 @@ void test_network_redirect__redirect_http(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"http://example.com/foo/bar/baz"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
cl_git_pass(git_net_url_apply_redirect(&conndata,
"http://example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(conndata.host, "example.com");
......@@ -32,7 +32,7 @@ void test_network_redirect__redirect_ssl(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"https://example.com/foo/bar/baz"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
cl_git_pass(git_net_url_apply_redirect(&conndata,
"https://example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com");
......@@ -46,7 +46,7 @@ void test_network_redirect__redirect_leaves_root_path(void)
{
cl_git_pass(git_net_url_parse(&conndata,
"https://example.com/foo/bar/baz"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
cl_git_pass(git_net_url_apply_redirect(&conndata,
"https://example.com/foo/bar/baz", "/foo/bar/baz"));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com");
......@@ -60,7 +60,7 @@ void test_network_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"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
cl_git_pass(git_net_url_apply_redirect(&conndata,
"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com");
......@@ -73,7 +73,7 @@ void test_network_redirect__redirect_encoded_username_password(void)
void test_network_redirect__redirect_cross_host_denied(void)
{
cl_git_pass(git_net_url_parse(&conndata, "https://bar.com/bar/baz"));
cl_git_fail_with(gitno_connection_data_handle_redirect(&conndata,
cl_git_fail_with(git_net_url_apply_redirect(&conndata,
"https://foo.com/bar/baz", NULL),
-1);
}
......@@ -81,7 +81,7 @@ void test_network_redirect__redirect_cross_host_denied(void)
void test_network_redirect__redirect_http_downgrade_denied(void)
{
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz"));
cl_git_fail_with(gitno_connection_data_handle_redirect(&conndata,
cl_git_fail_with(git_net_url_apply_redirect(&conndata,
"http://foo.com/bar/baz", NULL),
-1);
}
......@@ -89,7 +89,7 @@ void test_network_redirect__redirect_http_downgrade_denied(void)
void test_network_redirect__redirect_relative(void)
{
cl_git_pass(git_net_url_parse(&conndata, "http://foo.com/bar/baz/biff"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
cl_git_pass(git_net_url_apply_redirect(&conndata,
"/zap/baz/biff?bam", NULL));
cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(conndata.host, "foo.com");
......@@ -102,7 +102,7 @@ void test_network_redirect__redirect_relative(void)
void test_network_redirect__redirect_relative_ssl(void)
{
cl_git_pass(git_net_url_parse(&conndata, "https://foo.com/bar/baz/biff"));
cl_git_pass(gitno_connection_data_handle_redirect(&conndata,
cl_git_pass(git_net_url_apply_redirect(&conndata,
"/zap/baz/biff?bam", NULL));
cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "foo.com");
......@@ -115,7 +115,7 @@ void test_network_redirect__redirect_relative_ssl(void)
void test_network_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(gitno_connection_data_handle_redirect(&conndata,
cl_git_pass(git_net_url_apply_redirect(&conndata,
"/baz/info/refs", "/info/refs?service=git-upload-pack"));
cl_assert_equal_s(conndata.path, "/baz");
}
......@@ -123,7 +123,7 @@ void test_network_redirect__service_query_no_query_params_in_location(void)
void test_network_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(gitno_connection_data_handle_redirect(&conndata,
cl_git_pass(git_net_url_apply_redirect(&conndata,
"/baz/info/refs?service=git-upload-pack", "/info/refs?service=git-upload-pack"));
cl_assert_equal_s(conndata.path, "/baz");
}
......@@ -30,6 +30,7 @@ static char *_remote_proxy_host = NULL;
static char *_remote_proxy_user = NULL;
static char *_remote_proxy_pass = NULL;
static char *_remote_proxy_selfsigned = NULL;
static char *_remote_expectcontinue = NULL;
static int _orig_proxies_need_reset = 0;
static char *_orig_http_proxy = NULL;
......@@ -74,6 +75,10 @@ void test_online_clone__initialize(void)
_remote_proxy_user = cl_getenv("GITTEST_REMOTE_PROXY_USER");
_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");
if (_remote_expectcontinue)
git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 1);
_orig_proxies_need_reset = 0;
}
......@@ -99,6 +104,7 @@ void test_online_clone__cleanup(void)
git__free(_remote_proxy_user);
git__free(_remote_proxy_pass);
git__free(_remote_proxy_selfsigned);
git__free(_remote_expectcontinue);
if (_orig_proxies_need_reset) {
cl_setenv("HTTP_PROXY", _orig_http_proxy);
......@@ -455,8 +461,8 @@ void test_online_clone__can_cancel(void)
{
g_options.fetch_opts.callbacks.transfer_progress = cancel_at_half;
cl_git_fail_with(
git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), 4321);
cl_git_fail_with(4321,
git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
}
static int cred_cb(git_cred **cred, const char *url, const char *user_from_url,
......
......@@ -19,6 +19,7 @@ static char *_remote_ssh_pubkey = NULL;
static char *_remote_ssh_passphrase = NULL;
static char *_remote_default = NULL;
static char *_remote_expectcontinue = NULL;
static int cred_acquire_cb(git_cred **, const char *, const char *, unsigned int, void *);
......@@ -366,12 +367,16 @@ void test_online_push__initialize(void)
_remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
_remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
_remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT");
_remote_expectcontinue = cl_getenv("GITTEST_REMOTE_EXPECTCONTINUE");
_remote = NULL;
/* Skip the test if we're missing the remote URL */
if (!_remote_url)
cl_skip();
if (_remote_expectcontinue)
git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 1);
cl_git_pass(git_remote_create(&_remote, _repo, "test", _remote_url));
record_callbacks_data_clear(&_record_cbs_data);
......@@ -417,10 +422,13 @@ void test_online_push__cleanup(void)
git__free(_remote_ssh_pubkey);
git__free(_remote_ssh_passphrase);
git__free(_remote_default);
git__free(_remote_expectcontinue);
/* Freed by cl_git_sandbox_cleanup */
_repo = NULL;
git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 0);
record_callbacks_data_clear(&_record_cbs_data);
cl_fixture_cleanup("testrepo.git");
......
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