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: ...@@ -19,7 +19,8 @@ jobs:
environmentVariables: | environmentVariables: |
CC=gcc CC=gcc
CMAKE_GENERATOR=Ninja 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 - job: linux_amd64_xenial_gcc_mbedtls
displayName: 'Linux (amd64; Xenial; GCC; mbedTLS)' displayName: 'Linux (amd64; Xenial; GCC; mbedTLS)'
...@@ -34,7 +35,8 @@ jobs: ...@@ -34,7 +35,8 @@ jobs:
environmentVariables: | environmentVariables: |
CC=gcc CC=gcc
CMAKE_GENERATOR=Ninja 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 - job: linux_amd64_xenial_clang_openssl
displayName: 'Linux (amd64; Xenial; Clang; OpenSSL)' displayName: 'Linux (amd64; Xenial; Clang; OpenSSL)'
...@@ -49,7 +51,8 @@ jobs: ...@@ -49,7 +51,8 @@ jobs:
environmentVariables: | environmentVariables: |
CC=clang CC=clang
CMAKE_GENERATOR=Ninja 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 - job: linux_amd64_xenial_clang_mbedtls
displayName: 'Linux (amd64; Xenial; Clang; mbedTLS)' displayName: 'Linux (amd64; Xenial; Clang; mbedTLS)'
...@@ -64,7 +67,8 @@ jobs: ...@@ -64,7 +67,8 @@ jobs:
environmentVariables: | environmentVariables: |
CC=clang CC=clang
CMAKE_GENERATOR=Ninja 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 - job: macos
displayName: 'macOS' displayName: 'macOS'
...@@ -81,6 +85,7 @@ jobs: ...@@ -81,6 +85,7 @@ jobs:
CMAKE_GENERATOR: Ninja CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
SKIP_SSH_TESTS: true SKIP_SSH_TESTS: true
GITTEST_NEGOTIATE_PASSWORD: $(GITTEST_NEGOTIATE_PASSWORD)
- job: windows_vs_amd64 - job: windows_vs_amd64
displayName: 'Windows (amd64; Visual Studio)' displayName: 'Windows (amd64; Visual Studio)'
...@@ -92,6 +97,7 @@ jobs: ...@@ -92,6 +97,7 @@ jobs:
CMAKE_GENERATOR: Visual Studio 12 2013 Win64 CMAKE_GENERATOR: Visual Studio 12 2013 Win64
CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON
SKIP_SSH_TESTS: true SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- job: windows_vs_x86 - job: windows_vs_x86
displayName: 'Windows (x86; Visual Studio)' displayName: 'Windows (x86; Visual Studio)'
...@@ -103,6 +109,7 @@ jobs: ...@@ -103,6 +109,7 @@ jobs:
CMAKE_GENERATOR: Visual Studio 12 2013 CMAKE_GENERATOR: Visual Studio 12 2013
CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS CMAKE_OPTIONS: -DMSVC_CRTDBG=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS
SKIP_SSH_TESTS: true SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- job: windows_mingw_amd64 - job: windows_mingw_amd64
displayName: 'Windows (amd64; MinGW)' displayName: 'Windows (amd64; MinGW)'
...@@ -120,6 +127,7 @@ jobs: ...@@ -120,6 +127,7 @@ jobs:
CMAKE_GENERATOR: MinGW Makefiles CMAKE_GENERATOR: MinGW Makefiles
CMAKE_OPTIONS: -DDEPRECATE_HARD=ON CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
SKIP_SSH_TESTS: true SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- job: windows_mingw_x86 - job: windows_mingw_x86
displayName: 'Windows (x86; MinGW)' displayName: 'Windows (x86; MinGW)'
...@@ -138,6 +146,7 @@ jobs: ...@@ -138,6 +146,7 @@ jobs:
CMAKE_GENERATOR: MinGW Makefiles CMAKE_GENERATOR: MinGW Makefiles
CMAKE_OPTIONS: -DDEPRECATE_HARD=ON CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
SKIP_SSH_TESTS: true SKIP_SSH_TESTS: true
SKIP_NEGOTIATE_TESTS: true
- job: documentation - job: documentation
displayName: 'Generate Documentation' displayName: 'Generate Documentation'
......
ARG BASE ARG BASE
FROM $BASE AS apt FROM $BASE AS apt
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y --no-install-recommends \ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
bzip2 \ bzip2 \
clang \ clang \
cmake \ cmake \
...@@ -9,8 +9,10 @@ RUN apt-get update && \ ...@@ -9,8 +9,10 @@ RUN apt-get update && \
gcc \ gcc \
git \ git \
gosu \ gosu \
krb5-user \
libcurl4-gnutls-dev \ libcurl4-gnutls-dev \
libgcrypt20-dev \ libgcrypt20-dev \
libkrb5-dev \
libpcre3-dev \ libpcre3-dev \
libssl-dev \ libssl-dev \
libz-dev \ libz-dev \
......
...@@ -6,6 +6,11 @@ if [ -n "$SKIP_TESTS" ]; then ...@@ -6,6 +6,11 @@ if [ -n "$SKIP_TESTS" ]; then
exit 0 exit 0
fi 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 ) )} SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
BUILD_DIR=$(pwd) BUILD_DIR=$(pwd)
TMPDIR=${TMPDIR:-/tmp} TMPDIR=${TMPDIR:-/tmp}
...@@ -89,6 +94,16 @@ if [ -z "$SKIP_PROXY_TESTS" ]; then ...@@ -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 & java -jar poxyproxy.jar --address 127.0.0.1 --port 8090 --credentials foo:bar --auth-type ntlm --quiet &
fi 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 if [ -z "$SKIP_SSH_TESTS" ]; then
echo "Starting ssh daemon..." echo "Starting ssh daemon..."
HOME=`mktemp -d ${TMPDIR}/home.XXXXXXXX` HOME=`mktemp -d ${TMPDIR}/home.XXXXXXXX`
...@@ -207,6 +222,65 @@ if [ -z "$SKIP_PROXY_TESTS" ]; then ...@@ -207,6 +222,65 @@ if [ -z "$SKIP_PROXY_TESTS" ]; then
unset GITTEST_REMOTE_PROXY_PASS unset GITTEST_REMOTE_PROXY_PASS
fi 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 if [ -z "$SKIP_SSH_TESTS" ]; then
echo "" echo ""
echo "Running ssh tests" echo "Running ssh tests"
......
...@@ -49,5 +49,5 @@ IF(GSS_BACKEND) ...@@ -49,5 +49,5 @@ IF(GSS_BACKEND)
ENDIF() ENDIF()
ELSE() ELSE()
SET(GIT_GSSAPI 0) SET(GIT_GSSAPI 0)
ADD_FEATURE_INFO(SPNEGO NO "") ADD_FEATURE_INFO(SPNEGO NO "SPNEGO authentication support")
ENDIF() ENDIF()
...@@ -203,7 +203,8 @@ typedef enum { ...@@ -203,7 +203,8 @@ typedef enum {
GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY,
GIT_OPT_GET_PACK_MAX_OBJECTS, GIT_OPT_GET_PACK_MAX_OBJECTS,
GIT_OPT_SET_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; } git_libgit2_opt_t;
/** /**
...@@ -397,6 +398,11 @@ typedef enum { ...@@ -397,6 +398,11 @@ typedef enum {
* > This will cause .keep file existence checks to be skipped when * > This will cause .keep file existence checks to be skipped when
* > accessing packfiles, which can help performance with remote filesystems. * > 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 option Option key
* @param ... value to set the option * @param ... value to set the option
* @return 0 on success, <0 on failure * @return 0 on success, <0 on failure
......
...@@ -106,7 +106,8 @@ typedef enum { ...@@ -106,7 +106,8 @@ typedef enum {
GIT_ERROR_FILESYSTEM, GIT_ERROR_FILESYSTEM,
GIT_ERROR_PATCH, GIT_ERROR_PATCH,
GIT_ERROR_WORKTREE, GIT_ERROR_WORKTREE,
GIT_ERROR_SHA1 GIT_ERROR_SHA1,
GIT_ERROR_HTTP
} git_error_t; } git_error_t;
/** /**
......
...@@ -567,6 +567,11 @@ void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf) ...@@ -567,6 +567,11 @@ void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
data[copylen] = '\0'; 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) void git_buf_consume(git_buf *buf, const char *end)
{ {
if (end > buf->ptr && end <= buf->ptr + buf->size) { if (end > buf->ptr && end <= buf->ptr + buf->size) {
......
...@@ -113,6 +113,7 @@ int git_buf_puts(git_buf *buf, const char *string); ...@@ -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_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); int git_buf_vprintf(git_buf *buf, const char *format, va_list ap);
void git_buf_clear(git_buf *buf); 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_consume(git_buf *buf, const char *end);
void git_buf_truncate(git_buf *buf, size_t len); void git_buf_truncate(git_buf *buf, size_t len);
void git_buf_shorten(git_buf *buf, size_t amount); void git_buf_shorten(git_buf *buf, size_t amount);
......
...@@ -153,6 +153,187 @@ done: ...@@ -153,6 +153,187 @@ done:
return error; 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) int git_net_url_is_default_port(git_net_url *url)
{ {
return (strcmp(url->port, default_port_for_scheme(url->scheme)) == 0); 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) ...@@ -167,6 +348,51 @@ void git_net_url_swap(git_net_url *a, git_net_url *b)
memcpy(b, &tmp, sizeof(git_net_url)); 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) void git_net_url_dispose(git_net_url *url)
{ {
if (url->username) if (url->username)
...@@ -179,6 +405,7 @@ void git_net_url_dispose(git_net_url *url) ...@@ -179,6 +405,7 @@ void git_net_url_dispose(git_net_url *url)
git__free(url->host); url->host = NULL; git__free(url->host); url->host = NULL;
git__free(url->port); url->port = NULL; git__free(url->port); url->port = NULL;
git__free(url->path); url->path = 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->username); url->username = NULL;
git__free(url->password); url->password = NULL; git__free(url->password); url->password = NULL;
} }
...@@ -22,15 +22,36 @@ typedef struct git_net_url { ...@@ -22,15 +22,36 @@ typedef struct git_net_url {
#define GIT_NET_URL_INIT { NULL } #define GIT_NET_URL_INIT { NULL }
/** Parses a string containing a URL into a structure. */ /** 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. */ /** 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. */ /** 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. */ /** 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 #endif
...@@ -121,101 +121,3 @@ int gitno__match_host(const char *pattern, const char *host) ...@@ -121,101 +121,3 @@ int gitno__match_host(const char *pattern, const char *host)
return -1; 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); ...@@ -65,15 +65,4 @@ int gitno_recv(gitno_buffer *buf);
void gitno_consume(gitno_buffer *buf, const char *ptr); void gitno_consume(gitno_buffer *buf, const char *ptr);
void gitno_consume_n(gitno_buffer *buf, size_t cons); 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 #endif
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "refs.h" #include "refs.h"
#include "index.h" #include "index.h"
#include "transports/smart.h" #include "transports/smart.h"
#include "transports/http.h"
#include "streams/openssl.h" #include "streams/openssl.h"
#include "streams/mbedtls.h" #include "streams/mbedtls.h"
...@@ -284,6 +285,10 @@ int git_libgit2_opts(int key, ...) ...@@ -284,6 +285,10 @@ int git_libgit2_opts(int key, ...)
git_disable_pack_keep_file_checks = (va_arg(ap, int) != 0); git_disable_pack_keep_file_checks = (va_arg(ap, int) != 0);
break; break;
case GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE:
git_http__expect_continue = (va_arg(ap, int) != 0);
break;
default: default:
git_error_set(GIT_ERROR_INVALID, "invalid option key"); git_error_set(GIT_ERROR_INVALID, "invalid option key");
error = -1; error = -1;
......
...@@ -56,7 +56,7 @@ GIT_INLINE(void) git_trace__null( ...@@ -56,7 +56,7 @@ GIT_INLINE(void) git_trace__null(
GIT_UNUSED(fmt); GIT_UNUSED(fmt);
} }
#define git_trace_level() ((void)0) #define git_trace_level() ((git_trace_level_t)0)
#define git_trace git_trace__null #define git_trace git_trace__null
#endif #endif
......
...@@ -75,6 +75,22 @@ static int negotiate_set_challenge( ...@@ -75,6 +75,22 @@ static int negotiate_set_challenge(
return 0; 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( static int negotiate_next_token(
git_buf *buf, git_buf *buf,
git_http_auth_context *c, git_http_auth_context *c,
...@@ -105,18 +121,20 @@ static int negotiate_next_token( ...@@ -105,18 +121,20 @@ static int negotiate_next_token(
if (GSS_ERROR(status_major)) { if (GSS_ERROR(status_major)) {
negotiate_err_set(status_major, status_minor, negotiate_err_set(status_major, status_minor,
"Could not parse principal"); "could not parse principal");
error = -1; error = -1;
goto done; goto done;
} }
challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0; challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
if (challenge_len < 9) { if (challenge_len < 9 || memcmp(ctx->challenge, "Negotiate", 9) != 0) {
git_error_set(GIT_ERROR_NET, "no negotiate challenge sent from server"); git_error_set(GIT_ERROR_NET, "server did not request negotiate");
error = -1; error = -1;
goto done; goto done;
} else if (challenge_len > 9) { }
if (challenge_len > 9) {
if (git_buf_decode_base64(&input_buf, if (git_buf_decode_base64(&input_buf,
ctx->challenge + 10, challenge_len - 10) < 0) { ctx->challenge + 10, challenge_len - 10) < 0) {
git_error_set(GIT_ERROR_NET, "invalid negotiate challenge from server"); git_error_set(GIT_ERROR_NET, "invalid negotiate challenge from server");
...@@ -128,14 +146,12 @@ static int negotiate_next_token( ...@@ -128,14 +146,12 @@ static int negotiate_next_token(
input_token.length = input_buf.size; input_token.length = input_buf.size;
input_token_ptr = &input_token; input_token_ptr = &input_token;
} else if (ctx->gss_context != GSS_C_NO_CONTEXT) { } else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
git_error_set(GIT_ERROR_NET, "could not restart authentication"); negotiate_context_dispose(ctx);
error = -1;
goto done;
} }
mech = &negotiate_oid_spnego; mech = &negotiate_oid_spnego;
if (GSS_ERROR(status_major = gss_init_sec_context( status_major = gss_init_sec_context(
&status_minor, &status_minor,
GSS_C_NO_CREDENTIAL, GSS_C_NO_CREDENTIAL,
&ctx->gss_context, &ctx->gss_context,
...@@ -148,7 +164,9 @@ static int negotiate_next_token( ...@@ -148,7 +164,9 @@ static int negotiate_next_token(
NULL, NULL,
&output_token, &output_token,
NULL, NULL,
NULL))) { NULL);
if (GSS_ERROR(status_major)) {
negotiate_err_set(status_major, status_minor, "negotiate failure"); negotiate_err_set(status_major, status_minor, "negotiate failure");
error = -1; error = -1;
goto done; goto done;
...@@ -156,10 +174,17 @@ static int negotiate_next_token( ...@@ -156,10 +174,17 @@ static int negotiate_next_token(
/* This message merely told us auth was complete; we do not respond. */ /* This message merely told us auth was complete; we do not respond. */
if (status_major == GSS_S_COMPLETE) { if (status_major == GSS_S_COMPLETE) {
negotiate_context_dispose(ctx);
ctx->complete = 1; ctx->complete = 1;
goto done; 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_puts(buf, "Negotiate ");
git_buf_encode_base64(buf, output_token.value, output_token.length); 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) ...@@ -185,17 +210,8 @@ static int negotiate_is_complete(git_http_auth_context *c)
static void negotiate_context_free(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; http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
OM_uint32 status_minor;
if (ctx->gss_context != GSS_C_NO_CONTEXT) { negotiate_context_dispose(ctx);
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->configured = 0; ctx->configured = 0;
ctx->complete = 0; ctx->complete = 0;
...@@ -214,8 +230,9 @@ static int negotiate_init_context( ...@@ -214,8 +230,9 @@ static int negotiate_init_context(
size_t i; size_t i;
/* Query supported mechanisms looking for SPNEGO) */ /* Query supported mechanisms looking for SPNEGO) */
if (GSS_ERROR(status_major = status_major = gss_indicate_mechs(&status_minor, &mechanism_list);
gss_indicate_mechs(&status_minor, &mechanism_list))) {
if (GSS_ERROR(status_major)) {
negotiate_err_set(status_major, status_minor, negotiate_err_set(status_major, status_minor,
"could not query mechanisms"); "could not query mechanisms");
return -1; return -1;
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
#include "git2.h" #include "git2.h"
#include "auth.h" #include "auth.h"
/* NTLM requires a full request/challenge/response */
#define GIT_AUTH_STEPS_NTLM 2
#ifdef GIT_NTLM #ifdef GIT_NTLM
#if defined(GIT_OPENSSL) #if defined(GIT_OPENSSL)
......
...@@ -9,9 +9,12 @@ ...@@ -9,9 +9,12 @@
#define INCLUDE_transports_http_h__ #define INCLUDE_transports_http_h__
#include "buffer.h" #include "buffer.h"
#include "httpclient.h"
#define GIT_HTTP_REPLAY_MAX 15 #define GIT_HTTP_REPLAY_MAX 15
extern bool git_http__expect_continue;
GIT_INLINE(int) git_http__user_agent(git_buf *buf) GIT_INLINE(int) git_http__user_agent(git_buf *buf)
{ {
const char *ua = git_libgit2__user_agent(); 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 ...@@ -371,7 +371,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
} else if (pkt_type == GIT_PKT_NAK) { } else if (pkt_type == GIT_PKT_NAK) {
continue; continue;
} else { } else {
git_error_set(GIT_ERROR_NET, "Unexpected pkt type"); git_error_set(GIT_ERROR_NET, "unexpected pkt type");
error = -1; error = -1;
goto on_error; goto on_error;
} }
...@@ -439,7 +439,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c ...@@ -439,7 +439,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
return error; return error;
if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) { 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; return -1;
} }
} else { } else {
...@@ -460,7 +460,7 @@ static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, ...@@ -460,7 +460,7 @@ static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack,
do { do {
if (t->cancelled.val) { 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; return GIT_EUSER;
} }
...@@ -831,7 +831,7 @@ static int parse_report(transport_smart *transport, git_push *push) ...@@ -831,7 +831,7 @@ static int parse_report(transport_smart *transport, git_push *push)
if (data_pkt_buf.size > 0) { if (data_pkt_buf.size > 0) {
/* If there was data remaining in the pack data buffer, /* If there was data remaining in the pack data buffer,
* then the server sent a partial pkt-line */ * 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; error = GIT_ERROR;
} }
goto done; goto done;
......
...@@ -57,6 +57,8 @@ ...@@ -57,6 +57,8 @@
# define DWORD_MAX 0xffffffff # define DWORD_MAX 0xffffffff
#endif #endif
bool git_http__expect_continue = false;
static const char *prefix_https = "https://"; static const char *prefix_https = "https://";
static const char *upload_pack_service = "upload-pack"; static const char *upload_pack_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-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 ...@@ -143,7 +145,7 @@ static int apply_userpass_credentials(HINTERNET request, DWORD target, int mecha
} else if (mechanisms & GIT_WINHTTP_AUTH_BASIC) { } else if (mechanisms & GIT_WINHTTP_AUTH_BASIC) {
native_scheme = WINHTTP_AUTH_SCHEME_BASIC; native_scheme = WINHTTP_AUTH_SCHEME_BASIC;
} else { } else {
git_error_set(GIT_ERROR_NET, "invalid authentication scheme"); git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
error = -1; error = -1;
goto done; goto done;
} }
...@@ -182,7 +184,7 @@ static int apply_default_credentials(HINTERNET request, DWORD target, int mechan ...@@ -182,7 +184,7 @@ static int apply_default_credentials(HINTERNET request, DWORD target, int mechan
} else if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) != 0) { } else if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) != 0) {
native_scheme = WINHTTP_AUTH_SCHEME_NTLM; native_scheme = WINHTTP_AUTH_SCHEME_NTLM;
} else { } else {
git_error_set(GIT_ERROR_NET, "invalid authentication scheme"); git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
return -1; return -1;
} }
...@@ -285,7 +287,7 @@ static int certificate_check(winhttp_stream *s, int valid) ...@@ -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 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->certificate_check_cb == NULL && !valid) {
if (!git_error_last()) 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; return GIT_ECERTIFICATE;
} }
...@@ -309,7 +311,7 @@ static int certificate_check(winhttp_stream *s, int valid) ...@@ -309,7 +311,7 @@ static int certificate_check(winhttp_stream *s, int valid)
error = valid ? 0 : GIT_ECERTIFICATE; error = valid ? 0 : GIT_ECERTIFICATE;
if (error < 0 && !git_error_last()) 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; return error;
} }
...@@ -438,7 +440,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -438,7 +440,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error; goto on_error;
if (strcmp(t->proxy.url.scheme, "http") != 0 && strcmp(t->proxy.url.scheme, "https") != 0) { 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; error = -1;
goto on_error; goto on_error;
} }
...@@ -711,21 +713,21 @@ static void CALLBACK winhttp_status( ...@@ -711,21 +713,21 @@ static void CALLBACK winhttp_status(
status = *((DWORD *)info); status = *((DWORD *)info);
if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)) 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)) 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)) 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)) 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)) 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)) 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)) 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 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( static int winhttp_connect(
...@@ -830,7 +832,7 @@ on_error: ...@@ -830,7 +832,7 @@ on_error:
return 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; int attempts;
bool success; bool success;
...@@ -841,7 +843,7 @@ static int do_send_request(winhttp_stream *s, size_t len, int ignore_length) ...@@ -841,7 +843,7 @@ static int do_send_request(winhttp_stream *s, size_t len, int ignore_length)
} }
for (attempts = 0; attempts < 5; attempts++) { for (attempts = 0; attempts < 5; attempts++) {
if (ignore_length) { if (chunked) {
success = WinHttpSendRequest(s->request, success = WinHttpSendRequest(s->request,
WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, WINHTTP_NO_REQUEST_DATA, 0,
...@@ -860,13 +862,13 @@ static int do_send_request(winhttp_stream *s, size_t len, int ignore_length) ...@@ -860,13 +862,13 @@ static int do_send_request(winhttp_stream *s, size_t len, int ignore_length)
return success ? 0 : -1; 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; int request_failed = 0, cert_valid = 1, error = 0;
DWORD ignore_flags; DWORD ignore_flags;
git_error_clear(); 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) { if (GetLastError() != ERROR_WINHTTP_SECURE_FAILURE) {
git_error_set(GIT_ERROR_OS, "failed to send request"); git_error_set(GIT_ERROR_OS, "failed to send request");
return -1; return -1;
...@@ -895,7 +897,7 @@ static int send_request(winhttp_stream *s, size_t len, int ignore_length) ...@@ -895,7 +897,7 @@ static int send_request(winhttp_stream *s, size_t len, int ignore_length)
return -1; 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"); git_error_set(GIT_ERROR_OS, "failed to send request with unchecked certificate");
return error; return error;
...@@ -969,7 +971,7 @@ static int winhttp_stream_read( ...@@ -969,7 +971,7 @@ static int winhttp_stream_read(
replay: replay:
/* Enforce a reasonable cap on the number of replays */ /* Enforce a reasonable cap on the number of replays */
if (replay_count++ >= GIT_HTTP_REPLAY_MAX) { 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; return -1;
} }
...@@ -984,7 +986,7 @@ replay: ...@@ -984,7 +986,7 @@ replay:
if (!s->sent_request) { 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; return error;
s->sent_request = 1; s->sent_request = 1;
...@@ -1128,7 +1130,7 @@ replay: ...@@ -1128,7 +1130,7 @@ replay:
if (!git__prefixcmp_icase(location8, prefix_https)) { if (!git__prefixcmp_icase(location8, prefix_https)) {
/* Upgrade to secure connection; disconnect and start over */ /* 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); git__free(location8);
return -1; return -1;
} }
...@@ -1175,7 +1177,7 @@ replay: ...@@ -1175,7 +1177,7 @@ replay:
} }
if (HTTP_STATUS_OK != status_code) { 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; return -1;
} }
...@@ -1202,7 +1204,7 @@ replay: ...@@ -1202,7 +1204,7 @@ replay:
} }
if (wcscmp(expected_content_type, content_type)) { 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; return -1;
} }
...@@ -1237,11 +1239,11 @@ static int winhttp_stream_write_single( ...@@ -1237,11 +1239,11 @@ static int winhttp_stream_write_single(
/* This implementation of write permits only a single call. */ /* This implementation of write permits only a single call. */
if (s->sent_request) { 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; return -1;
} }
if ((error = send_request(s, len, 0)) < 0) if ((error = send_request(s, len, false)) < 0)
return error; return error;
s->sent_request = 1; s->sent_request = 1;
...@@ -1268,12 +1270,12 @@ static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch) ...@@ -1268,12 +1270,12 @@ static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch)
if (RPC_S_OK != status && if (RPC_S_OK != status &&
RPC_S_UUID_LOCAL_ONLY != status && RPC_S_UUID_LOCAL_ONLY != status &&
RPC_S_UUID_NO_ADDRESS != 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; return -1;
} }
if (buffer_len_cch < UUID_LENGTH_CCH + 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; return -1;
} }
...@@ -1380,7 +1382,7 @@ static int winhttp_stream_write_chunked( ...@@ -1380,7 +1382,7 @@ static int winhttp_stream_write_chunked(
return -1; return -1;
} }
if ((error = send_request(s, 0, 1)) < 0) if ((error = send_request(s, 0, true)) < 0)
return error; return error;
s->sent_request = 1; s->sent_request = 1;
......
...@@ -60,9 +60,11 @@ FUNCTION(ADD_CLAR_TEST name) ...@@ -60,9 +60,11 @@ FUNCTION(ADD_CLAR_TEST name)
ENDIF() ENDIF()
ENDFUNCTION(ADD_CLAR_TEST) ENDFUNCTION(ADD_CLAR_TEST)
ADD_CLAR_TEST(offline -v -xonline) ADD_CLAR_TEST(offline -v -xonline)
ADD_CLAR_TEST(invasive -v -score::ftruncate -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root) ADD_CLAR_TEST(invasive -v -score::ftruncate -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root)
ADD_CLAR_TEST(online -v -sonline) ADD_CLAR_TEST(online -v -sonline)
ADD_CLAR_TEST(gitdaemon -v -sonline::push) 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(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(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" #include "clar_libgit2_trace.h"
#if defined(GIT_TRACE)
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "clar_libgit2_timer.h" #include "clar_libgit2_timer.h"
#include "trace.h" #include "trace.h"
...@@ -264,15 +261,3 @@ void cl_global_trace_disable(void) ...@@ -264,15 +261,3 @@ void cl_global_trace_disable(void)
* once. * 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) ...@@ -18,7 +18,7 @@ void test_network_redirect__redirect_http(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, cl_git_pass(git_net_url_parse(&conndata,
"http://example.com/foo/bar/baz")); "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")); "http://example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.scheme, "http"); cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(conndata.host, "example.com"); cl_assert_equal_s(conndata.host, "example.com");
...@@ -32,7 +32,7 @@ void test_network_redirect__redirect_ssl(void) ...@@ -32,7 +32,7 @@ void test_network_redirect__redirect_ssl(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, cl_git_pass(git_net_url_parse(&conndata,
"https://example.com/foo/bar/baz")); "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")); "https://example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.scheme, "https"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com"); cl_assert_equal_s(conndata.host, "example.com");
...@@ -46,7 +46,7 @@ void test_network_redirect__redirect_leaves_root_path(void) ...@@ -46,7 +46,7 @@ void test_network_redirect__redirect_leaves_root_path(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, cl_git_pass(git_net_url_parse(&conndata,
"https://example.com/foo/bar/baz")); "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")); "https://example.com/foo/bar/baz", "/foo/bar/baz"));
cl_assert_equal_s(conndata.scheme, "https"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com"); cl_assert_equal_s(conndata.host, "example.com");
...@@ -60,7 +60,7 @@ void test_network_redirect__redirect_encoded_username_password(void) ...@@ -60,7 +60,7 @@ void test_network_redirect__redirect_encoded_username_password(void)
{ {
cl_git_pass(git_net_url_parse(&conndata, 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(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")); "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.scheme, "https");
cl_assert_equal_s(conndata.host, "example.com"); cl_assert_equal_s(conndata.host, "example.com");
...@@ -73,7 +73,7 @@ void test_network_redirect__redirect_encoded_username_password(void) ...@@ -73,7 +73,7 @@ void test_network_redirect__redirect_encoded_username_password(void)
void test_network_redirect__redirect_cross_host_denied(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_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), "https://foo.com/bar/baz", NULL),
-1); -1);
} }
...@@ -81,7 +81,7 @@ void test_network_redirect__redirect_cross_host_denied(void) ...@@ -81,7 +81,7 @@ void test_network_redirect__redirect_cross_host_denied(void)
void test_network_redirect__redirect_http_downgrade_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_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), "http://foo.com/bar/baz", NULL),
-1); -1);
} }
...@@ -89,7 +89,7 @@ void test_network_redirect__redirect_http_downgrade_denied(void) ...@@ -89,7 +89,7 @@ void test_network_redirect__redirect_http_downgrade_denied(void)
void test_network_redirect__redirect_relative(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(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)); "/zap/baz/biff?bam", NULL));
cl_assert_equal_s(conndata.scheme, "http"); cl_assert_equal_s(conndata.scheme, "http");
cl_assert_equal_s(conndata.host, "foo.com"); cl_assert_equal_s(conndata.host, "foo.com");
...@@ -102,7 +102,7 @@ void test_network_redirect__redirect_relative(void) ...@@ -102,7 +102,7 @@ void test_network_redirect__redirect_relative(void)
void test_network_redirect__redirect_relative_ssl(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(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)); "/zap/baz/biff?bam", NULL));
cl_assert_equal_s(conndata.scheme, "https"); cl_assert_equal_s(conndata.scheme, "https");
cl_assert_equal_s(conndata.host, "foo.com"); cl_assert_equal_s(conndata.host, "foo.com");
...@@ -115,7 +115,7 @@ void test_network_redirect__redirect_relative_ssl(void) ...@@ -115,7 +115,7 @@ void test_network_redirect__redirect_relative_ssl(void)
void test_network_redirect__service_query_no_query_params_in_location(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(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")); "/baz/info/refs", "/info/refs?service=git-upload-pack"));
cl_assert_equal_s(conndata.path, "/baz"); cl_assert_equal_s(conndata.path, "/baz");
} }
...@@ -123,7 +123,7 @@ void test_network_redirect__service_query_no_query_params_in_location(void) ...@@ -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) 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(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")); "/baz/info/refs?service=git-upload-pack", "/info/refs?service=git-upload-pack"));
cl_assert_equal_s(conndata.path, "/baz"); cl_assert_equal_s(conndata.path, "/baz");
} }
...@@ -30,6 +30,7 @@ static char *_remote_proxy_host = NULL; ...@@ -30,6 +30,7 @@ static char *_remote_proxy_host = NULL;
static char *_remote_proxy_user = NULL; static char *_remote_proxy_user = NULL;
static char *_remote_proxy_pass = NULL; static char *_remote_proxy_pass = NULL;
static char *_remote_proxy_selfsigned = NULL; static char *_remote_proxy_selfsigned = NULL;
static char *_remote_expectcontinue = NULL;
static int _orig_proxies_need_reset = 0; static int _orig_proxies_need_reset = 0;
static char *_orig_http_proxy = NULL; static char *_orig_http_proxy = NULL;
...@@ -74,6 +75,10 @@ void test_online_clone__initialize(void) ...@@ -74,6 +75,10 @@ void test_online_clone__initialize(void)
_remote_proxy_user = cl_getenv("GITTEST_REMOTE_PROXY_USER"); _remote_proxy_user = cl_getenv("GITTEST_REMOTE_PROXY_USER");
_remote_proxy_pass = cl_getenv("GITTEST_REMOTE_PROXY_PASS"); _remote_proxy_pass = cl_getenv("GITTEST_REMOTE_PROXY_PASS");
_remote_proxy_selfsigned = cl_getenv("GITTEST_REMOTE_PROXY_SELFSIGNED"); _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; _orig_proxies_need_reset = 0;
} }
...@@ -99,6 +104,7 @@ void test_online_clone__cleanup(void) ...@@ -99,6 +104,7 @@ void test_online_clone__cleanup(void)
git__free(_remote_proxy_user); git__free(_remote_proxy_user);
git__free(_remote_proxy_pass); git__free(_remote_proxy_pass);
git__free(_remote_proxy_selfsigned); git__free(_remote_proxy_selfsigned);
git__free(_remote_expectcontinue);
if (_orig_proxies_need_reset) { if (_orig_proxies_need_reset) {
cl_setenv("HTTP_PROXY", _orig_http_proxy); cl_setenv("HTTP_PROXY", _orig_http_proxy);
...@@ -455,8 +461,8 @@ void test_online_clone__can_cancel(void) ...@@ -455,8 +461,8 @@ void test_online_clone__can_cancel(void)
{ {
g_options.fetch_opts.callbacks.transfer_progress = cancel_at_half; g_options.fetch_opts.callbacks.transfer_progress = cancel_at_half;
cl_git_fail_with( cl_git_fail_with(4321,
git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), 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, 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; ...@@ -19,6 +19,7 @@ static char *_remote_ssh_pubkey = NULL;
static char *_remote_ssh_passphrase = NULL; static char *_remote_ssh_passphrase = NULL;
static char *_remote_default = 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 *); static int cred_acquire_cb(git_cred **, const char *, const char *, unsigned int, void *);
...@@ -366,12 +367,16 @@ void test_online_push__initialize(void) ...@@ -366,12 +367,16 @@ void test_online_push__initialize(void)
_remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY"); _remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
_remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE"); _remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
_remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT"); _remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT");
_remote_expectcontinue = cl_getenv("GITTEST_REMOTE_EXPECTCONTINUE");
_remote = NULL; _remote = NULL;
/* Skip the test if we're missing the remote URL */ /* Skip the test if we're missing the remote URL */
if (!_remote_url) if (!_remote_url)
cl_skip(); 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)); cl_git_pass(git_remote_create(&_remote, _repo, "test", _remote_url));
record_callbacks_data_clear(&_record_cbs_data); record_callbacks_data_clear(&_record_cbs_data);
...@@ -417,10 +422,13 @@ void test_online_push__cleanup(void) ...@@ -417,10 +422,13 @@ void test_online_push__cleanup(void)
git__free(_remote_ssh_pubkey); git__free(_remote_ssh_pubkey);
git__free(_remote_ssh_passphrase); git__free(_remote_ssh_passphrase);
git__free(_remote_default); git__free(_remote_default);
git__free(_remote_expectcontinue);
/* Freed by cl_git_sandbox_cleanup */ /* Freed by cl_git_sandbox_cleanup */
_repo = NULL; _repo = NULL;
git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 0);
record_callbacks_data_clear(&_record_cbs_data); record_callbacks_data_clear(&_record_cbs_data);
cl_fixture_cleanup("testrepo.git"); 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