"C. Michael Pilato" <cmpilato_at_collab.net> writes:
> Just a thought: Have you considered expanding the scope of the private
> resource space rather than using the magic prefix hack? You could add
> ".../!svn/vtxn/UUID" and ".../!svn/vtxr/UUID/..." to be alternate ways to
> address transactions and transaction roots (the "v" there being a shortcut
> for "virtual"). This is *effectively* the same approach as yours -- there's
> a different prefix here. But the prefix is a clearly defined piece of the
> protocol, not just some magic bit buried in mod_dav_svn's codebase.
This looks promising, it's quite straightforward to implement. New
log/patch follows:
Extend Subversion's v2 HTTP protocol to include URIs that allow the
client to define the transaction name visible in on the wire.
If the client sends, or a proxy injects, an SVN-VTxn-Name header with
the POST request it defines the transaction name to be returned to the
client in the POST response. If the client recieves the new
SVN-VTxn-Name header it uses that name in the new URIs in the requests
that make up the commit.
By default the client will not send the new header.
* subversion/mod_dav_svn/dav_svn.h
(struct dav_svn_root): Add vtxn_name member.
(dav_svn__get_vtxn_stub, dav_svn__get_vtxn_root_stub): New.
* subversion/mod_dav_svn/mod_dav_svn.c
(dav_svn__get_vtxn_stub, dav_svn__get_vtxn_root_stub): New.
* subversion/mod_dav_svn/posts/create_txn.c
(dav_svn__post_create_txn): Get vtxn_name from header, if sent, and
store vtxn_name:txn_name mapping in the activity database.
* subversion/mod_dav_svn/version.c
(get_option): Add vtxn stub and vtxn root stub headers.
(merge): Delete visible_txn_name:txn_name mapping after commit.
* subversion/mod_dav_svn/repos.c
(parse_vtxnstub_uri, parse_vtxnroot_uri): New.
(special_subdirs): Add vtxn and vtxr.
(remove_resource): Delete mapping when aborting the transaction.
* subversion/libsvn_ra_serf/ra_serf.h
(struct svn_ra_serf__session_t): Add vtxn_stub and vtxn_root_stub.
* subversion/libsvn_ra_serf/commit.c
(setup_post_headers): New.
(post_headers_iterator_callback): Handle vtxn name header.
(open_root): Set header_delegate.
* subversion/libsvn_ra_serf/options.c
(capabilities_headers_iterator_callback): Support vtxn stub and
vtxn root stub headers.
* subversion/libsvn_ra_neon/options.c
(parse_capabilities): Support vtxn stub and vtxn root stub headers.
* subversion/include/svn_dav.h
(SVN_DAV_VTXN_STUB_HEADER, SVN_DAV_VTXN_ROOT_STUB_HEADER,
SVN_DAV_VTXN_NAME_HEADER): New.
* notes/http-and-webdav/http-protocol-v2.txt: Update.
Index: notes/http-and-webdav/http-protocol-v2.txt
===================================================================
--- notes/http-and-webdav/http-protocol-v2.txt (revision 1077861)
+++ notes/http-and-webdav/http-protocol-v2.txt (working copy)
@@ -128,6 +128,14 @@
Various read- and write-type requests can be issued against
these resources (MKCOL, PUT, PROPFIND, PROPPATCH, GET, etc.).
+ - alternate transaction resource (!svn/vtxn/VTXN-NAME)
+ alternate transaction root resource (!svn/vtxr/VTXN-NAME/[PATH])
+
+ Alternative names for the transaction based on a virtual, or
+ visible, name supplied by the client when the transaction
+ was created. The client supplied name is optional, if not
+ supplied these resource names are not valid.
+
* Opening an RA session:
ra_serf will send an OPTIONS request when creating a new
@@ -220,12 +228,17 @@
- no more need to "discover" the activity URI; !svn/act/ is gone.
- - client no longer creates an activity UUID itself.
+ - client no longer needs to create an activity UUID itself.
- instead, POST returns the name of the transaction it created,
- which can then be appended to the transaction stub and
- transaction root stub as necessary.
+ as TXN-NAME, which can then be appended to the transaction
+ stub and transaction root stub as necessary.
+ - if the client does choose to supply a UUID with the POST
+ request then the POST returns that UUID as VTXN-NAME, and the
+ client then uses that with the alternate transaction stub and
+ transaction root stub in subsequent requests.
+
- Once the commit transaction is created, the client is free to
send write requests against transaction resources it constructs
itself. This eliminates the CHECKOUT requests, and also
Index: subversion/mod_dav_svn/mod_dav_svn.c
===================================================================
--- subversion/mod_dav_svn/mod_dav_svn.c (revision 1077861)
+++ subversion/mod_dav_svn/mod_dav_svn.c (working copy)
@@ -626,6 +626,22 @@
}
+const char *
+dav_svn__get_vtxn_stub(request_rec *r)
+{
+ return apr_pstrcat(r->pool, dav_svn__get_special_uri(r), "/vtxn",
+ (char *)NULL);
+}
+
+
+const char *
+dav_svn__get_vtxn_root_stub(request_rec *r)
+{
+ return apr_pstrcat(r->pool, dav_svn__get_special_uri(r), "/vtxr",
+ (char *)NULL);
+}
+
+
svn_boolean_t
dav_svn__get_autoversioning_flag(request_rec *r)
{
Index: subversion/mod_dav_svn/dav_svn.h
===================================================================
--- subversion/mod_dav_svn/dav_svn.h (revision 1077861)
+++ subversion/mod_dav_svn/dav_svn.h (working copy)
@@ -202,6 +202,15 @@
*/
const char *txn_name;
+ /* The optional vtxn name supplied by an HTTPv2 client and
+ used in subsequent requests. This may be NULL if the client
+ is not using a vtxn name.
+
+ PRIVATE resources that directly represent either a txn or
+ txn-root use this field.
+ */
+ const char *vtxn_name;
+
/* If the root is part of a transaction, this contains the FS's transaction
handle. It may be NULL if this root corresponds to a specific revision.
It may also be NULL if we have not opened the transaction yet.
@@ -392,7 +401,13 @@
/* For accessing transaction properties (typically "!svn/txr") */
const char *dav_svn__get_txn_root_stub(request_rec *r);
+/* For accessing transaction resources (typically "!svn/vtxn") */
+const char *dav_svn__get_vtxn_stub(request_rec *r);
+/* For accessing transaction properties (typically "!svn/vtxr") */
+const char *dav_svn__get_vtxn_root_stub(request_rec *r);
+
+
/*** activity.c ***/
/* Create a new transaction based on HEAD in REPOS, setting *PTXN_NAME
Index: subversion/mod_dav_svn/version.c
===================================================================
--- subversion/mod_dav_svn/version.c (revision 1077861)
+++ subversion/mod_dav_svn/version.c (working copy)
@@ -250,6 +250,12 @@
apr_table_set(r->headers_out, SVN_DAV_TXN_STUB_HEADER,
apr_pstrcat(resource->pool, repos_root_uri, "/",
dav_svn__get_txn_stub(r), (char *)NULL));
+ apr_table_set(r->headers_out, SVN_DAV_VTXN_ROOT_STUB_HEADER,
+ apr_pstrcat(resource->pool, repos_root_uri, "/",
+ dav_svn__get_vtxn_root_stub(r), (char *)NULL));
+ apr_table_set(r->headers_out, SVN_DAV_VTXN_STUB_HEADER,
+ apr_pstrcat(resource->pool, repos_root_uri, "/",
+ dav_svn__get_vtxn_stub(r), (char *)NULL));
}
return NULL;
@@ -1427,6 +1433,12 @@
svn_error_clear(serr);
serr = SVN_NO_ERROR;
}
+
+ /* HTTPv2 doesn't send DELETE after a successful MERGE so if
+ using the optional vtxn name mapping then delete it here. */
+ if (source->info->root.vtxn_name)
+ dav_svn__delete_activity(source->info->repos,
+ source->info->root.vtxn_name);
}
else
{
Index: subversion/mod_dav_svn/posts/create_txn.c
===================================================================
--- subversion/mod_dav_svn/posts/create_txn.c (revision 1077861)
+++ subversion/mod_dav_svn/posts/create_txn.c (working copy)
@@ -37,6 +37,7 @@
ap_filter_t *output)
{
const char *txn_name;
+ const char *vtxn_name;
dav_error *derr;
request_rec *r = resource->info->r;
@@ -47,7 +48,20 @@
/* Build a "201 Created" response with header that tells the
client our new transaction's name. */
- apr_table_set(r->headers_out, SVN_DAV_TXN_NAME_HEADER, txn_name);
+ vtxn_name = apr_table_get(r->headers_in, SVN_DAV_VTXN_NAME_HEADER);
+ if (vtxn_name && vtxn_name[0])
+ {
+ /* If the client supplied a vtxn name then store a mapping from
+ the client name to the FS transaction name in the activity
+ database. */
+ if ((derr = dav_svn__store_activity(resource->info->repos,
+ vtxn_name, txn_name)))
+ return derr;
+ apr_table_set(r->headers_out, SVN_DAV_VTXN_NAME_HEADER, vtxn_name);
+ }
+ else
+ apr_table_set(r->headers_out, SVN_DAV_TXN_NAME_HEADER, txn_name);
+
r->status = HTTP_CREATED;
return NULL;
Index: subversion/mod_dav_svn/repos.c
===================================================================
--- subversion/mod_dav_svn/repos.c (revision 1077861)
+++ subversion/mod_dav_svn/repos.c (working copy)
@@ -491,7 +491,25 @@
return FALSE;
}
+static int
+parse_vtxnstub_uri(dav_resource_combined *comb,
+ const char *path,
+ const char *label,
+ int use_checked_in)
+{
+ /* format: !svn/vtxn/TXN_NAME */
+ if (parse_txnstub_uri(comb, path, label, use_checked_in))
+ return TRUE;
+
+ comb->priv.root.vtxn_name = comb->priv.root.txn_name;
+ comb->priv.root.txn_name = dav_svn__get_txn(comb->priv.repos,
+ comb->priv.root.vtxn_name);
+
+ return FALSE;
+}
+
+
static int
parse_txnroot_uri(dav_resource_combined *comb,
const char *path,
@@ -541,7 +559,25 @@
return FALSE;
}
+static int
+parse_vtxnroot_uri(dav_resource_combined *comb,
+ const char *path,
+ const char *label,
+ int use_checked_in)
+{
+ /* format: !svn/vtxr/TXN_NAME/[PATH] */
+ if (parse_txnroot_uri(comb, path, label, use_checked_in))
+ return TRUE;
+
+ comb->priv.root.vtxn_name = comb->priv.root.txn_name;
+ comb->priv.root.txn_name = dav_svn__get_txn(comb->priv.repos,
+ comb->priv.root.vtxn_name);
+
+ return FALSE;
+}
+
+
static int
parse_wrk_baseline_uri(dav_resource_combined *comb,
const char *path,
@@ -621,6 +657,8 @@
{ "rvr", parse_revroot_uri, 1, TRUE, DAV_SVN_RESTYPE_REVROOT_COLLECTION },
{ "txn", parse_txnstub_uri, 1, FALSE, DAV_SVN_RESTYPE_TXN_COLLECTION},
{ "txr", parse_txnroot_uri, 1, TRUE, DAV_SVN_RESTYPE_TXNROOT_COLLECTION},
+ { "vtxn", parse_vtxnstub_uri, 1, FALSE, DAV_SVN_RESTYPE_TXN_COLLECTION},
+ { "vtxr", parse_vtxnroot_uri, 1, TRUE, DAV_SVN_RESTYPE_TXNROOT_COLLECTION},
{ NULL } /* sentinel */
};
@@ -3783,11 +3821,13 @@
if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
&& resource->info->restype == DAV_SVN_RESTYPE_TXN_COLLECTION)
{
- /* We'll assume that no activity was created to map to this
- transaction. */
- return dav_svn__abort_txn(resource->info->repos,
- resource->info->root.txn_name,
- resource->pool);
+ if (resource->info->root.vtxn_name)
+ return dav_svn__delete_activity(resource->info->repos,
+ resource->info->root.vtxn_name);
+ else
+ return dav_svn__abort_txn(resource->info->repos,
+ resource->info->root.txn_name,
+ resource->pool);
}
/* ### note that the parent was checked out at some point, and this
Index: subversion/include/svn_dav.h
===================================================================
--- subversion/include/svn_dav.h (revision 1077861)
+++ subversion/include/svn_dav.h (working copy)
@@ -133,6 +133,11 @@
* from a "txn root"). (HTTP protocol v2 only) */
#define SVN_DAV_TXN_STUB_HEADER "SVN-Txn-Stub"
+/** Companion to @c SVN_DAV_TXN_STUB_HEADER, used when a POST request
+ * returns @c SVN_DAV_VTXN_NAME_HEADER in response to a client
+ * supplied name. (HTTP protocol v2 only) */
+#define SVN_DAV_VTXN_STUB_HEADER "SVN-VTxn-Stub"
+
/** This header provides an opaque URI which represents the root
* directory of a Subversion transaction (revision-in-progress),
* similar to the concept of a "txn root" in the libsvn_fs API. The
@@ -141,6 +146,11 @@
* protocol v2 only) */
#define SVN_DAV_TXN_ROOT_STUB_HEADER "SVN-Txn-Root-Stub"
+/** Companion to @c SVN_DAV_TXN_ROOT_STUB_HEADER, used when a POST
+ * request returns @c SVN_DAV_VTXN_NAME_HEADER in response to a
+ * client supplied name. (HTTP protocol v2 only) */
+#define SVN_DAV_VTXN_ROOT_STUB_HEADER "SVN-VTxn-Root-Stub"
+
/** This header is used in the POST response to tell the client the
* name of the Subversion transaction created by the request. It can
* then be appended to the transaction stub and transaction root stub
@@ -148,6 +158,12 @@
* transaction. (HTTP protocol v2 only) */
#define SVN_DAV_TXN_NAME_HEADER "SVN-Txn-Name"
+/** This header is used in the POST request, to pass a client supplied
+ * alternative transaction name to the server, and in the the POST
+ * response, to tell the client that the alternative transaction
+ * resource names should be used. (HTTP protocol v2 only) */
+#define SVN_DAV_VTXN_NAME_HEADER "SVN-VTxn-Name"
+
/**
* @name Fulltext MD5 headers
*
Index: subversion/libsvn_ra_neon/ra_neon.h
===================================================================
--- subversion/libsvn_ra_neon/ra_neon.h (revision 1077861)
+++ subversion/libsvn_ra_neon/ra_neon.h (working copy)
@@ -146,6 +146,8 @@
const char *rev_root_stub; /* for accessing REV/PATH pairs */
const char *txn_stub; /* for accessing transactions (i.e. txnprops) */
const char *txn_root_stub; /* for accessing TXN/PATH pairs */
+ const char *vtxn_stub; /* for accessing transactions (i.e. txnprops) */
+ const char *vtxn_root_stub; /* for accessing TXN/PATH pairs */
/*** End HTTP v2 stuff ***/
Index: subversion/libsvn_ra_neon/options.c
===================================================================
--- subversion/libsvn_ra_neon/options.c (revision 1077861)
+++ subversion/libsvn_ra_neon/options.c (working copy)
@@ -253,6 +253,14 @@
{
ras->txn_stub = apr_pstrdup(ras->pool, val);
}
+ if ((val = ne_get_response_header(req, SVN_DAV_VTXN_ROOT_STUB_HEADER)))
+ {
+ ras->vtxn_root_stub = apr_pstrdup(ras->pool, val);
+ }
+ if ((val = ne_get_response_header(req, SVN_DAV_VTXN_STUB_HEADER)))
+ {
+ ras->vtxn_stub = apr_pstrdup(ras->pool, val);
+ }
}
Index: subversion/libsvn_ra_serf/ra_serf.h
===================================================================
--- subversion/libsvn_ra_serf/ra_serf.h (revision 1077861)
+++ subversion/libsvn_ra_serf/ra_serf.h (working copy)
@@ -195,6 +195,8 @@
const char *rev_root_stub; /* for accessing REV/PATH pairs */
const char *txn_stub; /* for accessing transactions (i.e. txnprops) */
const char *txn_root_stub; /* for accessing TXN/PATH pairs */
+ const char *vtxn_stub; /* for accessing transactions (i.e. txnprops) */
+ const char *vtxn_root_stub; /* for accessing TXN/PATH pairs */
/*** End HTTP v2 stuff ***/
Index: subversion/libsvn_ra_serf/commit.c
===================================================================
--- subversion/libsvn_ra_serf/commit.c (revision 1077861)
+++ subversion/libsvn_ra_serf/commit.c (working copy)
@@ -1141,6 +1141,21 @@
}
static svn_error_t *
+setup_post_headers(serf_bucket_t *headers,
+ void *baton,
+ apr_pool_t *pool)
+{
+#ifdef SVN_SERF_SEND_VTXN_NAME
+ /* Enable this to exercise the VTXN-NAME code based on a client
+ supplied transaction name. */
+ serf_bucket_headers_set(headers, SVN_DAV_VTXN_NAME_HEADER,
+ svn_uuid_generate(pool));
+#endif
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
setup_delete_headers(serf_bucket_t *headers,
void *baton,
apr_pool_t *pool)
@@ -1254,6 +1269,18 @@
prc_cc->txn_root_url =
svn_path_url_add_component2(sess->txn_root_stub, val, prc_cc->pool);
}
+
+ if (svn_cstring_casecmp(key, SVN_DAV_VTXN_NAME_HEADER) == 0)
+ {
+ /* Build out vtxn and vtxn-root URLs using the vtxn name we're
+ given, and store the whole lot of it in the commit context. */
+ prc_cc->txn_name = apr_pstrdup(prc_cc->pool, val);
+ prc_cc->txn_url =
+ svn_path_url_add_component2(sess->vtxn_stub, val, prc_cc->pool);
+ prc_cc->txn_root_url =
+ svn_path_url_add_component2(sess->vtxn_root_stub, val, prc_cc->pool);
+ }
+
return 0;
}
@@ -1308,6 +1335,8 @@
handler->body_type = SVN_SKEL_MIME_TYPE;
handler->body_delegate = create_txn_post_body;
handler->body_delegate_baton = NULL;
+ handler->header_delegate = setup_post_headers;
+ handler->header_delegate_baton = NULL;
handler->path = ctx->session->me_resource;
handler->conn = ctx->session->conns[0];
handler->session = ctx->session;
Index: subversion/libsvn_ra_serf/options.c
===================================================================
--- subversion/libsvn_ra_serf/options.c (revision 1077861)
+++ subversion/libsvn_ra_serf/options.c (working copy)
@@ -373,6 +373,14 @@
{
orc->session->txn_root_stub = apr_pstrdup(orc->session->pool, val);
}
+ else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_STUB_HEADER) == 0)
+ {
+ orc->session->vtxn_stub = apr_pstrdup(orc->session->pool, val);
+ }
+ else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_ROOT_STUB_HEADER) == 0)
+ {
+ orc->session->vtxn_root_stub = apr_pstrdup(orc->session->pool, val);
+ }
else if (svn_cstring_casecmp(key, SVN_DAV_REPOS_UUID_HEADER) == 0)
{
orc->session->uuid = apr_pstrdup(orc->session->pool, val);
--
Philip
Received on 2011-03-04 13:30:00 CET