You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2013/10/12 20:43:20 UTC
svn commit: r1531574 - in /subversion/trunk: CHANGES
subversion/include/svn_ra.h subversion/libsvn_ra_svn/client.c
subversion/libsvn_ra_svn/ra_svn.h subversion/tests/libsvn_ra/ra-test.c
Author: brane
Date: Sat Oct 12 18:43:20 2013
New Revision: 1531574
URL: http://svn.apache.org/r1531574
Log:
Added two new callbacks to the svn_ra_callbacks2_t table for opening
and closing ra_svn tunnels. When they're used, they override all
tunnel definitions in the config file, includint the built-in svn+ssh.
* subversion/include/svn_ra.h (struct svn_ra_svn_conn_st): Forward-declare
this struct that is otherwise typedef'd in svn_ra_svn.h.
(svn_ra_open_tunnel_func_t, svn_ra_close_tunnel_func_t):
New callback function typedefs.
(svn_ra_callbacks2_t.open_tunnel, svn_ra_callbacks2_t.close_tunnel):
New RA callbacks. Used only by ra_svn, and only if a tunnel is needed.
* subversion/libsvn_ra_svn/ra_svn.h (svn_ra_svn__session_baton_t):
New member tunnel_name.
* subversion/libsvn_ra_svn/client.c (tunnel_data_t): New private struct,
used as a pool-cleanup baton.
(close_tunnel_cleanup): New; pool cleanup for closing tunnels.
(open_session): New argument tunnel_name. Use the new callbacks
if they're defined.
(ra_svn_open): Do not call find_tunnel_agent if the open-tunnel
callback is defined. Update call to open_sesssion.
(ra_svn_reparent): Update call to open_sesssion.
* subversion/tests/libsvn_ra/ra-test.c
(make_and_open_local_repos): Use svn_ra_open4 instead of the
deprecated svn_ra_open3.
(tunnel_open_count, open_tunnel, close_tunnel): New.
(tunel_callback_test): New test for tunnel callbacks.
(test_funcs): Add tunel_callback_test.
Modified:
subversion/trunk/CHANGES
subversion/trunk/subversion/include/svn_ra.h
subversion/trunk/subversion/libsvn_ra_svn/client.c
subversion/trunk/subversion/libsvn_ra_svn/ra_svn.h
subversion/trunk/subversion/tests/libsvn_ra/ra-test.c
Modified: subversion/trunk/CHANGES
URL: http://svn.apache.org/viewvc/subversion/trunk/CHANGES?rev=1531574&r1=1531573&r2=1531574&view=diff
==============================================================================
--- subversion/trunk/CHANGES (original)
+++ subversion/trunk/CHANGES Sat Oct 12 18:43:20 2013
@@ -20,6 +20,8 @@ http://svn.apache.org/repos/asf/subversi
- General:
- API changes:
+ * New RA callbacks for managing ra_svn tunnels:
+ svn_ra_callbacks2_t::open_tunnel and svn_ra_callbacks2_t::close_tunnel.
- Bindings:
Modified: subversion/trunk/subversion/include/svn_ra.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_ra.h?rev=1531574&r1=1531573&r2=1531574&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_ra.h (original)
+++ subversion/trunk/subversion/include/svn_ra.h Sat Oct 12 18:43:20 2013
@@ -270,6 +270,52 @@ typedef svn_error_t *(*svn_ra_replay_rev
apr_hash_t *rev_props,
apr_pool_t *pool);
+
+/**
+ * Forward-declared ra_svn connection type.
+ * @see svn_ra_svn.h
+ */
+struct svn_ra_svn_conn_st;
+
+/**
+ * Callback function for opening a tunnel in ra_svn.
+ *
+ * Given the @a tunnel_name, tunnel @a user and server @a hostname and
+ * @a port, return a new ra_svn connection in @a conn. The returned
+ * connection must be allocated from @a pool.
+ *
+ * @a tunnel_baton will be passed on to the close-unnel callback.
+ *
+ * @a open_baton is the baton as originally passed to ra_open.
+ *
+ * @since New in 1.9.
+ */
+typedef svn_error_t *(*svn_ra_open_tunnel_func_t)(
+ struct svn_ra_svn_conn_st **conn,
+ void **tunnel_baton, void *open_baton,
+ const char *tunnel_name, const char *user,
+ const char *hostname, int port,
+ apr_pool_t *pool);
+
+/**
+ * Callback function for closing a tunnel in ra_svn.
+ *
+ * This function will be called when the pool that owns the tunnel
+ * connection is cleared or destroyed. It receives the @a baton that
+ * was created by the open-tunnel callback, and the same
+ * @a tunnel_name, @a user, @a hostname and @a port parameters.
+ *
+ * @a tunel_baton was returned by the open-tunnel callback.
+ *
+ * @a open_baton is the baton as originally passed to ra_open.
+ *
+ * @since New in 1.9.
+ */
+typedef svn_error_t *(*svn_ra_close_tunnel_func_t)(
+ void *tunnel_baton, void *open_baton,
+ const char *tunnel_name, const char *user,
+ const char *hostname, int port);
+
/**
* The update Reporter.
@@ -538,6 +584,24 @@ typedef struct svn_ra_callbacks2_t
*/
svn_ra_get_wc_contents_func_t get_wc_contents;
+ /** Open-tunnel callback
+ * If not @c null, this callback will be invoked to create an ra_svn
+ * connection that needs a tunnel, overriding any tunnel definitions
+ * in the client config file. This callback is used only for ra_svn
+ * and ignored by the other RA modules.
+ * @since New in 1.9.
+ */
+ svn_ra_open_tunnel_func_t open_tunnel;
+
+ /** Close-tunnel callback
+ * If not @c null, this callback will be invoked when the pool that
+ * owns the connection created by the open_tunnel callback is
+ * cleared or destroyed. This callback is used only for ra_svn and
+ * ignored by the other RA modules.
+ * @since New in 1.9.
+ */
+ svn_ra_close_tunnel_func_t close_tunnel;
+
} svn_ra_callbacks2_t;
/** Similar to svn_ra_callbacks2_t, except that the progress
Modified: subversion/trunk/subversion/libsvn_ra_svn/client.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_svn/client.c?rev=1531574&r1=1531573&r2=1531574&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_svn/client.c (original)
+++ subversion/trunk/subversion/libsvn_ra_svn/client.c Sat Oct 12 18:43:20 2013
@@ -561,14 +561,42 @@ static svn_error_t *parse_url(const char
return SVN_NO_ERROR;
}
+/* This structure is used as a baton for the pool cleanup function to
+ store tunnel parameters used by the close-tunnel callback. */
+struct tunnel_data_t {
+ void *tunnel_baton;
+ void *open_baton;
+ const char *tunnel_name;
+ const char *user;
+ const char *hostname;
+ int port;
+ svn_ra_close_tunnel_func_t close_tunnel;
+};
+
+/* Pool cleanup function that invokes the close-tunel callback. */
+static apr_status_t close_tunnel_cleanup(void *baton)
+{
+ const struct tunnel_data_t *const td = baton;
+ svn_error_t *const err =
+ svn_error_root_cause(td->close_tunnel(td->tunnel_baton, td->open_baton,
+ td->tunnel_name, td->user,
+ td->hostname, td->port));
+ const apr_status_t ret = (err ? err->apr_err : 0);
+ svn_error_clear(err);
+ return ret;
+}
+
/* Open a session to URL, returning it in *SESS_P, allocating it in POOL.
URI is a parsed version of URL. CALLBACKS and CALLBACKS_BATON
- are provided by the caller of ra_svn_open. If tunnel_argv is non-null,
- it points to a program argument list to use when invoking the tunnel agent.
+ are provided by the caller of ra_svn_open. If TUNNEL_NAME is not NULL,
+ it is the name of the tunnel type parsed from the URL scheme.
+ If TUNNEL_ARGV is not NULL, it points to a program argument list to use
+ when invoking the tunnel agent.
*/
static svn_error_t *open_session(svn_ra_svn__session_baton_t **sess_p,
const char *url,
const apr_uri_t *uri,
+ const char *tunnel_name,
const char **tunnel_argv,
const svn_ra_callbacks2_t *callbacks,
void *callbacks_baton,
@@ -583,19 +611,45 @@ static svn_error_t *open_session(svn_ra_
sess = apr_palloc(pool, sizeof(*sess));
sess->pool = pool;
- sess->is_tunneled = (tunnel_argv != NULL);
+ sess->is_tunneled = (tunnel_name != NULL);
sess->url = apr_pstrdup(pool, url);
sess->user = uri->user;
sess->hostname = uri->hostname;
sess->realm_prefix = apr_psprintf(pool, "<svn://%s:%d>", uri->hostname,
uri->port);
+ sess->tunnel_name = tunnel_name;
sess->tunnel_argv = tunnel_argv;
sess->callbacks = callbacks;
sess->callbacks_baton = callbacks_baton;
sess->bytes_read = sess->bytes_written = 0;
- if (tunnel_argv)
- SVN_ERR(make_tunnel(tunnel_argv, &conn, pool));
+ if (tunnel_name)
+ {
+ if (!callbacks->open_tunnel)
+ SVN_ERR(make_tunnel(tunnel_argv, &conn, pool));
+ else
+ {
+ void *tunnel_baton = NULL;
+ SVN_ERR(callbacks->open_tunnel(&conn, &tunnel_baton,
+ callbacks_baton,
+ tunnel_name, uri->user,
+ uri->hostname, uri->port,
+ pool));
+ if (callbacks->close_tunnel)
+ {
+ struct tunnel_data_t *const td = apr_palloc(pool, sizeof(*td));
+ td->tunnel_baton = tunnel_baton;
+ td->open_baton = callbacks_baton;
+ td->tunnel_name = apr_pstrdup(pool, tunnel_name);
+ td->user = apr_pstrdup(pool, uri->user);
+ td->hostname = apr_pstrdup(pool, uri->hostname);
+ td->port = uri->port;
+ td->close_tunnel = callbacks->close_tunnel;
+ apr_pool_cleanup_register(pool, td, close_tunnel_cleanup,
+ apr_pool_cleanup_null);
+ }
+ }
+ }
else
{
SVN_ERR(make_connection(uri->hostname, uri->port, &sock, pool));
@@ -738,7 +792,7 @@ static svn_error_t *ra_svn_open(svn_ra_s
parse_tunnel(url, &tunnel, pool);
- if (tunnel)
+ if (tunnel && !callbacks->open_tunnel)
SVN_ERR(find_tunnel_agent(tunnel, uri.hostinfo, &tunnel_argv, config,
pool));
else
@@ -755,7 +809,7 @@ static svn_error_t *ra_svn_open(svn_ra_s
/* We open the session in a subpool so we can get rid of it if we
reparent with a server that doesn't support reparenting. */
- SVN_ERR(open_session(&sess, url, &uri, tunnel_argv,
+ SVN_ERR(open_session(&sess, url, &uri, tunnel, tunnel_argv,
callbacks, callback_baton, sess_pool));
session->priv = sess;
@@ -791,7 +845,7 @@ static svn_error_t *ra_svn_reparent(svn_
sess_pool = svn_pool_create(ra_session->pool);
err = parse_url(url, &uri, sess_pool);
if (! err)
- err = open_session(&new_sess, url, &uri, sess->tunnel_argv,
+ err = open_session(&new_sess, url, &uri, sess->tunnel_name, sess->tunnel_argv,
sess->callbacks, sess->callbacks_baton, sess_pool);
/* We destroy the new session pool on error, since it is allocated in
the main session pool. */
Modified: subversion/trunk/subversion/libsvn_ra_svn/ra_svn.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_svn/ra_svn.h?rev=1531574&r1=1531573&r2=1531574&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_svn/ra_svn.h (original)
+++ subversion/trunk/subversion/libsvn_ra_svn/ra_svn.h Sat Oct 12 18:43:20 2013
@@ -127,6 +127,7 @@ struct svn_ra_svn__session_baton_t {
const char *user;
const char *hostname; /* The remote hostname. */
const char *realm_prefix;
+ const char *tunnel_name;
const char **tunnel_argv;
const svn_ra_callbacks2_t *callbacks;
void *callbacks_baton;
Modified: subversion/trunk/subversion/tests/libsvn_ra/ra-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_ra/ra-test.c?rev=1531574&r1=1531573&r2=1531574&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_ra/ra-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_ra/ra-test.c Sat Oct 12 18:43:20 2013
@@ -31,11 +31,17 @@
#include "svn_error.h"
#include "svn_delta.h"
#include "svn_ra.h"
+#include "svn_ra_svn.h"
+#include "svn_pools.h"
+#include "svn_cmdline.h"
+#include "svn_dirent_uri.h"
#include "../svn_test.h"
#include "../svn_test_fs.h"
#include "../../libsvn_ra_local/ra_local.h"
+static const char tunnel_repos_name[] = "test-repo-tunnel";
+
/*-------------------------------------------------------------------*/
/** Helper routines. **/
@@ -58,7 +64,7 @@ make_and_open_local_repos(svn_ra_session
SVN_ERR(svn_uri_get_file_url_from_dirent(&url, repos_name, pool));
- SVN_ERR(svn_ra_open3(session, url, NULL, cbtable, NULL, NULL, pool));
+ SVN_ERR(svn_ra_open4(session, NULL, url, NULL, cbtable, NULL, NULL, pool));
return SVN_NO_ERROR;
}
@@ -88,6 +94,77 @@ commit_changes(svn_ra_session_t *session
return SVN_NO_ERROR;
}
+static int tunnel_open_count;
+
+static svn_error_t *
+open_tunnel(svn_ra_svn_conn_t **conn, void **tunnel_baton,
+ void *callbacks_baton,
+ const char *tunnel_name, const char *user,
+ const char *hostname, int port,
+ apr_pool_t *pool)
+{
+ svn_node_kind_t kind;
+ apr_proc_t *proc;
+ apr_procattr_t *attr;
+ apr_status_t status;
+ const char *args[] = { "svnserve", "-t", "-r", ".", NULL };
+ const char *svnserve;
+
+ SVN_ERR(svn_dirent_get_absolute(&svnserve, "../../svnserve/svnserve", pool));
+#ifdef WIN32
+ svnserve = apr_pstrcat(pool, svnserve, ".exe", NULL);
+#endif
+ SVN_ERR(svn_io_check_path(svnserve, &kind, pool));
+ if (kind != svn_node_file)
+ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+ "Could not find svnserve at %s",
+ svn_dirent_local_style(svnserve, pool));
+
+ status = apr_procattr_create(&attr, pool);
+ if (status == APR_SUCCESS)
+ status = apr_procattr_io_set(attr, 1, 1, 0);
+ if (status == APR_SUCCESS)
+ status = apr_procattr_cmdtype_set(attr, APR_PROGRAM);
+ proc = apr_palloc(pool, sizeof(*proc));
+ if (status == APR_SUCCESS)
+ status = apr_proc_create(proc,
+ svn_dirent_local_style(svnserve, pool),
+ args, NULL, attr, pool);
+ if (status != APR_SUCCESS)
+ return svn_error_wrap_apr(status, "Could not run svnserve");
+#ifdef WIN32
+ apr_pool_note_subprocess(pool, proc, APR_KILL_NEVER);
+#else
+ apr_pool_note_subprocess(pool, proc, APR_KILL_ONLY_ONCE);
+#endif
+
+ /* APR pipe objects inherit by default. But we don't want the
+ * tunnel agent's pipes held open by future child processes
+ * (such as other ra_svn sessions), so turn that off. */
+ apr_file_inherit_unset(proc->in);
+ apr_file_inherit_unset(proc->out);
+
+ *conn = svn_ra_svn_create_conn3(NULL, proc->out, proc->in,
+ SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
+ 0, 0, pool);
+
+ *tunnel_baton = NULL;
+ ++tunnel_open_count;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+close_tunnel(void *tunnel_baton, void *callbacks_baton,
+ const char *tunnel_name, const char *user,
+ const char *hostname, int port)
+{
+ --tunnel_open_count;
+ return SVN_NO_ERROR;
+}
+
+
+
+
/*-------------------------------------------------------------------*/
/** The tests **/
@@ -151,6 +228,50 @@ location_segments_test(const svn_test_op
}
+/* Test ra_svn tunnel callbacks. */
+static svn_error_t *
+tunel_callback_test(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ apr_pool_t *connection_pool;
+ svn_repos_t *repos;
+ const char *url;
+ svn_ra_callbacks2_t *cbtable;
+ svn_ra_session_t *session;
+ svn_error_t *err;
+
+ SVN_ERR(svn_test__create_repos(&repos, tunnel_repos_name, opts, pool));
+
+ url = apr_pstrcat(pool, "svn+test://localhost/", tunnel_repos_name, NULL);
+ SVN_ERR(svn_ra_create_callbacks(&cbtable, pool));
+ cbtable->open_tunnel = open_tunnel;
+ cbtable->close_tunnel = close_tunnel;
+ SVN_ERR(svn_cmdline_create_auth_baton(&cbtable->auth_baton,
+ TRUE /* non_interactive */,
+ "jrandom", "rayjandom",
+ NULL,
+ TRUE /* no_auth_cache */,
+ FALSE /* trust_server_cert */,
+ NULL, NULL, NULL, pool));
+
+ tunnel_open_count = 0;
+ connection_pool = svn_pool_create(pool);
+ err = svn_ra_open4(&session, NULL, url, NULL, cbtable, NULL, NULL,
+ connection_pool);
+ if (err && err->apr_err == SVN_ERR_TEST_FAILED)
+ {
+ svn_handle_error2(err, stderr, FALSE, "svn_tests: ");
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ SVN_ERR(err);
+ SVN_TEST_ASSERT(tunnel_open_count > 0);
+ svn_pool_destroy(connection_pool);
+ SVN_TEST_ASSERT(tunnel_open_count == 0);
+ return SVN_NO_ERROR;
+}
+
+
/* The test table. */
struct svn_test_descriptor_t test_funcs[] =
@@ -158,5 +279,7 @@ struct svn_test_descriptor_t test_funcs[
SVN_TEST_NULL,
SVN_TEST_OPTS_PASS(location_segments_test,
"test svn_ra_get_location_segments"),
+ SVN_TEST_OPTS_PASS(tunel_callback_test,
+ "test ra_svn tunnel creation callbacks"),
SVN_TEST_NULL
};