You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2012/08/20 20:52:10 UTC

svn commit: r1375167 - in /subversion/trunk/subversion: libsvn_ra_serf/commit.c libsvn_ra_serf/options.c libsvn_ra_serf/ra_serf.h mod_dav_svn/activity.c mod_dav_svn/dav_svn.h mod_dav_svn/posts/create_txn.c mod_dav_svn/repos.c mod_dav_svn/version.c

Author: cmpilato
Date: Mon Aug 20 18:52:09 2012
New Revision: 1375167

URL: http://svn.apache.org/viewvc?rev=1375167&view=rev
Log:
Teach mod_dav_svn to handle a new POST type -- one that allows you to
create the transaction with a set of inline txnprops.  And then teach
libsvn_ra_serf to make use of that functionality when it's available,
thus eliminating the need for a subsequent PROPPATCH request.

* subversion/mod_dav_svn/dav_svn.h
  (dav_svn__posts_list): Add new "create-txn-with-props" POST type.
  (dav_svn__post_create_txn_with_props): New function.
  (dav_svn__create_txn): Add 'revprops' parameter.

* subversion/mod_dav_svn/repos.c
  (handle_post_request): Dispatch an incoming "create-txn-with-props"
    POST request to the appropriate handler.  While, make some other
    minor logic changes.

* subversion/mod_dav_svn/activity.c
  (dav_svn__create_txn): Add 'revprops' parameter and handling.

* subversion/mod_dav_svn/version.c
  (dav_svn__checkout, make_activity): Update call to dav_svn_create_txn().

* subversion/mod_dav_svn/posts/create_txn.c
  (dav_svn__post_create_txn): Update call to dav_svn_create_txn().
  (dav_svn__post_create_txn_with_props): New function.

* subversion/libsvn_ra_serf/ra_serf.h
  (svn_ra_serf__session_t): Add 'supported_posts' member.

* subversion/libsvn_ra_serf/options.c
  (capabilities_headers_iterator_callback): Look for new "supported
    POSTS" header, caching the parsed value thereof in the session
    baton.

* subversion/libsvn_ra_serf/commit.c
  (create_txn_post_body): Expect an optional revprop hash for the
    function baton.  If present, use the new "create-txn-with-props"
    POST type instead of the original "create-txn" one.
  (open_root): If the server has advertised support for getting
    revprops as part of the initial commit POST request, then pass the
    revprops to the POST mechanisms and don't send the standalone
    PROPPATCH followup request.

Modified:
    subversion/trunk/subversion/libsvn_ra_serf/commit.c
    subversion/trunk/subversion/libsvn_ra_serf/options.c
    subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h
    subversion/trunk/subversion/mod_dav_svn/activity.c
    subversion/trunk/subversion/mod_dav_svn/dav_svn.h
    subversion/trunk/subversion/mod_dav_svn/posts/create_txn.c
    subversion/trunk/subversion/mod_dav_svn/repos.c
    subversion/trunk/subversion/mod_dav_svn/version.c

Modified: subversion/trunk/subversion/libsvn_ra_serf/commit.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/commit.c?rev=1375167&r1=1375166&r2=1375167&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/commit.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/commit.c Mon Aug 20 18:52:09 2012
@@ -38,6 +38,7 @@
 #include "svn_private_config.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_fspath.h"
+#include "private/svn_skel.h"
 
 #include "ra_serf.h"
 #include "../libsvn_ra/ra_loader.h"
