summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2015-06-24 23:33:46 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2015-06-24 23:33:46 +0200
commite1f434f8643f26d48ee8de21d715069de76b14e1 (patch)
treef92e4580127930303c98a49d5651b9e2cd8a8b4c
parent9d5efab89f3e8579e1641c1d5c726a2dcdb8c014 (diff)
parent58ca8c7e1ff820b9deecbb1bbd34ad7408f47da9 (diff)
downloadlibgit2-e1f434f8643f26d48ee8de21d715069de76b14e1.tar.gz
Merge pull request #3183 from libgit2/cmn/curl-stream
Implement a cURL stream
-rw-r--r--CHANGELOG.md3
-rw-r--r--CMakeLists.txt11
-rw-r--r--THREADING.md26
-rw-r--r--include/git2/sys/stream.h2
-rw-r--r--include/git2/types.h12
-rw-r--r--src/curl_stream.c257
-rw-r--r--src/curl_stream.h14
-rw-r--r--src/openssl_stream.c122
-rw-r--r--src/stransport_stream.c18
-rw-r--r--src/stream.h15
-rw-r--r--src/transports/http.c18
11 files changed, 475 insertions, 23 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1340f78f9..86f995545 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -72,6 +72,9 @@ support for HTTPS connections insead of OpenSSL.
* The race condition mitigations described in `racy-git.txt` have been
implemented.
+* If libcurl is installed, we will use it to connect to HTTP(S)
+ servers.
+
### API additions
* The `git_merge_options` gained a `file_flags` member.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cecccb331..012814e7a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -38,6 +38,7 @@ OPTION( USE_ICONV "Link with and use iconv library" OFF )
OPTION( USE_SSH "Link with libssh to enable SSH support" ON )
OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF )
OPTION( VALGRIND "Configure build for valgrind" OFF )
+OPTION( CURL "User curl for HTTP if available" ON)
IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
SET( USE_ICONV ON )
@@ -199,10 +200,20 @@ IF (WIN32 AND WINHTTP)
LINK_LIBRARIES(winhttp rpcrt4 crypt32)
ELSE ()
+ IF (CURL)
+ FIND_PACKAGE(CURL)
+ ENDIF ()
+
IF (NOT AMIGA AND USE_OPENSSL)
FIND_PACKAGE(OpenSSL)
ENDIF ()
+ IF (CURL_FOUND)
+ ADD_DEFINITIONS(-DGIT_CURL)
+ INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIRS})
+ LINK_LIBRARIES(${CURL_LIBRARIES})
+ ENDIF()
+
FIND_PACKAGE(HTTP_Parser)
IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
diff --git a/THREADING.md b/THREADING.md
index cf7ac1ff0..3717d6c88 100644
--- a/THREADING.md
+++ b/THREADING.md
@@ -47,9 +47,14 @@ you.
On Mac OS X
-----------
-On OS X, the library makes use of CommonCrypto and SecureTransport for
-cryptographic support. These are thread-safe and you do not need to do
-anything special.
+By default we use libcurl to perform the encryption. The
+system-provided libcurl uses SecureTransport, so no special steps are
+necessary. If you link against another libcurl (e.g. from homebrew)
+refer to the general case.
+
+If the option to use libcurl was deactivated, the library makes use of
+CommonCrypto and SecureTransport for cryptographic support. These are
+thread-safe and you do not need to do anything special.
Note that libssh2 may still use OpenSSL itself. In that case, the
general case still affects you if you use ssh.
@@ -57,12 +62,15 @@ general case still affects you if you use ssh.
General Case
------------
-On the rest of the platforms, libgit2 uses OpenSSL to be able to use
-HTTPS as a transport. This library is made to be thread-implementation
-agnostic, and the users of the library must set which locking function
-it should use. This means that libgit2 cannot know what to set as the
-user of libgit2 may use OpenSSL independently and the locking settings
-must survive libgit2 shutting down.
+By default we use libcurl, which has its own ![recommendations for
+thread safety](http://curl.haxx.se/libcurl/c/libcurl-tutorial.html#Multi-threading).
+
+If libcurl was not found or was disabled, libgit2 uses OpenSSL to be
+able to use HTTPS as a transport. This library is made to be
+thread-implementation agnostic, and the users of the library must set
+which locking function it should use. This means that libgit2 cannot
+know what to set as the user of libgit2 may use OpenSSL independently
+and the locking settings must survive libgit2 shutting down.
libgit2 does provide a last-resort convenience function
`git_openssl_set_locking()` (available in `sys/openssl.h`) to use the
diff --git a/include/git2/sys/stream.h b/include/git2/sys/stream.h
index c22179fab..55a714bbb 100644
--- a/include/git2/sys/stream.h
+++ b/include/git2/sys/stream.h
@@ -29,8 +29,10 @@ typedef struct git_stream {
int version;
int encrypted;
+ int proxy_support;
int (*connect)(struct git_stream *);
int (*certificate)(git_cert **, struct git_stream *);
+ int (*set_proxy)(struct git_stream *, const char *proxy_url);
ssize_t (*read)(struct git_stream *, void *, size_t);
ssize_t (*write)(struct git_stream *, const char *, size_t, int);
int (*close)(struct git_stream *);
diff --git a/include/git2/types.h b/include/git2/types.h
index d1e7cd92c..c97e5ba61 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -284,6 +284,11 @@ typedef int (*git_transport_message_cb)(const char *str, int len, void *payload)
* Type of host certificate structure that is passed to the check callback
*/
typedef enum git_cert_t {
+ /**
+ * No information about the certificate is available. This may
+ * happen when using curl.
+ */
+ GIT_CERT_NONE,
/**
* The `data` argument to the callback will be a pointer to
* the DER-encoded data.
@@ -294,6 +299,13 @@ typedef enum git_cert_t {
* `git_cert_hostkey` structure.
*/
GIT_CERT_HOSTKEY_LIBSSH2,
+ /**
+ * The `data` argument to the callback will be a pointer to a
+ * `git_strarray` with `name:content` strings containing
+ * information about the certificate. This is used when using
+ * curl.
+ */
+ GIT_CERT_STRARRAY,
} git_cert_t;
/**
diff --git a/src/curl_stream.c b/src/curl_stream.c
new file mode 100644
index 000000000..6534bdbbe
--- /dev/null
+++ b/src/curl_stream.c
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ */
+
+#ifdef GIT_CURL
+
+#include <curl/curl.h>
+
+#include "stream.h"
+#include "git2/transport.h"
+#include "buffer.h"
+#include "vector.h"
+
+typedef struct {
+ git_stream parent;
+ CURL *handle;
+ curl_socket_t socket;
+ char curl_error[CURL_ERROR_SIZE + 1];
+ git_cert_x509 cert_info;
+ git_strarray cert_info_strings;
+} curl_stream;
+
+static int seterr_curl(curl_stream *s)
+{
+ giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error);
+ return -1;
+}
+
+static int curls_connect(git_stream *stream)
+{
+ curl_stream *s = (curl_stream *) stream;
+ long sockextr;
+ int failed_cert = 0;
+ CURLcode res;
+ res = curl_easy_perform(s->handle);
+
+ if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
+ return seterr_curl(s);
+ if (res == CURLE_PEER_FAILED_VERIFICATION)
+ failed_cert = 1;
+
+ if ((res = curl_easy_getinfo(s->handle, CURLINFO_LASTSOCKET, &sockextr)) != CURLE_OK)
+ return seterr_curl(s);
+
+ s->socket = sockextr;
+
+ if (s->parent.encrypted && failed_cert)
+ return GIT_ECERTIFICATE;
+
+ return 0;
+}
+
+static int curls_certificate(git_cert **out, git_stream *stream)
+{
+ int error;
+ CURLcode res;
+ struct curl_slist *slist;
+ struct curl_certinfo *certinfo;
+ git_vector strings = GIT_VECTOR_INIT;
+ curl_stream *s = (curl_stream *) stream;
+
+ if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK)
+ return seterr_curl(s);
+
+ /* No information is available, can happen with SecureTransport */
+ if (certinfo->num_of_certs == 0) {
+ s->cert_info.cert_type = GIT_CERT_NONE;
+ s->cert_info.data = NULL;
+ s->cert_info.len = 0;
+ return 0;
+ }
+
+ if ((error = git_vector_init(&strings, 8, NULL)) < 0)
+ return error;
+
+ for (slist = certinfo->certinfo[0]; slist; slist = slist->next) {
+ char *str = git__strdup(slist->data);
+ GITERR_CHECK_ALLOC(str);
+ }
+
+ /* Copy the contents of the vector into a strarray so we can expose them */
+ s->cert_info_strings.strings = (char **) strings.contents;
+ s->cert_info_strings.count = strings.length;
+
+ s->cert_info.cert_type = GIT_CERT_STRARRAY;
+ s->cert_info.data = &s->cert_info_strings;
+ s->cert_info.len = strings.length;
+
+ *out = (git_cert *) &s->cert_info;
+
+ return 0;
+}
+
+static int curls_set_proxy(git_stream *stream, const char *proxy_url)
+{
+ CURLcode res;
+ curl_stream *s = (curl_stream *) stream;
+
+ if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, proxy_url)) != CURLE_OK)
+ return seterr_curl(s);
+
+ return 0;
+}
+
+static int wait_for(curl_socket_t fd, bool reading)
+{
+ int ret;
+ fd_set infd, outfd, errfd;
+
+ FD_ZERO(&infd);
+ FD_ZERO(&outfd);
+ FD_ZERO(&errfd);
+
+ FD_SET(fd, &errfd);
+ if (reading)
+ FD_SET(fd, &infd);
+ else
+ FD_SET(fd, &outfd);
+
+ if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) {
+ giterr_set(GITERR_OS, "error in select");
+ return -1;
+ }
+
+ return 0;
+}
+
+static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags)
+{
+ int error;
+ size_t off = 0, sent;
+ CURLcode res;
+ curl_stream *s = (curl_stream *) stream;
+
+ GIT_UNUSED(flags);
+
+ do {
+ if ((error = wait_for(s->socket, false)) < 0)
+ return error;
+
+ res = curl_easy_send(s->handle, data + off, len - off, &sent);
+ if (res == CURLE_OK)
+ off += sent;
+ } while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len);
+
+ if (res != CURLE_OK)
+ return seterr_curl(s);
+
+ return len;
+}
+
+static ssize_t curls_read(git_stream *stream, void *data, size_t len)
+{
+ int error;
+ size_t read;
+ CURLcode res;
+ curl_stream *s = (curl_stream *) stream;
+
+ do {
+ if ((error = wait_for(s->socket, true)) < 0)
+ return error;
+
+ res = curl_easy_recv(s->handle, data, len, &read);
+ } while (res == CURLE_AGAIN);
+
+ if (res != CURLE_OK)
+ return seterr_curl(s);
+
+ return read;
+}
+
+static int curls_close(git_stream *stream)
+{
+ curl_stream *s = (curl_stream *) stream;
+
+ if (!s->handle)
+ return 0;
+
+ curl_easy_cleanup(s->handle);
+ s->handle = NULL;
+ s->socket = 0;
+
+ return 0;
+}
+
+static void curls_free(git_stream *stream)
+{
+ curl_stream *s = (curl_stream *) stream;
+
+ curls_close(stream);
+ git_strarray_free(&s->cert_info_strings);
+ git__free(s);
+}
+
+int git_curl_stream_new(git_stream **out, const char *host, const char *port)
+{
+ curl_stream *st;
+ CURL *handle;
+ int iport = 0, error;
+
+ st = git__calloc(1, sizeof(curl_stream));
+ GITERR_CHECK_ALLOC(st);
+
+ handle = curl_easy_init();
+ if (handle == NULL) {
+ giterr_set(GITERR_NET, "failed to create curl handle");
+ return -1;
+ }
+
+ if ((error = git__strtol32(&iport, port, NULL, 10)) < 0)
+ return error;
+
+ curl_easy_setopt(handle, CURLOPT_URL, host);
+ curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error);
+ curl_easy_setopt(handle, CURLOPT_PORT, iport);
+ curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1);
+ curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1);
+ curl_easy_setopt(handle, CURLOPT_CERTINFO, 1);
+ curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1);
+
+ /* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.encrypted = 0; /* we don't encrypt ourselves */
+ st->parent.proxy_support = 1;
+ st->parent.connect = curls_connect;
+ st->parent.certificate = curls_certificate;
+ st->parent.set_proxy = curls_set_proxy;
+ st->parent.read = curls_read;
+ st->parent.write = curls_write;
+ st->parent.close = curls_close;
+ st->parent.free = curls_free;
+ st->handle = handle;
+
+ *out = (git_stream *) st;
+ return 0;
+}
+
+#else
+
+#include "stream.h"
+
+int git_curl_stream_new(git_stream **out, const char *host, const char *port)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(host);
+ GIT_UNUSED(port);
+
+ giterr_set(GITERR_NET, "curl is not supported in this version");
+ return -1;
+}
+
+
+#endif
diff --git a/src/curl_stream.h b/src/curl_stream.h
new file mode 100644
index 000000000..283f0fe40
--- /dev/null
+++ b/src/curl_stream.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_curl_stream_h__
+#define INCLUDE_curl_stream_h__
+
+#include "git2/sys/stream.h"
+
+extern int git_curl_stream_new(git_stream **out, const char *host, const char *port);
+
+#endif
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index 9b2d5951c..412dee739 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -16,6 +16,10 @@
#include "netops.h"
#include "git2/transport.h"
+#ifdef GIT_CURL
+# include "curl_stream.h"
+#endif
+
#ifndef GIT_WIN32
# include <sys/types.h>
# include <sys/socket.h>
@@ -25,6 +29,79 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>
+#include <openssl/bio.h>
+
+static int bio_create(BIO *b)
+{
+ b->init = 1;
+ b->num = 0;
+ b->ptr = NULL;
+ b->flags = 0;
+
+ return 1;
+}
+
+static int bio_destroy(BIO *b)
+{
+ if (!b)
+ return 0;
+
+ b->init = 0;
+ b->num = 0;
+ b->ptr = NULL;
+ b->flags = 0;
+
+ return 1;
+}
+
+static int bio_read(BIO *b, char *buf, int len)
+{
+ git_stream *io = (git_stream *) b->ptr;
+ return (int) git_stream_read(io, buf, len);
+}
+
+static int bio_write(BIO *b, const char *buf, int len)
+{
+ git_stream *io = (git_stream *) b->ptr;
+ return (int) git_stream_write(io, buf, len, 0);
+}
+
+static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+ GIT_UNUSED(b);
+ GIT_UNUSED(num);
+ GIT_UNUSED(ptr);
+
+ if (cmd == BIO_CTRL_FLUSH)
+ return 1;
+
+ return 0;
+}
+
+static int bio_gets(BIO *b, char *buf, int len)
+{
+ GIT_UNUSED(b);
+ GIT_UNUSED(buf);
+ GIT_UNUSED(len);
+ return -1;
+}
+
+static int bio_puts(BIO *b, const char *str)
+{
+ return bio_write(b, str, strlen(str));
+}
+
+static BIO_METHOD git_stream_bio_method = {
+ BIO_TYPE_SOURCE_SINK,
+ "git_stream",
+ bio_write,
+ bio_read,
+ bio_puts,
+ bio_gets,
+ bio_ctrl,
+ bio_create,
+ bio_destroy
+};
static int ssl_set_error(SSL *ssl, int error)
{
@@ -224,7 +301,8 @@ cert_fail_name:
typedef struct {
git_stream parent;
- git_socket_stream *socket;
+ git_stream *io;
+ char *host;
SSL *ssl;
git_cert_x509 cert_info;
} openssl_stream;
@@ -234,23 +312,24 @@ int openssl_close(git_stream *stream);
int openssl_connect(git_stream *stream)
{
int ret;
+ BIO *bio;
openssl_stream *st = (openssl_stream *) stream;
- if ((ret = git_stream_connect((git_stream *)st->socket)) < 0)
+ if ((ret = git_stream_connect(st->io)) < 0)
return ret;
- if ((ret = SSL_set_fd(st->ssl, st->socket->s)) <= 0) {
- openssl_close((git_stream *) st);
- return ssl_set_error(st->ssl, ret);
- }
+ bio = BIO_new(&git_stream_bio_method);
+ GITERR_CHECK_ALLOC(bio);
+ bio->ptr = st->io;
+ SSL_set_bio(st->ssl, bio, bio);
/* specify the host in case SNI is needed */
- SSL_set_tlsext_host_name(st->ssl, st->socket->host);
+ SSL_set_tlsext_host_name(st->ssl, st->host);
if ((ret = SSL_connect(st->ssl)) <= 0)
return ssl_set_error(st->ssl, ret);
- return verify_server_cert(st->ssl, st->socket->host);
+ return verify_server_cert(st->ssl, st->host);
}
int openssl_certificate(git_cert **out, git_stream *stream)
@@ -287,6 +366,13 @@ int openssl_certificate(git_cert **out, git_stream *stream)
return 0;
}
+static int openssl_set_proxy(git_stream *stream, const char *proxy_url)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+
+ return git_stream_set_proxy(st->io, proxy_url);
+}
+
ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags)
{
openssl_stream *st = (openssl_stream *) stream;
@@ -320,7 +406,7 @@ int openssl_close(git_stream *stream)
if ((ret = ssl_teardown(st->ssl)) < 0)
return -1;
- return git_stream_close((git_stream *)st->socket);
+ return git_stream_close(st->io);
}
void openssl_free(git_stream *stream)
@@ -328,19 +414,26 @@ void openssl_free(git_stream *stream)
openssl_stream *st = (openssl_stream *) stream;
git__free(st->cert_info.data);
- git_stream_free((git_stream *) st->socket);
+ git_stream_free(st->io);
git__free(st);
}
int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
{
+ int error;
openssl_stream *st;
st = git__calloc(1, sizeof(openssl_stream));
GITERR_CHECK_ALLOC(st);
- if (git_socket_stream_new((git_stream **) &st->socket, host, port))
- return -1;
+#ifdef GIT_CURL
+ error = git_curl_stream_new(&st->io, host, port);
+#else
+ error = git_socket_stream_new(&st->io, host, port)
+#endif
+
+ if (error < 0)
+ return error;
st->ssl = SSL_new(git__ssl_ctx);
if (st->ssl == NULL) {
@@ -348,10 +441,15 @@ int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
return -1;
}
+ st->host = git__strdup(host);
+ GITERR_CHECK_ALLOC(st->host);
+
st->parent.version = GIT_STREAM_VERSION;
st->parent.encrypted = 1;
+ st->parent.proxy_support = git_stream_supports_proxy(st->io);
st->parent.connect = openssl_connect;
st->parent.certificate = openssl_certificate;
+ st->parent.set_proxy = openssl_set_proxy;
st->parent.read = openssl_read;
st->parent.write = openssl_write;
st->parent.close = openssl_close;
diff --git a/src/stransport_stream.c b/src/stransport_stream.c
index 34c38b22d..bbc2d32d6 100644
--- a/src/stransport_stream.c
+++ b/src/stransport_stream.c
@@ -14,6 +14,7 @@
#include "git2/transport.h"
#include "socket_stream.h"
+#include "curl_stream.h"
int stransport_error(OSStatus ret)
{
@@ -115,6 +116,13 @@ int stransport_certificate(git_cert **out, git_stream *stream)
return 0;
}
+int stransport_set_proxy(git_stream *stream, const char *proxy)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+
+ return git_stream_set_proxy(st->io, proxy);
+}
+
/*
* Contrary to typical network IO callbacks, Secure Transport write callback is
* expected to write *all* passed data, not just as much as it can, and any
@@ -233,7 +241,13 @@ int git_stransport_stream_new(git_stream **out, const char *host, const char *po
st = git__calloc(1, sizeof(stransport_stream));
GITERR_CHECK_ALLOC(st);
- if ((error = git_socket_stream_new(&st->io, host, port)) < 0){
+#ifdef GIT_CURL
+ error = git_curl_stream_new(&st->io, host, port);
+#else
+ error = git_socket_stream_new(&st->io, host, port)
+#endif
+
+ if (error < 0){
git__free(st);
return error;
}
@@ -256,8 +270,10 @@ int git_stransport_stream_new(git_stream **out, const char *host, const char *po
st->parent.version = GIT_STREAM_VERSION;
st->parent.encrypted = 1;
+ st->parent.proxy_support = git_stream_supports_proxy(st->io);
st->parent.connect = stransport_connect;
st->parent.certificate = stransport_certificate;
+ st->parent.set_proxy = stransport_set_proxy;
st->parent.read = stransport_read;
st->parent.write = stransport_write;
st->parent.close = stransport_close;
diff --git a/src/stream.h b/src/stream.h
index d810e704d..43fcc3045 100644
--- a/src/stream.h
+++ b/src/stream.h
@@ -30,6 +30,21 @@ GIT_INLINE(int) git_stream_certificate(git_cert **out, git_stream *st)
return st->certificate(out, st);
}
+GIT_INLINE(int) git_stream_supports_proxy(git_stream *st)
+{
+ return st->proxy_support;
+}
+
+GIT_INLINE(int) git_stream_set_proxy(git_stream *st, const char *proxy_url)
+{
+ if (!st->proxy_support) {
+ giterr_set(GITERR_INVALID, "proxy not supported on this stream");
+ return -1;
+ }
+
+ return st->set_proxy(st, proxy_url);
+}
+
GIT_INLINE(ssize_t) git_stream_read(git_stream *st, void *data, size_t len)
{
return st->read(st, data, len);
diff --git a/src/transports/http.c b/src/transports/http.c
index 4aca2755b..bae2a328d 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -10,11 +10,13 @@
#include "http_parser.h"
#include "buffer.h"
#include "netops.h"
+#include "remote.h"
#include "smart.h"
#include "auth.h"
#include "auth_negotiate.h"
#include "tls_stream.h"
#include "socket_stream.h"
+#include "curl_stream.h"
git_http_auth_scheme auth_schemes[] = {
{ GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
@@ -532,6 +534,7 @@ static int write_chunk(git_stream *io, const char *buffer, size_t len)
static int http_connect(http_subtransport *t)
{
int error;
+ char *proxy_url;
if (t->connected &&
http_should_keep_alive(&t->parser) &&
@@ -547,7 +550,11 @@ static int http_connect(http_subtransport *t)
if (t->connection_data.use_ssl) {
error = git_tls_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
} else {
+#ifdef GIT_CURL
+ error = git_curl_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
+#else
error = git_socket_stream_new(&t->io, t->connection_data.host, t->connection_data.port);
+#endif
}
if (error < 0)
@@ -555,9 +562,18 @@ static int http_connect(http_subtransport *t)
GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream");
+ if (git_stream_supports_proxy(t->io) &&
+ !git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url)) {
+ error = git_stream_set_proxy(t->io, proxy_url);
+ git__free(proxy_url);
+
+ if (error < 0)
+ return error;
+ }
+
error = git_stream_connect(t->io);
-#if defined(GIT_OPENSSL) || defined(GIT_SECURE_TRANSPORT)
+#if defined(GIT_OPENSSL) || defined(GIT_SECURE_TRANSPORT) || defined(GIT_CURL)
if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL &&
git_stream_is_encrypted(t->io)) {
git_cert *cert;