Commit 5aa28a8f2d47ac5939deffaabf70110e4ea29922

Edward Thomson 2015-11-04T14:16:24

Merge pull request #3465 from libgit2/cmn/tls-register stream: allow registering a user-provided TLS constructor

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b2e433..dec40e4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,11 +17,16 @@ v0.23 + 1
   the opportunity for concurrent operations and not committing any
   changes until the unlock.
 
+
 * `git_diff_options` added a new callback `progress_cb` to report on the
   progress of the diff as files are being compared. The documentation of
   the existing callback `notify_cb` was updated to reflect that it only
   gets called when new deltas are added to the diff.
 
+* `git_stream_register_tls()` lets you register a callback to be used
+  as the constructor for a TLS stream instead of the libgit2 built-in
+  one.
+
 ### API removals
 
 ### Breaking API changes
diff --git a/include/git2/sys/stream.h b/include/git2/sys/stream.h
index 55a714b..2b4ff7f 100644
--- a/include/git2/sys/stream.h
+++ b/include/git2/sys/stream.h
@@ -39,6 +39,19 @@ typedef struct git_stream {
 	void (*free)(struct git_stream *);
 } git_stream;
 
+typedef int (*git_stream_cb)(git_stream **out, const char *host, const char *port);
+
+/**
+ * Register a TLS stream constructor for the library to use
+ *
+ * If a constructor is already set, it will be overwritten. Pass
+ * `NULL` in order to deregister the current constructor.
+ *
+ * @param ctor the constructor to use
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_stream_register_tls(git_stream_cb ctor);
+
 GIT_END_DECL
 
 #endif
diff --git a/src/stream.h b/src/stream.h
index 43fcc30..4692c71 100644
--- a/src/stream.h
+++ b/src/stream.h
@@ -62,6 +62,9 @@ GIT_INLINE(int) git_stream_close(git_stream *st)
 
 GIT_INLINE(void) git_stream_free(git_stream *st)
 {
+	if (!st)
+		return;
+
 	st->free(st);
 }
 
diff --git a/src/tls_stream.c b/src/tls_stream.c
index 39a8ce3..83e2d06 100644
--- a/src/tls_stream.c
+++ b/src/tls_stream.c
@@ -11,8 +11,21 @@
 #include "openssl_stream.h"
 #include "stransport_stream.h"
 
+static git_stream_cb tls_ctor;
+
+int git_stream_register_tls(git_stream_cb ctor)
+{
+	tls_ctor = ctor;
+
+	return 0;
+}
+
 int git_tls_stream_new(git_stream **out, const char *host, const char *port)
 {
+
+	if (tls_ctor)
+		return tls_ctor(out, host, port);
+
 #ifdef GIT_SECURE_TRANSPORT
 	return git_stransport_stream_new(out, host, port);
 #elif defined(GIT_OPENSSL)
diff --git a/tests/core/stream.c b/tests/core/stream.c
new file mode 100644
index 0000000..ace6a05
--- /dev/null
+++ b/tests/core/stream.c
@@ -0,0 +1,47 @@
+#include "clar_libgit2.h"
+#include "git2/sys/stream.h"
+#include "tls_stream.h"
+#include "stream.h"
+
+static git_stream test_stream;
+static int ctor_called;
+
+static int test_ctor(git_stream **out, const char *host, const char *port)
+{
+	GIT_UNUSED(host);
+	GIT_UNUSED(port);
+
+	ctor_called = 1;
+	*out = &test_stream;
+
+	return 0;
+}
+
+void test_core_stream__register_tls(void)
+{
+	git_stream *stream;
+	int error;
+
+	ctor_called = 0;
+	cl_git_pass(git_stream_register_tls(test_ctor));
+	cl_git_pass(git_tls_stream_new(&stream, "localhost", "443"));
+	cl_assert_equal_i(1, ctor_called);
+	cl_assert_equal_p(&test_stream, stream);
+
+	ctor_called = 0;
+	stream = NULL;
+	cl_git_pass(git_stream_register_tls(NULL));
+	error = git_tls_stream_new(&stream, "localhost", "443");
+
+	/* We don't have arbitrary TLS stream support on Windows */
+#if GIT_WIN32
+	cl_git_fail_with(-1, error);
+#else
+	cl_git_pass(error);
+#endif
+
+	cl_assert_equal_i(0, ctor_called);
+	cl_assert(&test_stream != stream);
+
+	git_stream_free(stream);
+}