@@ -1108,7 +1109,26 @@ create_txn_post_body(serf_bucket_t **bod
                      serf_bucket_alloc_t *alloc,
                      apr_pool_t *pool)
 {
-  *body_bkt = SERF_BUCKET_SIMPLE_STRING("( create-txn )", alloc);
+  apr_hash_t *revprops = baton;
+  svn_skel_t *request_skel;
+  svn_stringbuf_t *skel_str;
+
+  request_skel = svn_skel__make_empty_list(pool);
+  if (revprops)
+    {
+      svn_skel_t *proplist_skel;
+
+      SVN_ERR(svn_skel__unparse_proplist(&proplist_skel, revprops, pool));
+      svn_skel__prepend(proplist_skel, request_skel);
+      svn_skel__prepend_str("create-txn-with-props", request_skel, pool);
+      skel_str = svn_skel__unparse(request_skel, pool);
+      *body_bkt = SERF_BUCKET_SIMPLE_STRING(skel_str->data, alloc);
+    }
+  else
+    {
+      *body_bkt = SERF_BUCKET_SIMPLE_STRING("( create-txn )", alloc);
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -1213,12 +1233,15 @@ open_root(void *edit_baton,
   proppatch_context_t *proppatch_ctx;
   dir_context_t *dir;
   apr_hash_index_t *hi;
-  const char *proppatch_target;
+  const char *proppatch_target = NULL;
 
   if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->session))
     {
       post_response_ctx_t *prc;
       const char *rel_path;
+      svn_boolean_t post_with_revprops =
+        apr_hash_get(ctx->session->supported_posts, "create-txn-with-props",
+                     APR_HASH_KEY_STRING) ? TRUE : FALSE;
 
       /* Create our activity URL now on the server. */
       handler = apr_pcalloc(ctx->pool, sizeof(*handler));
@@ -1226,7 +1249,8 @@ open_root(void *edit_baton,
       handler->method = "POST";
       handler->body_type = SVN_SKEL_MIME_TYPE;
       handler->body_delegate = create_txn_post_body;
-      handler->body_delegate_baton = NULL;
+      handler->body_delegate_baton =
+        post_with_revprops ? ctx->revprop_table : NULL;
       handler->header_delegate = setup_post_headers;
       handler->header_delegate_baton = NULL;
       handler->path = ctx->session->me_resource;
@@ -1288,7 +1312,9 @@ open_root(void *edit_baton,
       dir->removed_props = apr_hash_make(dir->pool);
       dir->url = apr_pstrdup(dir->pool, ctx->txn_root_url);
 
-      proppatch_target = ctx->txn_url;
+      /* If we included our revprops in the POST, we need not
+         PROPPATCH them. */
+      proppatch_target = post_with_revprops ? NULL : ctx->txn_url;
     }
   else
     {
@@ -1369,45 +1395,50 @@ open_root(void *edit_baton,
       proppatch_target = ctx->baseline_url;
     }
 
+  /* Unless this is NULL -- which means we don't need to PROPPATCH the
+     transaction with our revprops -- then, you know, PROPPATCH the
+     transaction with our revprops.  */
+  if (proppatch_target)
+    {
+      proppatch_ctx = apr_pcalloc(ctx->pool, sizeof(*proppatch_ctx));
+      proppatch_ctx->pool = dir_pool;
+      proppatch_ctx->commit = ctx;
+      proppatch_ctx->path = proppatch_target;
+      proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
+      proppatch_ctx->removed_props = apr_hash_make(proppatch_ctx->pool);
+      proppatch_ctx->base_revision = SVN_INVALID_REVNUM;
+
+      for (hi = apr_hash_first(ctx->pool, ctx->revprop_table); hi;
+           hi = apr_hash_next(hi))
+        {
+          const void *key;
+          void *val;
+          const char *name;
+          svn_string_t *value;
+          const char *ns;
+
+          apr_hash_this(hi, &key, NULL, &val);
+          name = key;
+          value = val;
 
-  /* PROPPATCH our revprops and pass them along.  */
-  proppatch_ctx = apr_pcalloc(ctx->pool, sizeof(*proppatch_ctx));
-  proppatch_ctx->pool = dir_pool;
-  proppatch_ctx->commit = ctx;
-  proppatch_ctx->path = proppatch_target;
-  proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
-  proppatch_ctx->removed_props = apr_hash_make(proppatch_ctx->pool);
-  proppatch_ctx->base_revision = SVN_INVALID_REVNUM;
-
-  for (hi = apr_hash_first(ctx->pool, ctx->revprop_table); hi;
-       hi = apr_hash_next(hi))
-    {
-      const void *key;
-      void *val;
-      const char *name;
-      svn_string_t *value;
-      const char *ns;
-
-      apr_hash_this(hi, &key, NULL, &val);
-      name = key;
-      value = val;
+          if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
+            {
+              ns = SVN_DAV_PROP_NS_SVN;
+              name += sizeof(SVN_PROP_PREFIX) - 1;
+            }
+          else
+            {
+              ns = SVN_DAV_PROP_NS_CUSTOM;
+            }
 
-      if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
-        {
-          ns = SVN_DAV_PROP_NS_SVN;
-          name += sizeof(SVN_PROP_PREFIX) - 1;
-        }
-      else
-        {
-          ns = SVN_DAV_PROP_NS_CUSTOM;
+          svn_ra_serf__set_prop(proppatch_ctx->changed_props,
+                                proppatch_ctx->path,
+                                ns, name, value, proppatch_ctx->pool);
         }
 
-      svn_ra_serf__set_prop(proppatch_ctx->changed_props, proppatch_ctx->path,
-                            ns, name, value, proppatch_ctx->pool);
+      SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, ctx->pool));
     }
 
-  SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, ctx->pool));
-
   *root_baton = dir;
 
   return SVN_NO_ERROR;

Modified: subversion/trunk/subversion/libsvn_ra_serf/options.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/options.c?rev=1375167&r1=1375166&r2=1375167&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/options.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/options.c Mon Aug 20 18:52:09 2012
@@ -204,6 +204,16 @@ capabilities_headers_iterator_callback(v
   /* SVN-specific headers -- if present, server supports HTTP protocol v2 */
   else if (strncmp(key, "SVN", 3) == 0)
     {
+      /* If we've not yet seen any information about supported POST
+         requests, we'll initialize the list/hash with "create-txn"
+         (which we know is supported by virtue of the server speaking
+         HTTPv2 at all. */
+      if (! session->supported_posts)
+        {
+          session->supported_posts = apr_hash_make(session->pool);
+          apr_hash_set(session->supported_posts, "create-txn", 10, (void *)1);
+        }
+
       if (svn_cstring_casecmp(key, SVN_DAV_ROOT_URI_HEADER) == 0)
         {
           session->repos_root = session->session_url;
@@ -257,6 +267,21 @@ capabilities_headers_iterator_callback(v
         {
           opt_ctx->youngest_rev = SVN_STR_TO_REV(val);
         }
+      else if (svn_cstring_casecmp(key, SVN_DAV_SUPPORTED_POSTS_HEADER) == 0)
+        {
+          /* May contain multiple values, separated by commas. */
+          int i;
+          apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE,
+                                                       opt_ctx->pool);
+
+          for (i = 0; i < vals->nelts; i++)
+            {
+              const char *post_val = APR_ARRAY_IDX(vals, i, const char *);
+
+              apr_hash_set(session->supported_posts, post_val,
+                           APR_HASH_KEY_STRING, (void *)1);
+            }
+        }
     }
 
   return 0;

Modified: subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h?rev=1375167&r1=1375166&r2=1375167&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h Mon Aug 20 18:52:09 2012
@@ -209,6 +209,10 @@ struct svn_ra_serf__session_t {
   const char *vtxn_stub;        /* for accessing transactions (i.e. txnprops) */
   const char *vtxn_root_stub;   /* for accessing TXN/PATH pairs */
 
+  /* Hash mapping const char * server-supported POST types to
+     disinteresting-but-non-null values. */
+  apr_hash_t *supported_posts;
+
   /*** End HTTP v2 stuff ***/
 
   svn_ra_serf__blncache_t *blncache;

Modified: subversion/trunk/subversion/mod_dav_svn/activity.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/activity.c?rev=1375167&r1=1375166&r2=1375167&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/activity.c (original)
+++ subversion/trunk/subversion/mod_dav_svn/activity.c Mon Aug 20 18:52:09 2012
@@ -240,16 +240,21 @@ dav_svn__store_activity(const dav_svn_re
 dav_error *
 dav_svn__create_txn(const dav_svn_repos *repos,
                     const char **ptxn_name,
+                    apr_hash_t *revprops,
                     apr_pool_t *pool)
 {
   svn_revnum_t rev;
   svn_fs_txn_t *txn;
   svn_error_t *serr;
-  apr_hash_t *revprop_table = apr_hash_make(pool);
+
+  if (! revprops)
+    {
+      revprops = apr_hash_make(pool);
+    }
 
   if (repos->username)
     {
-      apr_hash_set(revprop_table, SVN_PROP_REVISION_AUTHOR, APR_HASH_KEY_STRING,
+      apr_hash_set(revprops, SVN_PROP_REVISION_AUTHOR, APR_HASH_KEY_STRING,
                    svn_string_create(repos->username, pool));
     }
 
@@ -262,8 +267,7 @@ dav_svn__create_txn(const dav_svn_repos 
     }
 
   serr = svn_repos_fs_begin_txn_for_commit2(&txn, repos->repos, rev,
-                                            revprop_table,
-                                            repos->pool);
+                                            revprops, repos->pool);
   if (serr != NULL)
     {
       return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,

Modified: subversion/trunk/subversion/mod_dav_svn/dav_svn.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/dav_svn.h?rev=1375167&r1=1375166&r2=1375167&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/dav_svn.h (original)
+++ subversion/trunk/subversion/mod_dav_svn/dav_svn.h Mon Aug 20 18:52:09 2012
@@ -420,10 +420,17 @@ const char *dav_svn__get_vtxn_root_stub(
 /*** activity.c ***/
 
 /* Create a new transaction based on HEAD in REPOS, setting *PTXN_NAME
-   to the name of that transaction.  Use POOL for allocations. */
+   to the name of that transaction.  REVPROPS is an optional hash of
+   const char * property names and const svn_string_t * values which
+   will be set as transactions properties on the transaction this
+   function creates.  Use POOL for allocations.
+
+   NOTE:  This function will overwrite the svn:author property, if
+   any, found in REVPROPS.  */
 dav_error *
 dav_svn__create_txn(const dav_svn_repos *repos,
                     const char **ptxn_name,
+                    apr_hash_t *revprops,
                     apr_pool_t *pool);
 
 /* If it exists, abort the transaction named TXN_NAME from REPOS.  Use
@@ -670,18 +677,15 @@ dav_svn__get_deleted_rev_report(const da
 
 /*** posts/ ***/
 
-/* The list of Subversion's custom POSTs. */
-static const char * dav_svn__posts_list[] = {
-  "create-txn",
-  NULL
-};
-
-
 /* The various POST handlers, defined in posts/, and used by repos.c.  */
 dav_error *
 dav_svn__post_create_txn(const dav_resource *resource,
                          svn_skel_t *request_skel,
                          ap_filter_t *output);
+dav_error *
+dav_svn__post_create_txn_with_props(const dav_resource *resource,
+                                    svn_skel_t *request_skel,
+                                    ap_filter_t *output);
 
 /*** authz.c ***/
 

Modified: subversion/trunk/subversion/mod_dav_svn/posts/create_txn.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/posts/create_txn.c?rev=1375167&r1=1375166&r2=1375167&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/posts/create_txn.c (original)
+++ subversion/trunk/subversion/mod_dav_svn/posts/create_txn.c Mon Aug 20 18:52:09 2012
@@ -42,13 +42,64 @@ dav_svn__post_create_txn(const dav_resou
   request_rec *r = resource->info->r;
 
   /* Create a Subversion repository transaction based on HEAD. */
-  if ((derr = dav_svn__create_txn(resource->info->repos, &txn_name,
+  if ((derr = dav_svn__create_txn(resource->info->repos, &txn_name, NULL,
                                   resource->pool)))
     return derr;
 
   /* Build a "201 Created" response with header that tells the
      client our new transaction's name. */
-  vtxn_name =  apr_table_get(r->headers_in, SVN_DAV_VTXN_NAME_HEADER);
+  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;
+}
+
+
+/* Respond to a "create-txn-with-props" POST request.
+ *
+ * Syntax:  ( create-txn-with-props (PROPNAME PROPVAL [PROPNAME PROPVAL ...])
+ */
+dav_error *
+dav_svn__post_create_txn_with_props(const dav_resource *resource,
+                                    svn_skel_t *request_skel,
+                                    ap_filter_t *output)
+{
+  const char *txn_name;
+  const char *vtxn_name;
+  dav_error *derr;
+  svn_error_t *err;
+  request_rec *r = resource->info->r;
+  apr_hash_t *revprops;
+  svn_skel_t *proplist_skel = request_skel->children->next;
+
+  if ((err = svn_skel__parse_proplist(&revprops, proplist_skel,
+                                      resource->pool)))
+    {
+      return dav_svn__convert_err(err, HTTP_BAD_REQUEST,
+                                  "Malformatted request skel", resource->pool);
+    }
+  
+  /* Create a Subversion repository transaction based on HEAD. */
+  if ((derr = dav_svn__create_txn(resource->info->repos, &txn_name,
+                                  revprops, resource->pool)))
+    return derr;
+
+  /* Build a "201 Created" response with header that tells the
+     client our new transaction's 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

Modified: subversion/trunk/subversion/mod_dav_svn/repos.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/repos.c?rev=1375167&r1=1375166&r2=1375167&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/repos.c (original)
+++ subversion/trunk/subversion/mod_dav_svn/repos.c Mon Aug 20 18:52:09 2012
@@ -4444,7 +4444,7 @@ handle_post_request(request_rec *r,
                     dav_resource *resource,
                     ap_filter_t *output)
 {
-  svn_skel_t *request_skel;
+  svn_skel_t *request_skel, *post_skel;
   int status;
   apr_pool_t *pool = resource->pool;
 
@@ -4461,16 +4461,23 @@ handle_post_request(request_rec *r,
     return dav_svn__new_error(pool, HTTP_BAD_REQUEST, 0,
                               "Unable to identify skel POST request flavor.");
 
-  if (svn_skel__matches_atom(request_skel->children, "create-txn"))
+  post_skel = request_skel->children;
+
+  /* NOTE: If you add POST handlers here, you'll want to advertise
+     that the server supports them, too.  See version.c:get_option(). */
+
+  if (svn_skel__matches_atom(post_skel, "create-txn"))
     {
       return dav_svn__post_create_txn(resource, request_skel, output);
     }
-  else
+  else if (svn_skel__matches_atom(post_skel, "create-txn-with-props"))
     {
-      return dav_svn__new_error(pool, HTTP_BAD_REQUEST, 0,
-                                "Unsupported skel POST request flavor.");
+      return dav_svn__post_create_txn_with_props(resource,
+                                                 request_skel, output);
     }
-  /* NOTREACHED */
+
+  return dav_svn__new_error(pool, HTTP_BAD_REQUEST, 0,
+                            "Unsupported skel POST request flavor.");
 }
 
 int dav_svn__method_post(request_rec *r)

Modified: subversion/trunk/subversion/mod_dav_svn/version.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/version.c?rev=1375167&r1=1375166&r2=1375167&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/version.c (original)
+++ subversion/trunk/subversion/mod_dav_svn/version.c Mon Aug 20 18:52:09 2012
@@ -234,7 +234,15 @@ get_option(const dav_resource *resource,
      DeltaV-free!  If we're configured to advise this support, do so.  */
   if (resource->info->repos->v2_protocol)
     {
-      const char **this_post = dav_svn__posts_list;
+      /* The list of Subversion's custom POSTs.  You'll want to keep
+         this in sync with the handling of these suckers in
+         handle_post_request().  */
+      static const char * posts_list[] = {
+        "create-txn",
+        "create-txn-with-props",
+        NULL
+      };
+      const char **this_post = posts_list;
 
       apr_table_set(r->headers_out, SVN_DAV_ROOT_URI_HEADER, repos_root_uri);
       apr_table_set(r->headers_out, SVN_DAV_ME_RESOURCE_HEADER,
@@ -409,7 +417,7 @@ dav_svn__checkout(dav_resource *resource
           shared_activity = apr_pstrdup(resource->info->r->pool, uuid_buf);
 
           derr = dav_svn__create_txn(resource->info->repos, &shared_txn_name,
-                                     resource->info->r->pool);
+                                     NULL, resource->info->r->pool);
           if (derr) return derr;
 
           derr = dav_svn__store_activity(resource->info->repos,
@@ -1149,7 +1157,8 @@ make_activity(dav_resource *resource)
                                   SVN_DAV_ERROR_NAMESPACE,
                                   SVN_DAV_ERROR_TAG);
 
-  err = dav_svn__create_txn(resource->info->repos, &txn_name, resource->pool);
+  err = dav_svn__create_txn(resource->info->repos, &txn_name, 
+                            NULL, resource->pool);
   if (err != NULL)
     return err;