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/06/27 17:13:42 UTC

svn commit: r1354571 [16/37] - in /subversion/branches/master-passphrase: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/server-side/ notes/ notes/api-errata/1.8/ notes/directory-i...

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/property.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/property.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/property.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/property.c Wed Jun 27 15:12:37 2012
@@ -41,28 +41,23 @@
 
 /* Our current parsing state we're in for the PROPFIND response. */
 typedef enum prop_state_e {
-  NONE = 0,
+  INITIAL = 0,
+  MULTISTATUS,
   RESPONSE,
+  HREF,
+  PROPSTAT,
+  STATUS,
   PROP,
-  PROPVAL
+  PROPVAL,
+  COLLECTION,
+  HREF_VALUE
 } prop_state_e;
 
-typedef struct prop_info_t {
-  apr_pool_t *pool;
-
-  /* Current ns, attribute name, and value of the property we're parsing */
-  const char *ns;
-  const char *name;
-  svn_stringbuf_t *value;
-
-  const char *encoding;
-
-} prop_info_t;
 
 /*
  * This structure represents a pending PROPFIND response.
  */
-struct svn_ra_serf__propfind_context_t {
+typedef struct propfind_context_t {
   /* pool to issue allocations from */
   apr_pool_t *pool;
 
@@ -87,31 +82,273 @@ struct svn_ra_serf__propfind_context_t {
 
   /* hash table that will be updated with the properties
    *
-   * This can be shared between multiple svn_ra_serf__propfind_context_t
+   * This can be shared between multiple propfind_context_t
    * structures
    */
   apr_hash_t *ret_props;
 
-  /* If we're dealing with a Depth: 1 response,
-   * we may be dealing with multiple paths.
+  /* hash table containing all the properties associated with the
+   * "current" <propstat> tag.  These will get copied into RET_PROPS
+   * if the status code similarly associated indicates that they are
+   * "good"; otherwise, they'll get discarded.
    */
-  const char *current_path;
-
-  /* Returned status code. */
-  int status_code;
-
-  /* Are we done issuing the PROPFIND? */
-  svn_boolean_t done;
-
-  /* Context from XML stream */
-  svn_ra_serf__xml_parser_t *parser_ctx;
+  apr_hash_t *ps_props;
 
   /* If not-NULL, add us to this list when we're done. */
   svn_ra_serf__list_t **done_list;
 
   svn_ra_serf__list_t done_item;
+
+} propfind_context_t;
+
+
+#define D_ "DAV:"
+#define S_ SVN_XML_NAMESPACE
+static const svn_ra_serf__xml_transition_t propfind_ttable[] = {
+  { INITIAL, D_, "multistatus", MULTISTATUS,
+    FALSE, { NULL }, TRUE },
+
+  { MULTISTATUS, D_, "response", RESPONSE,
+    FALSE, { NULL }, FALSE },
+
+  { RESPONSE, D_, "href", HREF,
+    TRUE, { NULL }, TRUE },
+
+  { RESPONSE, D_, "propstat", PROPSTAT,
+    FALSE, { NULL }, TRUE },
+
+  { PROPSTAT, D_, "status", STATUS,
+    TRUE, { NULL }, TRUE },
+
+  { PROPSTAT, D_, "prop", PROP,
+    FALSE, { NULL }, FALSE },
+
+  { PROP, "*", "*", PROPVAL,
+    TRUE, { "?V:encoding", NULL }, TRUE },
+
+  { PROPVAL, D_, "collection", COLLECTION,
+    FALSE, { NULL }, TRUE },
+
+  { PROPVAL, D_, "href", HREF_VALUE,
+    TRUE, { NULL }, TRUE },
+
+  { 0 }
 };
 
+
+/* Return the HTTP status code contained in STATUS_LINE, or 0 if
+   there's a problem parsing it. */
+static int parse_status_code(const char *status_line)
+{
+  /* STATUS_LINE should be of form: "HTTP/1.1 200 OK" */
+  if (status_line[0] == 'H' &&
+      status_line[1] == 'T' &&
+      status_line[2] == 'T' &&
+      status_line[3] == 'P' &&
+      status_line[4] == '/' &&
+      (status_line[5] >= '0' && status_line[5] <= '9') &&
+      status_line[6] == '.' &&
+      (status_line[7] >= '0' && status_line[7] <= '9') &&
+      status_line[8] == ' ')
+    {
+      char *reason;
+
+      return apr_strtoi64(status_line + 8, &reason, 10);
+    }
+  return 0;
+}
+
+
+/* Conforms to svn_ra_serf__path_rev_walker_t  */
+static svn_error_t *
+copy_into_ret_props(void *baton,
+                    const char *path, apr_ssize_t path_len,
+                    const char *ns, apr_ssize_t ns_len,
+                    const char *name, apr_ssize_t name_len,
+                    const svn_string_t *val,
+                    apr_pool_t *pool)
+{
+  propfind_context_t *ctx = baton;
+
+  svn_ra_serf__set_ver_prop(ctx->ret_props, path, ctx->rev, ns, name,
+                            val, ctx->pool);
+  return SVN_NO_ERROR;
+}
+
+
+/* Conforms to svn_ra_serf__xml_opened_t  */
+static svn_error_t *
+propfind_opened(svn_ra_serf__xml_estate_t *xes,
+                void *baton,
+                int entered_state,
+                const svn_ra_serf__dav_props_t *tag,
+                apr_pool_t *scratch_pool)
+{
+  propfind_context_t *ctx = baton;
+
+  if (entered_state == PROPVAL)
+    {
+      svn_ra_serf__xml_note(xes, PROPVAL, "ns", tag->namespace);
+      svn_ra_serf__xml_note(xes, PROPVAL, "name", tag->name);
+    }
+  else if (entered_state == PROPSTAT)
+    {
+      ctx->ps_props = apr_hash_make(ctx->pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Conforms to svn_ra_serf__xml_closed_t  */
+static svn_error_t *
+propfind_closed(svn_ra_serf__xml_estate_t *xes,
+                void *baton,
+                int leaving_state,
+                const svn_string_t *cdata,
+                apr_hash_t *attrs,
+                apr_pool_t *scratch_pool)
+{
+  propfind_context_t *ctx = baton;
+
+  if (leaving_state == MULTISTATUS)
+    {
+      /* We've gathered all the data from the reponse. Add this item
+         onto the "done list". External callers will then know this
+         request has been completed (tho stray response bytes may still
+         arrive).  */
+      if (ctx->done_list)
+        {
+          ctx->done_item.data = ctx->handler;
+          ctx->done_item.next = *ctx->done_list;
+          *ctx->done_list = &ctx->done_item;
+        }
+    }
+  else if (leaving_state == HREF)
+    {
+      const char *path;
+      const svn_string_t *val_str;
+
+      if (strcmp(ctx->depth, "1") == 0)
+        path = svn_urlpath__canonicalize(cdata->data, scratch_pool);
+      else
+        path = ctx->path;
+
+      svn_ra_serf__xml_note(xes, RESPONSE, "path", path);
+
+      /* Copy the value into the right pool, then save the HREF.  */
+      val_str = svn_string_dup(cdata, ctx->pool);
+      svn_ra_serf__set_ver_prop(ctx->ret_props,
+                                path, ctx->rev, D_, "href", val_str,
+                                ctx->pool);
+    }
+  else if (leaving_state == COLLECTION)
+    {
+      svn_ra_serf__xml_note(xes, PROPVAL, "altvalue", "collection");
+    }
+  else if (leaving_state == HREF_VALUE)
+    {
+      svn_ra_serf__xml_note(xes, PROPVAL, "altvalue", cdata->data);
+    }
+  else if (leaving_state == STATUS)
+    {
+      /* Parse the status field, and remember if this is a property
+         that we wish to ignore.  (Typically, if it's not a 200, the
+         status will be 404 to indicate that a property we
+         specifically requested from the server doesn't exist.)  */
+      int status = parse_status_code(cdata->data);
+      if (status != 200)
+        svn_ra_serf__xml_note(xes, PROPSTAT, "ignore-prop", "*");
+    }
+  else if (leaving_state == PROPVAL)
+    {
+      const char *encoding = apr_hash_get(attrs, "V:encoding",
+                                          APR_HASH_KEY_STRING);
+      const svn_string_t *val_str;
+      apr_hash_t *gathered;
+      const char *path;
+      const char *ns;
+      const char *name;
+      const char *altvalue;
+
+      if (encoding)
+        {
+          if (strcmp(encoding, "base64") != 0)
+            return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
+                                     NULL,
+                                     _("Got unrecognized encoding '%s'"),
+                                     encoding);
+
+          /* Decode into the right pool.  */
+          val_str = svn_base64_decode_string(cdata, ctx->pool);
+        }
+      else
+        {
+          /* Copy into the right pool.  */
+          val_str = svn_string_dup(cdata, ctx->pool);
+        }
+
+      /* The current path sits on the RESPONSE state. Gather up all the
+         state from this PROPVAL to the (grandparent) RESPONSE state,
+         and grab the path from there.
+
+         Now, it would be nice if we could, at this point, know that
+         the status code for this property indicated a problem -- then
+         we could simply bail out here and ignore the property.
+         Sadly, though, we might get the status code *after* we get
+         the property value.  So we'll carry on with our processing
+         here, setting the property and value as expected.  Once we
+         know for sure the status code associate with the property,
+         we'll decide its fate.  */
+      gathered = svn_ra_serf__xml_gather_since(xes, RESPONSE);
+
+      /* These will be dup'd into CTX->POOL, as necessary.  */
+      path = apr_hash_get(gathered, "path", APR_HASH_KEY_STRING);
+      if (path == NULL)
+        path = ctx->path;
+
+      ns = apr_hash_get(attrs, "ns", APR_HASH_KEY_STRING);
+      name = apr_pstrdup(ctx->pool,
+                         apr_hash_get(attrs, "name", APR_HASH_KEY_STRING));
+
+      altvalue = apr_hash_get(attrs, "altvalue", APR_HASH_KEY_STRING);
+      if (altvalue != NULL)
+        val_str = svn_string_create(altvalue, ctx->pool);
+
+      svn_ra_serf__set_ver_prop(ctx->ps_props,
+                                path, ctx->rev, ns, name, val_str,
+                                ctx->pool);
+    }
+  else
+    {
+      apr_hash_t *gathered;
+      const char *path;
+
+      SVN_ERR_ASSERT(leaving_state == PROPSTAT);
+
+      gathered = svn_ra_serf__xml_gather_since(xes, PROPSTAT);
+
+      path = apr_hash_get(gathered, "path", APR_HASH_KEY_STRING);
+      if (path == NULL)
+        path = ctx->path;
+
+      /* If we've squirreled away a note that says we want to ignore
+         these properties, we'll do so.  Otherwise, we need to copy
+         them from the temporary hash into the ctx->ret_props hash. */
+      if (! apr_hash_get(gathered, "ignore-prop", APR_HASH_KEY_STRING))
+        {
+          SVN_ERR(svn_ra_serf__walk_all_paths(ctx->ps_props, ctx->rev,
+                                              copy_into_ret_props, ctx,
+                                              scratch_pool));
+        }
+
+      ctx->ps_props = NULL;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
 const svn_string_t *
 svn_ra_serf__get_ver_prop_string(apr_hash_t *props,
                                  const char *path,
@@ -228,197 +465,15 @@ svn_ra_serf__set_prop(apr_hash_t *props,
                             val, pool);
 }
 
-static prop_info_t *
-push_state(svn_ra_serf__xml_parser_t *parser,
-           svn_ra_serf__propfind_context_t *propfind,
-           prop_state_e state)
-{
-  svn_ra_serf__xml_push_state(parser, state);
-
-  if (state == PROPVAL)
-    {
-      prop_info_t *info;
-
-      info = apr_pcalloc(parser->state->pool, sizeof(*info));
-      info->pool = parser->state->pool;
-      info->value = svn_stringbuf_create_empty(info->pool);
-
-      parser->state->private = info;
-    }
-
-  return parser->state->private;
-}
-
-/*
- * Expat callback invoked on a start element tag for a PROPFIND response.
- */
-static svn_error_t *
-start_propfind(svn_ra_serf__xml_parser_t *parser,
-               svn_ra_serf__dav_props_t name,
-               const char **attrs,
-               apr_pool_t *scratch_pool)
-{
-  svn_ra_serf__propfind_context_t *ctx = parser->user_data;
-  prop_state_e state;
-  prop_info_t *info;
-
-  state = parser->state->current_state;
-
-  if (state == NONE && strcmp(name.name, "response") == 0)
-    {
-      svn_ra_serf__xml_push_state(parser, RESPONSE);
-    }
-  else if (state == RESPONSE && strcmp(name.name, "href") == 0)
-    {
-      info = push_state(parser, ctx, PROPVAL);
-      info->ns = name.namespace;
-      info->name = "href";
-    }
-  else if (state == RESPONSE && strcmp(name.name, "prop") == 0)
-    {
-      push_state(parser, ctx, PROP);
-    }
-  else if (state == PROP)
-    {
-      info = push_state(parser, ctx, PROPVAL);
-      info->ns = name.namespace;
-      info->name = apr_pstrdup(info->pool, name.name);
-      info->encoding = apr_pstrdup(info->pool,
-                                   svn_xml_get_attr_value("V:encoding", attrs));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on an end element tag for a PROPFIND response.
- */
-static svn_error_t *
-end_propfind(svn_ra_serf__xml_parser_t *parser,
-             svn_ra_serf__dav_props_t name,
-             apr_pool_t *scratch_pool)
-{
-  svn_ra_serf__propfind_context_t *ctx = parser->user_data;
-  prop_state_e state;
-  prop_info_t *info;
-
-  state = parser->state->current_state;
-  info = parser->state->private;
-
-  if (state == RESPONSE && strcmp(name.name, "response") == 0)
-    {
-      svn_ra_serf__xml_pop_state(parser);
-    }
-  else if (state == PROP && strcmp(name.name, "prop") == 0)
-    {
-      svn_ra_serf__xml_pop_state(parser);
-    }
-  else if (state == PROPVAL)
-    {
-      const char *ns;
-      const char *pname;
-      const svn_string_t *val_str = NULL;
-
-      /* if we didn't see a CDATA element, we may want the tag name
-       * as long as it isn't equivalent to the property name.
-       */
-      /* ### gstein sez: I have no idea what this is about.  */
-      if (*info->value->data == '\0')
-        {
-          if (strcmp(info->name, name.name) != 0)
-            val_str = svn_string_create(name.name, ctx->pool);
-          else
-            val_str = svn_string_create_empty(ctx->pool);
-        }
-
-      if (parser->state->prev->current_state == RESPONSE &&
-          strcmp(name.name, "href") == 0)
-        {
-          if (strcmp(ctx->depth, "1") == 0)
-            {
-              ctx->current_path =
-                svn_urlpath__canonicalize(info->value->data, ctx->pool);
-            }
-          else
-            {
-              ctx->current_path = ctx->path;
-            }
-        }
-      else if (info->encoding)
-        {
-          if (strcmp(info->encoding, "base64") == 0)
-            {
-              const svn_string_t *morph;
-
-              morph = svn_stringbuf__morph_into_string(info->value);
-#ifdef SVN_DEBUG
-              info->value = NULL;  /* morph killed the stringbuf.  */
-#endif
-              val_str = svn_base64_decode_string(morph, ctx->pool);
-            }
-          else
-            {
-              return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
-                                       NULL,
-                                       _("Got unrecognized encoding '%s'"),
-                                       info->encoding);
-            }
-        }
-
-      /* ### there may be better logic to ensure this is set above, but just
-         ### going for the easy win here.  */
-      if (val_str == NULL)
-        val_str = svn_string_create_from_buf(info->value, ctx->pool);
-
-      ns = apr_pstrdup(ctx->pool, info->ns);
-      pname = apr_pstrdup(ctx->pool, info->name);
-
-      /* set the return props and update our cache too. */
-      svn_ra_serf__set_ver_prop(ctx->ret_props,
-                                ctx->current_path, ctx->rev,
-                                ns, pname, val_str,
-                                ctx->pool);
-
-      svn_ra_serf__xml_pop_state(parser);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on CDATA elements in a PROPFIND response.
- *
- * This callback can be called multiple times.
- */
-static svn_error_t *
-cdata_propfind(svn_ra_serf__xml_parser_t *parser,
-               const char *data,
-               apr_size_t len,
-               apr_pool_t *scratch_pool)
-{
-  svn_ra_serf__propfind_context_t *ctx = parser->user_data;
-  prop_state_e state;
-  prop_info_t *info;
-
-  UNUSED_CTX(ctx);
-
-  state = parser->state->current_state;
-  info = parser->state->private;
-
-  if (state == PROPVAL)
-    svn_stringbuf_appendbytes(info->value, data, len);
-
-  return SVN_NO_ERROR;
-}
 
 static svn_error_t *
 setup_propfind_headers(serf_bucket_t *headers,
                         void *setup_baton,
                         apr_pool_t *pool)
 {
-  svn_ra_serf__propfind_context_t *ctx = setup_baton;
+  propfind_context_t *ctx = setup_baton;
 
-  if (ctx->conn->using_compression)
+  if (ctx->sess->using_compression)
     {
       serf_bucket_headers_setn(headers, "Accept-Encoding", "gzip");
     }
@@ -440,7 +495,7 @@ create_propfind_body(serf_bucket_t **bkt
                      serf_bucket_alloc_t *alloc,
                      apr_pool_t *pool)
 {
-  svn_ra_serf__propfind_context_t *ctx = setup_baton;
+  propfind_context_t *ctx = setup_baton;
 
   serf_bucket_t *body_bkt, *tmp;
   const svn_ra_serf__dav_props_t *prop;
@@ -513,7 +568,7 @@ create_propfind_body(serf_bucket_t **bkt
 
 
 svn_error_t *
-svn_ra_serf__deliver_props(svn_ra_serf__propfind_context_t **prop_ctx,
+svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler,
                            apr_hash_t *ret_props,
                            svn_ra_serf__session_t *sess,
                            svn_ra_serf__connection_t *conn,
@@ -524,9 +579,9 @@ svn_ra_serf__deliver_props(svn_ra_serf__
                            svn_ra_serf__list_t **done_list,
                            apr_pool_t *pool)
 {
-  svn_ra_serf__propfind_context_t *new_prop_ctx;
+  propfind_context_t *new_prop_ctx;
   svn_ra_serf__handler_t *handler;
-  svn_ra_serf__xml_parser_t *parser_ctx;
+  svn_ra_serf__xml_context_t *xmlctx;
 
   new_prop_ctx = apr_pcalloc(pool, sizeof(*new_prop_ctx));
 
@@ -535,7 +590,6 @@ svn_ra_serf__deliver_props(svn_ra_serf__
   new_prop_ctx->find_props = find_props;
   new_prop_ctx->ret_props = ret_props;
   new_prop_ctx->depth = depth;
-  new_prop_ctx->done = FALSE;
   new_prop_ctx->sess = sess;
   new_prop_ctx->conn = conn;
   new_prop_ctx->rev = rev;
@@ -550,7 +604,13 @@ svn_ra_serf__deliver_props(svn_ra_serf__
       new_prop_ctx->label = NULL;
     }
 
-  handler = apr_pcalloc(pool, sizeof(*handler));
+  xmlctx = svn_ra_serf__xml_context_create(propfind_ttable,
+                                           propfind_opened,
+                                           propfind_closed,
+                                           NULL,
+                                           new_prop_ctx,
+                                           pool);
+  handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
 
   handler->method = "PROPFIND";
   handler->path = path;
@@ -565,57 +625,28 @@ svn_ra_serf__deliver_props(svn_ra_serf__
 
   new_prop_ctx->handler = handler;
 
-  parser_ctx = apr_pcalloc(pool, sizeof(*new_prop_ctx->parser_ctx));
-  parser_ctx->pool = pool;
-  parser_ctx->user_data = new_prop_ctx;
-  parser_ctx->start = start_propfind;
-  parser_ctx->end = end_propfind;
-  parser_ctx->cdata = cdata_propfind;
-  parser_ctx->status_code = &new_prop_ctx->status_code;
-  parser_ctx->done = &new_prop_ctx->done;
-  parser_ctx->done_list = new_prop_ctx->done_list;
-  parser_ctx->done_item = &new_prop_ctx->done_item;
-
-  new_prop_ctx->parser_ctx = parser_ctx;
-
-  handler->response_handler = svn_ra_serf__handle_xml_parser;
-  handler->response_baton = parser_ctx;
-
-  /* create request */
-  svn_ra_serf__request_create(new_prop_ctx->handler);
-
-  *prop_ctx = new_prop_ctx;
+  *propfind_handler = handler;
 
   return SVN_NO_ERROR;
 }
 
-svn_boolean_t
-svn_ra_serf__propfind_is_done(svn_ra_serf__propfind_context_t *ctx)
-{
-  return ctx->done;
-}
-
-int
-svn_ra_serf__propfind_status_code(svn_ra_serf__propfind_context_t *ctx)
-{
-  return ctx->status_code;
-}
 
 /*
  * This helper function will block until the PROP_CTX indicates that is done
  * or another error is returned.
  */
 svn_error_t *
-svn_ra_serf__wait_for_props(svn_ra_serf__propfind_context_t *prop_ctx,
-                            svn_ra_serf__session_t *sess,
-                            apr_pool_t *pool)
+svn_ra_serf__wait_for_props(svn_ra_serf__handler_t *handler,
+                            apr_pool_t *scratch_pool)
 {
-  svn_error_t *err, *err2;
+  svn_error_t *err;
+  svn_error_t *err2;
 
-  err = svn_ra_serf__context_run_wait(&prop_ctx->done, sess, pool);
+  err = svn_ra_serf__context_run_one(handler, scratch_pool);
 
-  err2 = svn_ra_serf__error_on_status(prop_ctx->status_code,
-                                      prop_ctx->path, NULL);
+  err2 = svn_ra_serf__error_on_status(handler->sline.code,
+                                      handler->path,
+                                      NULL);
   if (err2)
     {
       svn_error_clear(err);
@@ -639,45 +670,63 @@ svn_ra_serf__retrieve_props(apr_hash_t *
                             apr_pool_t *result_pool,
                             apr_pool_t *scratch_pool)
 {
-  svn_ra_serf__propfind_context_t *prop_ctx;
+  svn_ra_serf__handler_t *handler;
 
   *results = apr_hash_make(result_pool);
 
-  SVN_ERR(svn_ra_serf__deliver_props(&prop_ctx, *results, sess, conn, url,
+  SVN_ERR(svn_ra_serf__deliver_props(&handler, *results, sess, conn, url,
                                      rev, depth, props, NULL, result_pool));
-  SVN_ERR(svn_ra_serf__wait_for_props(prop_ctx, sess, result_pool));
+  SVN_ERR(svn_ra_serf__wait_for_props(handler, scratch_pool));
 
   return SVN_NO_ERROR;
 }
 
 
 svn_error_t *
-svn_ra_serf__walk_all_props(apr_hash_t *props,
-                            const char *name,
-                            svn_revnum_t rev,
-                            svn_ra_serf__walker_visitor_t walker,
-                            void *baton,
-                            apr_pool_t *scratch_pool)
+svn_ra_serf__fetch_node_props(apr_hash_t **results,
+                              svn_ra_serf__connection_t *conn,
+                              const char *url,
+                              svn_revnum_t revision,
+                              const svn_ra_serf__dav_props_t *which_props,
+                              apr_pool_t *result_pool,
+                              apr_pool_t *scratch_pool)
 {
-  apr_hash_index_t *ns_hi;
+  apr_hash_t *multiprops;
   apr_hash_t *ver_props;
-  apr_hash_t *path_props;
-  apr_pool_t *iterpool;
 
-  ver_props = apr_hash_get(props, &rev, sizeof(rev));
-  if (!ver_props)
-    {
-      return SVN_NO_ERROR;
-    }
+  /* Note: a couple extra hash tables and whatnot get into RESULT_POOL.
+     Not a big deal at this point. Theoretically, we could fetch all
+     props into SCRATCH_POOL, then copy just the REVISION/URL props
+     into RESULT_POOL. Too much work for too little gain...  */
+  SVN_ERR(svn_ra_serf__retrieve_props(&multiprops, conn->session, conn,
+                                      url, revision, "0", which_props,
+                                      result_pool, scratch_pool));
+
+  ver_props = apr_hash_get(multiprops, &revision, sizeof(revision));
+  if (ver_props != NULL)
+    {
+      *results = apr_hash_get(ver_props, url, APR_HASH_KEY_STRING);
+      if (*results != NULL)
+        return SVN_NO_ERROR;
+    }
+
+  return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
+                          _("The PROPFIND response did not include "
+                            "the requested properties"));
+}
 
-  path_props = apr_hash_get(ver_props, name, APR_HASH_KEY_STRING);
-  if (!path_props)
-    {
-      return SVN_NO_ERROR;
-    }
+
+svn_error_t *
+svn_ra_serf__walk_node_props(apr_hash_t *props,
+                             svn_ra_serf__walker_visitor_t walker,
+                             void *baton,
+                             apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool;
+  apr_hash_index_t *ns_hi;
 
   iterpool = svn_pool_create(scratch_pool);
-  for (ns_hi = apr_hash_first(scratch_pool, path_props); ns_hi;
+  for (ns_hi = apr_hash_first(scratch_pool, props); ns_hi;
        ns_hi = apr_hash_next(ns_hi))
     {
       void *ns_val;
@@ -711,6 +760,31 @@ svn_ra_serf__walk_all_props(apr_hash_t *
 
 
 svn_error_t *
+svn_ra_serf__walk_all_props(apr_hash_t *props,
+                            const char *name,
+                            svn_revnum_t rev,
+                            svn_ra_serf__walker_visitor_t walker,
+                            void *baton,
+                            apr_pool_t *scratch_pool)
+{
+  apr_hash_t *ver_props;
+  apr_hash_t *path_props;
+
+  ver_props = apr_hash_get(props, &rev, sizeof(rev));
+  if (!ver_props)
+    return SVN_NO_ERROR;
+
+  path_props = apr_hash_get(ver_props, name, APR_HASH_KEY_STRING);
+  if (!path_props)
+    return SVN_NO_ERROR;
+
+  return svn_error_trace(svn_ra_serf__walk_node_props(path_props,
+                                                      walker, baton,
+                                                      scratch_pool));
+}
+
+
+svn_error_t *
 svn_ra_serf__walk_all_paths(apr_hash_t *props,
                             svn_revnum_t rev,
                             svn_ra_serf__path_rev_walker_t walker,
@@ -832,15 +906,13 @@ set_flat_props(void *baton,
 svn_error_t *
 svn_ra_serf__flatten_props(apr_hash_t **flat_props,
                            apr_hash_t *props,
-                           const char *path,
-                           svn_revnum_t revision,
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool)
 {
   *flat_props = apr_hash_make(result_pool);
 
-  return svn_error_trace(svn_ra_serf__walk_all_props(
-                            props, path, revision,
+  return svn_error_trace(svn_ra_serf__walk_node_props(
+                            props,
                             set_flat_props,
                             *flat_props /* baton */,
                             scratch_pool));
@@ -899,7 +971,7 @@ svn_ra_serf__select_revprops(apr_hash_t 
 
 
 /*
- * Contact the server (using SESSION and CONN) to calculate baseline
+ * Contact the server (using CONN) to calculate baseline
  * information for BASELINE_URL at REVISION (which may be
  * SVN_INVALID_REVNUM to query the HEAD revision).
  *
@@ -911,209 +983,295 @@ svn_ra_serf__select_revprops(apr_hash_t 
 static svn_error_t *
 retrieve_baseline_info(svn_revnum_t *actual_revision,
                        const char **basecoll_url_p,
-                       svn_ra_serf__session_t *session,
                        svn_ra_serf__connection_t *conn,
                        const char *baseline_url,
                        svn_revnum_t revision,
-                       apr_pool_t *pool)
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool)
 {
   apr_hash_t *props;
+  apr_hash_t *dav_props;
   const char *basecoll_url;
-  const char *version_name;
-
-  SVN_ERR(svn_ra_serf__retrieve_props(&props, session, conn,
-                                      baseline_url, revision, "0",
-                                      baseline_props,
-                                      pool, pool));
 
-  basecoll_url = svn_ra_serf__get_ver_prop(props, baseline_url, revision,
-                                           "DAV:", "baseline-collection");
+  SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn,
+                                        baseline_url, revision,
+                                        baseline_props,
+                                        scratch_pool, scratch_pool));
+  dav_props = apr_hash_get(props, "DAV:", 4);
+  /* If DAV_PROPS is NULL, then svn_prop_get_value() will return NULL.  */
 
+  basecoll_url = svn_prop_get_value(dav_props, "baseline-collection");
   if (!basecoll_url)
     {
       return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
                               _("The PROPFIND response did not include "
                                 "the requested baseline-collection value"));
     }
-
-  *basecoll_url_p = svn_urlpath__canonicalize(basecoll_url, pool);
-
-  version_name = svn_ra_serf__get_ver_prop(props, baseline_url, revision,
-                                           "DAV:", SVN_DAV__VERSION_NAME);
-  if (!version_name)
-    {
-      return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
-                              _("The PROPFIND response did not include "
-                                "the requested version-name value"));
-    }
+  *basecoll_url_p = svn_urlpath__canonicalize(basecoll_url, result_pool);
 
   if (actual_revision)
     {
+      const char *version_name;
+
+      version_name = svn_prop_get_value(dav_props, SVN_DAV__VERSION_NAME);
+      if (!version_name)
+        return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
+                                _("The PROPFIND response did not include "
+                                  "the requested version-name value"));
+
       *actual_revision = SVN_STR_TO_REV(version_name);
     }
 
   return SVN_NO_ERROR;
 }
 
+
+/* For HTTPv1 servers, do a PROPFIND dance on the VCC to fetch the youngest
+   revnum. If BASECOLL_URL is non-NULL, then the corresponding baseline
+   collection URL is also returned.
+
+   Do the work over CONN.
+
+   *BASECOLL_URL (if requested) will be allocated in RESULT_POOL. All
+   temporary allocations will be made in SCRATCH_POOL.  */
+static svn_error_t *
+v1_get_youngest_revnum(svn_revnum_t *youngest,
+                       const char **basecoll_url,
+                       svn_ra_serf__connection_t *conn,
+                       const char *vcc_url,
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool)
+{
+  const char *baseline_url;
+  const char *bc_url;
+
+  /* Fetching DAV:checked-in from the VCC (with no Label: to specify a
+     revision) will return the latest Baseline resource's URL.  */
+  SVN_ERR(svn_ra_serf__fetch_dav_prop(&baseline_url, conn, vcc_url,
+                                      SVN_INVALID_REVNUM,
+                                      "checked-in",
+                                      scratch_pool, scratch_pool));
+  if (!baseline_url)
+    {
+      return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
+                              _("The OPTIONS response did not include "
+                                "the requested checked-in value"));
+    }
+  baseline_url = svn_urlpath__canonicalize(baseline_url, scratch_pool);
+
+  /* From the Baseline resource, we can fetch the DAV:baseline-collection
+     and DAV:version-name properties. The latter is the revision number,
+     which is formally the name used in Label: headers.  */
+
+  /* First check baseline information cache. */
+  SVN_ERR(svn_ra_serf__blncache_get_baseline_info(&bc_url,
+                                                  youngest,
+                                                  conn->session->blncache,
+                                                  baseline_url,
+                                                  scratch_pool));
+  if (!bc_url)
+    {
+      SVN_ERR(retrieve_baseline_info(youngest, &bc_url, conn,
+                                     baseline_url, SVN_INVALID_REVNUM,
+                                     scratch_pool, scratch_pool));
+      SVN_ERR(svn_ra_serf__blncache_set(conn->session->blncache,
+                                        baseline_url, *youngest,
+                                        bc_url, scratch_pool));
+    }
+
+  if (basecoll_url != NULL)
+    *basecoll_url = apr_pstrdup(result_pool, bc_url);
+
+  return SVN_NO_ERROR;
+}
+
+
 svn_error_t *
-svn_ra_serf__get_baseline_info(const char **bc_url,
-                               const char **bc_relative,
-                               svn_ra_serf__session_t *session,
-                               svn_ra_serf__connection_t *conn,
-                               const char *url,
-                               svn_revnum_t revision,
-                               svn_revnum_t *latest_revnum,
-                               apr_pool_t *pool)
+svn_ra_serf__get_youngest_revnum(svn_revnum_t *youngest,
+                                 svn_ra_serf__session_t *session,
+                                 apr_pool_t *scratch_pool)
 {
-  const char *vcc_url, *relative_url, *basecoll_url, *baseline_url;
+  const char *vcc_url;
 
-  /* No URL?  No sweat.  We'll use the session URL. */
-  if (! url)
-    url = session->session_url.path;
+  if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
+    return svn_error_trace(svn_ra_serf__v2_get_youngest_revnum(
+                             youngest, session->conns[0], scratch_pool));
 
-  /* If the caller didn't provide a specific connection for us to use,
-     we'll use the default one.  */
-  if (! conn)
-    conn = session->conns[0];
+  SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, scratch_pool));
+
+  return svn_error_trace(v1_get_youngest_revnum(youngest, NULL,
+                                                session->conns[0], vcc_url,
+                                                scratch_pool, scratch_pool));
+}
 
+
+/* Set *BC_URL to the baseline collection url for REVISION. If REVISION
+   is SVN_INVALID_REVNUM, then the youngest revnum ("HEAD") is used.
+
+   *REVNUM_USED will be set to the revision used.
+
+   Uses the specified CONN, which is part of SESSION.
+
+   All allocations (results and temporary) are performed in POOL.  */
+static svn_error_t *
+get_baseline_info(const char **bc_url,
+                  svn_revnum_t *revnum_used,
+                  svn_ra_serf__session_t *session,
+                  svn_ra_serf__connection_t *conn,
+                  svn_revnum_t revision,
+                  apr_pool_t *pool)
+{
   /* If we detected HTTP v2 support on the server, we can construct
      the baseline collection URL ourselves, and fetch the latest
      revision (if needed) with an OPTIONS request.  */
   if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
     {
-      svn_revnum_t actual_revision;
-
       if (SVN_IS_VALID_REVNUM(revision))
         {
-          actual_revision = revision;
+          *revnum_used = revision;
         }
       else
         {
-          svn_ra_serf__options_context_t *opt_ctx;
-
-          SVN_ERR(svn_ra_serf__create_options_req(&opt_ctx, session, conn,
-                                                  session->session_url.path,
-                                                  pool));
-          SVN_ERR(svn_ra_serf__context_run_wait(
-                svn_ra_serf__get_options_done_ptr(opt_ctx), session, pool));
-
-          actual_revision = svn_ra_serf__options_get_youngest_rev(opt_ctx);
-          if (! SVN_IS_VALID_REVNUM(actual_revision))
+          SVN_ERR(svn_ra_serf__v2_get_youngest_revnum(
+                    revnum_used, conn, pool));
+          if (! SVN_IS_VALID_REVNUM(*revnum_used))
             return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
                                     _("The OPTIONS response did not include "
                                       "the youngest revision"));
         }
 
-      basecoll_url = apr_psprintf(pool, "%s/%ld",
-                                  session->rev_root_stub, actual_revision);
-      if (latest_revnum)
-        *latest_revnum = actual_revision;
+      *bc_url = apr_psprintf(pool, "%s/%ld",
+                             session->rev_root_stub, *revnum_used);
     }
 
   /* Otherwise, we fall back to the old VCC_URL PROPFIND hunt.  */
   else
     {
+      const char *vcc_url;
+
       SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, conn, pool));
 
       if (SVN_IS_VALID_REVNUM(revision))
         {
           /* First check baseline information cache. */
-          SVN_ERR(svn_ra_serf__blncache_get_bc_url(&basecoll_url,
+          SVN_ERR(svn_ra_serf__blncache_get_bc_url(bc_url,
                                                    session->blncache,
                                                    revision, pool));
-
-          if (!basecoll_url)
+          if (!*bc_url)
             {
-              SVN_ERR(retrieve_baseline_info(NULL, &basecoll_url, session,
-                                             conn, vcc_url, revision, pool));
+              SVN_ERR(retrieve_baseline_info(NULL, bc_url, conn,
+                                             vcc_url, revision, pool, pool));
               SVN_ERR(svn_ra_serf__blncache_set(session->blncache, NULL,
-                                                revision, basecoll_url, pool));
+                                                revision, *bc_url, pool));
             }
 
-          if (latest_revnum)
-            {
-              *latest_revnum = revision;
-            }
+          *revnum_used = revision;
         }
       else
         {
-          apr_hash_t *props;
-          svn_revnum_t actual_revision;
+          SVN_ERR(v1_get_youngest_revnum(revnum_used, bc_url,
+                                         conn, vcc_url,
+                                         pool, pool));
+        }
+    }
 
-          SVN_ERR(svn_ra_serf__retrieve_props(&props, session, conn,
-                                              vcc_url, revision, "0",
-                                              checked_in_props,
-                                              pool, pool));
-          baseline_url = svn_ra_serf__get_ver_prop(props, vcc_url, revision,
-                                                   "DAV:", "checked-in");
-          if (!baseline_url)
-            {
-              return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
-                                      _("The OPTIONS response did not include "
-                                        "the requested checked-in value"));
-            }
+  return SVN_NO_ERROR;
+}
 
-          baseline_url = svn_urlpath__canonicalize(baseline_url, pool);
 
-          /* First check baseline information cache. */
-          SVN_ERR(svn_ra_serf__blncache_get_baseline_info(&basecoll_url,
-                                                          &actual_revision,
-                                                          session->blncache,
-                                                          baseline_url,
-                                                          pool));
-          if (!basecoll_url)
-            {
-              SVN_ERR(retrieve_baseline_info(&actual_revision, &basecoll_url,
-                                             session, conn,
-                                             baseline_url, revision, pool));
-              SVN_ERR(svn_ra_serf__blncache_set(session->blncache,
-                                                baseline_url, actual_revision,
-                                                basecoll_url, pool));
-            }
+svn_error_t *
+svn_ra_serf__get_stable_url(const char **stable_url,
+                            svn_revnum_t *latest_revnum,
+                            svn_ra_serf__session_t *session,
+                            svn_ra_serf__connection_t *conn,
+                            const char *url,
+                            svn_revnum_t revision,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool)
+{
+  const char *basecoll_url;
+  const char *repos_relpath;
+  svn_revnum_t revnum_used;
 
-          if (latest_revnum)
-            {
-              *latest_revnum = actual_revision;
-            }
-        }
-    }
+  /* No URL? No sweat. We'll use the session URL.  */
+  if (! url)
+    url = session->session_url.path;
 
-  /* And let's not forget to calculate our relative path. */
-  SVN_ERR(svn_ra_serf__get_relative_path(&relative_url, url, session,
-                                         conn, pool));
+  /* If the caller didn't provide a specific connection for us to use,
+     we'll use the default connection.  */
+  if (! conn)
+    conn = session->conns[0];
+
+  SVN_ERR(get_baseline_info(&basecoll_url, &revnum_used,
+                            session, conn, revision, scratch_pool));
+  SVN_ERR(svn_ra_serf__get_relative_path(&repos_relpath, url,
+                                         session, conn, scratch_pool));
+
+  *stable_url = svn_path_url_add_component2(basecoll_url, repos_relpath,
+                                            result_pool);
+  if (latest_revnum)
+    *latest_revnum = revnum_used;
 
-  *bc_url = basecoll_url;
-  *bc_relative = relative_url;
   return SVN_NO_ERROR;
 }
 
 
 svn_error_t *
-svn_ra_serf__get_resource_type(svn_node_kind_t *kind,
-                               apr_hash_t *props,
-                               const char *url,
-                               svn_revnum_t revision)
+svn_ra_serf__get_resource_type(svn_kind_t *kind,
+                               apr_hash_t *props)
 {
+  apr_hash_t *dav_props;
   const char *res_type;
 
-  res_type = svn_ra_serf__get_ver_prop(props, url, revision,
-                                       "DAV:", "resourcetype");
+  dav_props = apr_hash_get(props, "DAV:", 4);
+  res_type = svn_prop_get_value(dav_props, "resourcetype");
   if (!res_type)
     {
       /* How did this happen? */
       return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
                               _("The PROPFIND response did not include the "
-                              "requested resourcetype value"));
+                                "requested resourcetype value"));
     }
 
   if (strcmp(res_type, "collection") == 0)
     {
-      *kind = svn_node_dir;
+      *kind = svn_kind_dir;
     }
   else
     {
-      *kind = svn_node_file;
+      *kind = svn_kind_file;
     }
 
   return SVN_NO_ERROR;
 }
+
+
+svn_error_t *
+svn_ra_serf__fetch_dav_prop(const char **value,
+                            svn_ra_serf__connection_t *conn,
+                            const char *url,
+                            svn_revnum_t revision,
+                            const char *propname,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool)
+{
+  apr_hash_t *props;
+  apr_hash_t *dav_props;
+
+  SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn, url, revision,
+                                        checked_in_props,
+                                        scratch_pool, scratch_pool));
+  dav_props = apr_hash_get(props, "DAV:", 4);
+  if (dav_props == NULL)
+    return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
+                            _("The PROPFIND response did not include "
+                              "the requested 'DAV:' properties"));
+
+  /* We wouldn't get here if the resource was not found (404), so the
+     property should be present.
+
+     Note: it is okay to call apr_pstrdup() with NULL.  */
+  *value = apr_pstrdup(result_pool, svn_prop_get_value(dav_props, propname));
+
+  return SVN_NO_ERROR;
+}

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h Wed Jun 27 15:12:37 2012
@@ -26,7 +26,7 @@
 
 
 #include <serf.h>
-#include <expat.h>
+#include <expat.h>  /* for XML_Parser  */
 #include <apr_uri.h>
 
 #include "svn_types.h"
@@ -71,22 +71,11 @@ typedef struct svn_ra_serf__connection_t
   /* Our connection to a server. */
   serf_connection_t *conn;
 
-  /* The server is not Apache/mod_dav_svn (directly) and only supports
-     HTTP/1.0. Thus, we cannot send chunked requests.  */
-  svn_boolean_t http10;
-
   /* Bucket allocator for this connection. */
   serf_bucket_alloc_t *bkt_alloc;
 
-  /* Host name */
-  const char *hostname;
-
-  /* Are we using ssl */
-  svn_boolean_t using_ssl;
-  int server_cert_failures; /* Collected cert failures in chain */
-
-  /* Should we ask for compressed responses? */
-  svn_boolean_t using_compression;
+  /* Collected cert failures in chain.  */
+  int server_cert_failures;
 
   /* What was the last HTTP status code we got on this connection? */
   int last_status_code;
@@ -98,11 +87,11 @@ typedef struct svn_ra_serf__connection_t
 
   svn_ra_serf__session_t *session;
 
-  /* user agent string */
-  const char *useragent;
-
 } svn_ra_serf__connection_t;
 
+/** Max. number of connctions we'll open to the server. */
+#define MAX_NR_OF_CONNS 4
+
 /*
  * The master serf RA session.
  *
@@ -121,8 +110,11 @@ struct svn_ra_serf__session_t {
   /* Should we ask for compressed responses? */
   svn_boolean_t using_compression;
 
+  /* The user agent string */
+  const char *useragent;
+
   /* The current connection */
-  svn_ra_serf__connection_t **conns;
+  svn_ra_serf__connection_t *conns[MAX_NR_OF_CONNS];
   int num_conns;
   int cur_conn;
 
@@ -134,6 +126,10 @@ struct svn_ra_serf__session_t {
   apr_uri_t repos_root;
   const char *repos_root_str;
 
+  /* The server is not Apache/mod_dav_svn (directly) and only supports
+     HTTP/1.0. Thus, we cannot send chunked requests.  */
+  svn_boolean_t http10;
+
   /* Our Version-Controlled-Configuration; may be NULL until we know it. */
   const char *vcc_url;
 
@@ -186,7 +182,7 @@ struct svn_ra_serf__session_t {
   const char *uuid;
 
   /* Connection timeout value */
-  long timeout;
+  apr_short_interval_time_t timeout;
 
   /* HTTPv1 flags */
   svn_tristate_t supports_deadprop_count;
@@ -280,40 +276,21 @@ static const svn_ra_serf__dav_props_t al
   { NULL }
 };
 
-static const svn_ra_serf__dav_props_t vcc_props[] =
-{
-  { "DAV:", "version-controlled-configuration" },
-  { NULL }
-};
-
 static const svn_ra_serf__dav_props_t check_path_props[] =
 {
   { "DAV:", "resourcetype" },
   { NULL }
 };
 
-static const svn_ra_serf__dav_props_t uuid_props[] =
-{
-  { SVN_DAV_PROP_NS_DAV, "repository-uuid" },
-  { NULL }
-};
-
-static const svn_ra_serf__dav_props_t repos_root_props[] =
-{
-  { SVN_DAV_PROP_NS_DAV, "baseline-relative-path" },
-  { NULL }
-};
-
-static const svn_ra_serf__dav_props_t href_props[] =
+static const svn_ra_serf__dav_props_t type_and_checksum_props[] =
 {
-  { "DAV:", "href" },
+  { "DAV:", "resourcetype" },
+  { SVN_DAV_PROP_NS_DAV, "sha1-checksum" },
   { NULL }
 };
 
 /* WC props compatibility with ra_neon. */
-#define SVN_RA_SERF__WC_NAMESPACE SVN_PROP_WC_PREFIX "ra_dav:"
-#define SVN_RA_SERF__WC_ACTIVITY_URL SVN_RA_SERF__WC_NAMESPACE "activity-url"
-#define SVN_RA_SERF__WC_CHECKED_IN_URL SVN_RA_SERF__WC_NAMESPACE "version-url"
+#define SVN_RA_SERF__WC_CHECKED_IN_URL SVN_PROP_WC_PREFIX "ra_dav:version-url"
 
 /** Serf utility functions **/
 
@@ -364,7 +341,7 @@ typedef svn_error_t *
 (*svn_ra_serf__response_handler_t)(serf_request_t *request,
                                    serf_bucket_t *response,
                                    void *handler_baton,
-                                   apr_pool_t *pool);
+                                   apr_pool_t *scratch_pool);
 
 /* Callback for when a request body is needed. */
 /* ### should pass a scratch_pool  */
@@ -388,6 +365,9 @@ typedef svn_error_t *
                                  int status_code,
                                  void *baton);
 
+/* ### we should reorder the types in this file.  */
+typedef struct svn_ra_serf__server_error_t svn_ra_serf__server_error_t;
+
 /*
  * Structure that can be passed to our default handler to guide the
  * execution of the request through its lifecycle.
@@ -402,10 +382,24 @@ typedef struct svn_ra_serf__handler_t {
   /* The content-type of the request body. */
   const char *body_type;
 
+  /* Has the request/response been completed?  */
+  svn_boolean_t done;
+
+  /* If we captured an error from the server, then this will be non-NULL.
+     It will be allocated from HANDLER_POOL.  */
+  svn_ra_serf__server_error_t *server_error;
+
   /* The handler and baton pair for our handler. */
   svn_ra_serf__response_handler_t response_handler;
   void *response_baton;
 
+  /* When REPONSE_HANDLER is invoked, the following fields will be set
+     based on the response header. HANDLER_POOL must be non-NULL for these
+     values to be filled in. SLINE.REASON and LOCATION will be allocated
+     within HANDLER_POOL.  */
+  serf_status_line sline;  /* The parsed Status-Line  */
+  const char *location;  /* The Location: header, if any  */
+
   /* The handler and baton pair to be executed when a non-recoverable error
    * is detected.  If it is NULL in the presence of an error, an abort() may
    * be triggered.
@@ -437,8 +431,34 @@ typedef struct svn_ra_serf__handler_t {
   /* The connection and session to be used for this request. */
   svn_ra_serf__connection_t *conn;
   svn_ra_serf__session_t *session;
+
+  /* Internal flag to indicate we've parsed the headers.  */
+  svn_boolean_t reading_body;
+
+  /* When this flag will be set, the core handler will discard any unread
+     portion of the response body. The registered response handler will
+     no longer be called.  */
+  svn_boolean_t discard_body;
+
+  /* Pool for allocating SLINE.REASON and LOCATION. If this pool is NULL,
+     then the requestor does not care about SLINE and LOCATION.  */
+  apr_pool_t *handler_pool;
+
 } svn_ra_serf__handler_t;
 
+
+/* Run one request and process the response.
+
+   Similar to context_run_wait(), but this creates the request for HANDLER
+   and then waits for it to complete.
+
+   WARNING: context_run_wait() does NOT create a request, whereas this
+   function DOES. Avoid a double-create.  */
+svn_error_t *
+svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler,
+                             apr_pool_t *scratch_pool);
+
+
 /*
  * Helper function to queue a request in the @a handler's connection.
  */
@@ -532,16 +552,6 @@ struct svn_ra_serf__xml_parser_t {
   /* Our previously used states (will be reused). */
   svn_ra_serf__xml_state_t *free_state;
 
-  /* If non-NULL, the status code of the response will be stored here.
-   *
-   * If this is NULL and an error is received, an abort will be triggered.
-   */
-  int *status_code;
-
-  /* If non-NULL, this is the value of the response's Location header.
-   */
-  const char *location;
-
   /* If non-NULL, this value will be set to TRUE when the response is
    * completed.
    */
@@ -601,19 +611,217 @@ struct svn_ra_serf__xml_parser_t {
   apr_off_t read_size; /* Number of bytes read from response */
 };
 
+
+/* v2 of the XML parsing functions  */
+
+/* The XML parsing context.  */
+typedef struct svn_ra_serf__xml_context_t svn_ra_serf__xml_context_t;
+
+
+/* An opaque structure for the XML parse element/state.  */
+typedef struct svn_ra_serf__xml_estate_t svn_ra_serf__xml_estate_t;
+
+/* Called just after the parser moves into ENTERED_STATE. The tag causing
+   the transition is passed in TAG.
+
+   This callback is applied to a parsing context by using the
+   svn_ra_serf__xml_context_customize() function.
+
+   NOTE: this callback, when set, will be invoked on *every* transition.
+   The callback must examine ENTERED_STATE to determine if any action
+   must be taken. The original state is not provided, but must be derived
+   from ENTERED_STATE and/or the TAG causing the transition (if needed).  */
+typedef svn_error_t *
+(*svn_ra_serf__xml_opened_t)(svn_ra_serf__xml_estate_t *xes,
+                             void *baton,
+                             int entered_state,
+                             const svn_ra_serf__dav_props_t *tag,
+                             apr_pool_t *scratch_pool);
+
+
+/* Called just before the parser leaves LEAVING_STATE.
+
+   If cdata collection was enabled for this state, then CDATA will be
+   non-NULL and contain the collected cdata.
+
+   If attribute collection was enabled for this state, then ATTRS will
+   contain the attributes collected for this element only, along with
+   any values stored via svn_ra_serf__xml_note().
+
+   Use svn_ra_serf__xml_gather_since() to gather up data from outer states.
+
+   ATTRS is char* -> char*.
+
+   Temporary allocations may be made in SCRATCH_POOL.  */
+typedef svn_error_t *
+(*svn_ra_serf__xml_closed_t)(svn_ra_serf__xml_estate_t *xes,
+                             void *baton,
+                             int leaving_state,
+                             const svn_string_t *cdata,
+                             apr_hash_t *attrs,
+                             apr_pool_t *scratch_pool);
+
+
+/* Called for all states that are not using the builtin cdata collection.
+   This callback is (only) appropriate for unbounded-size cdata content.
+
+   CURRENT_STATE may be used to decide what to do with the data.
+
+   Temporary allocations may be made in SCRATCH_POOL.  */
+typedef svn_error_t *
+(*svn_ra_serf__xml_cdata_t)(svn_ra_serf__xml_estate_t *xes,
+                            void *baton,
+                            int current_state,
+                            const char *data,
+                            apr_size_t len,
+                            apr_pool_t *scratch_pool);
+
+
+/* State transition table.
+
+   When the XML Context is constructed, it is in state 0. User states are
+   positive integers.
+
+   In a list of transitions, use { 0 } to indicate the end. Specifically,
+   the code looks for NS == NULL.
+
+   ### more docco
+*/
+typedef struct svn_ra_serf__xml_transition_t {
+  /* This transition applies when in this state  */
+  int from_state;
+
+  /* And when this tag is observed  */
+  const char *ns;
+  const char *name;
+
+  /* Moving to this state  */
+  int to_state;
+
+  /* Should the cdata of NAME be collected? Note that CUSTOM_CLOSE should
+     be TRUE in order to capture this cdata.  */
+  svn_boolean_t collect_cdata;
+
+  /* Which attributes of NAME should be collected? Terminate with NULL.
+     Maximum of 10 attributes may be collected. Note that attribute
+     namespaces are ignored at this time.
+
+     Attribute names beginning with "?" are optional. Other names must
+     exist on the element, or SVN_ERR_XML_ATTRIB_NOT_FOUND will be raised.  */
+  const char *collect_attrs[11];
+
+  /* When NAME is closed, should the callback be invoked?  */
+  svn_boolean_t custom_close;
+
+} svn_ra_serf__xml_transition_t;
+
+
+/* Construct an XML parsing context, based on the TTABLE transition table.
+   As content is parsed, the CLOSED_CB callback will be invoked according
+   to the definition in the table.
+
+   If OPENED_CB is not NULL, then it will be invoked for *every* tag-open
+   event. The callback will need to use the ENTERED_STATE and TAG parameters
+   to decide what it would like to do.
+
+   If CDATA_CB is not NULL, then it will be called for all cdata that is
+   not be automatically collected (based on the transition table record's
+   COLLECT_CDATA flag). It will be called in every state, so the callback
+   must examine the CURRENT_STATE parameter to decide what to do.
+
+   The same BATON value will be passed to all three callbacks.
+
+   The context will be created within RESULT_POOL.  */
+svn_ra_serf__xml_context_t *
+svn_ra_serf__xml_context_create(
+  const svn_ra_serf__xml_transition_t *ttable,
+  svn_ra_serf__xml_opened_t opened_cb,
+  svn_ra_serf__xml_closed_t closed_cb,
+  svn_ra_serf__xml_cdata_t cdata_cb,
+  void *baton,
+  apr_pool_t *result_pool);
+
+/* Destroy all subpools for this structure. */
+void
+svn_ra_serf__xml_context_destroy(
+  svn_ra_serf__xml_context_t *xmlctx);
+
+/* Construct a handler with the response function/baton set up to parse
+   a response body using the given XML context. The handler and its
+   internal structures are allocated in RESULT_POOL.
+
+   This also initializes HANDLER_POOL to the given RESULT_POOL.  */
+svn_ra_serf__handler_t *
+svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx,
+                                  apr_pool_t *result_pool);
+
+
+/* Allocated within XES->STATE_POOL. Changes are not allowd (callers
+   should make a deep copy if they need to make changes).
+
+   The resulting hash maps char* names to char* values.  */
+apr_hash_t *
+svn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t *xes,
+                              int stop_state);
+
+
+/* Attach the NAME/VALUE pair onto this/parent state identified by STATE.
+   The name and value will be copied into the target state's pool.
+
+   These values will be available to the CLOSED_CB for the target state,
+   or part of the gathered state via xml_gather_since().
+
+   Typically, this function is used by a child state's close callback,
+   or within an opening callback to store additional data.
+
+   Note: if the state is not found, then a programmer error has occurred,
+   so the function will invoke SVN_ERR_MALFUNCTION().  */
+void
+svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes,
+                      int state,
+                      const char *name,
+                      const char *value);
+
+
+/* Returns XES->STATE_POOL for allocating structures that should live
+   as long as the state identified by XES.
+
+   Note: a state pool is created upon demand, so only use this function
+   when memory is required for a given state.  */
+apr_pool_t *
+svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes);
+
+
+/* Any XML parser may be used. When an opening tag is seen, call this
+   function to feed the information into XMLCTX.  */
+svn_error_t *
+svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
+                          const char *raw_name,
+                          const char *const *attrs);
+
+
+/* When a close tag is seen, call this function to feed the information
+   into XMLCTX.  */
+svn_error_t *
+svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
+                        const char *raw_name);
+
+
+/* When cdata is parsed by the wrapping XML parser, call this function to
+   feed the cdata into the XMLCTX.  */
+svn_error_t *
+svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
+                          const char *data,
+                          apr_size_t len);
+
+
 /*
  * Parses a server-side error message into a local Subversion error.
  */
-typedef struct svn_ra_serf__server_error_t {
+struct svn_ra_serf__server_error_t {
   /* Our local representation of the error. */
   svn_error_t *error;
 
-  /* Have we checked to see if there's an XML error in this response? */
-  svn_boolean_t init;
-
-  /* Was there an XML error response? */
-  svn_boolean_t has_xml_response;
-
   /* Are we done with the response? */
   svn_boolean_t done;
 
@@ -631,41 +839,8 @@ typedef struct svn_ra_serf__server_error
 
   /* XML parser and namespace used to parse the remote response */
   svn_ra_serf__xml_parser_t parser;
-} svn_ra_serf__server_error_t;
-
-/* A simple request context that can be passed to handle_status_only. */
-typedef struct svn_ra_serf__simple_request_context_t {
-  apr_pool_t *pool;
-
-  /* The HTTP status code of the response */
-  int status;
-
-  /* The HTTP status line of the response */
-  const char *reason;
-
-  /* The Location header value of the response, or NULL if there
-     wasn't one. */
-  const char *location;
-
-  /* This value is set to TRUE when the response is completed. */
-  svn_boolean_t done;
+};
 
-  /* If an error occurred, this value will be initialized. */
-  svn_ra_serf__server_error_t server_error;
-} svn_ra_serf__simple_request_context_t;
-
-/*
- * Serf handler for @a request / @a response pair that takes in a
- * @a baton (@see svn_ra_serf__simple_request_context_t).
- * Implements svn_ra_serf__response_handler_t.
- *
- * Temporary allocations are made in @a pool.
- */
-svn_error_t *
-svn_ra_serf__handle_status_only(serf_request_t *request,
-                                serf_bucket_t *response,
-                                void *baton,
-                                apr_pool_t *pool);
 
 /*
  * Handler that discards the entire @a response body associated with a
@@ -682,32 +857,45 @@ svn_ra_serf__handle_discard_body(serf_re
                                  void *baton,
                                  apr_pool_t *pool);
 
-/*
- * Handler that retrieves the embedded XML error response from the
- * the @a response body associated with a @a request.
- * Implements svn_ra_serf__response_handler_t.
- *
- * All temporary allocations will be made in a @a pool.
- */
-svn_error_t *
-svn_ra_serf__handle_server_error(serf_request_t *request,
-                                 serf_bucket_t *response,
-                                 apr_pool_t *pool);
 
 /*
  * Handler that retrieves the embedded XML multistatus response from the
- * the @a RESPONSE body associated with a @a REQUEST. *DONE is set to TRUE.
+ * the @a RESPONSE body associated with a @a REQUEST.
+ *
  * Implements svn_ra_serf__response_handler_t.
  *
- * The @a BATON should be of type svn_ra_serf__simple_request_context_t.
+ * The @a BATON should be of type svn_ra_serf__handler_t. When the request
+ * is complete, the handler's DONE flag will be set to TRUE.
  *
- * All temporary allocations will be made in a @a pool.
+ * All temporary allocations will be made in a @a scratch_pool.
  */
 svn_error_t *
 svn_ra_serf__handle_multistatus_only(serf_request_t *request,
                                      serf_bucket_t *response,
                                      void *baton,
-                                     apr_pool_t *pool);
+                                     apr_pool_t *scratch_pool);
+
+
+/* Handler that expects an empty body.
+
+   If a body IS present, and it is text/xml, then it will be parsed for
+   a server-side error.
+
+   BATON should be the svn_ra_serf__handler_t running REQUEST.
+
+   Status line information will be in HANDLER->SLINE.
+
+   Any parsed errors will be left in HANDLER->SERVER_ERROR. That member
+   may be NULL if no body was present, or a problem occurred trying to
+   parse the body.
+
+   All temporary allocations will be made in SCRATCH_POOL.  */
+svn_error_t *
+svn_ra_serf__expect_empty_body(serf_request_t *request,
+                               serf_bucket_t *response,
+                               void *baton,
+                               apr_pool_t *scratch_pool);
+
 
 /*
  * This function will feed the RESPONSE body into XMLP.  When parsing is
@@ -736,12 +924,6 @@ svn_ra_serf__response_discard_handler(se
                                       void *baton,
                                       apr_pool_t *pool);
 
-/* Return the value of the RESPONSE's Location header if any, or NULL
- * otherwise.  All allocations will be made in POOL.
- */
-const char *
-svn_ra_serf__response_get_location(serf_bucket_t *response,
-                                   apr_pool_t *pool);
 
 /** XML helper functions. **/
 
@@ -827,12 +1009,12 @@ svn_ra_serf__add_cdata_len_buckets(serf_
  * Look up the @a attrs array for namespace definitions and add each one
  * to the @a ns_list of namespaces.
  *
- * New namespaces will be allocated in @a pool.
+ * New namespaces will be allocated in RESULT_POOL.
  */
 void
 svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list,
-                       const char **attrs,
-                       apr_pool_t *pool);
+                       const char *const *attrs,
+                       apr_pool_t *result_pool);
 
 /*
  * Look up @a name in the @a ns_list list for previously declared namespace
@@ -843,27 +1025,12 @@ svn_ra_serf__define_ns(svn_ra_serf__ns_t
  */
 void
 svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
-                       svn_ra_serf__ns_t *ns_list,
+                       const svn_ra_serf__ns_t *ns_list,
                        const char *name);
 
 
 /** PROPFIND-related functions **/
 
-/* Opaque structure representing PROPFINDs. */
-typedef struct svn_ra_serf__propfind_context_t svn_ra_serf__propfind_context_t;
-
-/*
- * Returns a flag representing whether the PROPFIND @a ctx is completed.
- */
-svn_boolean_t
-svn_ra_serf__propfind_is_done(svn_ra_serf__propfind_context_t *ctx);
-
-/*
- * Returns the response status code of the PROPFIND @a ctx.
- */
-int
-svn_ra_serf__propfind_status_code(svn_ra_serf__propfind_context_t *ctx);
-
 /*
  * This function will deliver a PROP_CTX PROPFIND request in the SESS
  * serf context for the properties listed in LOOKUP_PROPS at URL for
@@ -873,7 +1040,7 @@ svn_ra_serf__propfind_status_code(svn_ra
  * expected to call svn_ra_serf__wait_for_props().
  */
 svn_error_t *
-svn_ra_serf__deliver_props(svn_ra_serf__propfind_context_t **prop_ctx,
+svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler,
                            apr_hash_t *prop_vals,
                            svn_ra_serf__session_t *sess,
                            svn_ra_serf__connection_t *conn,
@@ -885,13 +1052,12 @@ svn_ra_serf__deliver_props(svn_ra_serf__
                            apr_pool_t *pool);
 
 /*
- * This helper function will block until the PROP_CTX indicates that is done
- * or another error is returned.
+ * This helper function will block until PROPFIND_HANDLER indicates that is
+ * done or another error is returned.
  */
 svn_error_t *
-svn_ra_serf__wait_for_props(svn_ra_serf__propfind_context_t *prop_ctx,
-                            svn_ra_serf__session_t *sess,
-                            apr_pool_t *pool);
+svn_ra_serf__wait_for_props(svn_ra_serf__handler_t *handler,
+                            apr_pool_t *scratch_pool);
 
 /* This is a blocking version of deliver_props.
 
@@ -913,6 +1079,50 @@ svn_ra_serf__retrieve_props(apr_hash_t *
                             apr_pool_t *result_pool,
                             apr_pool_t *scratch_pool);
 
+
+/* Using CONN, fetch the properties specified by WHICH_PROPS using CONN
+   for URL at REVISION. The resulting properties are placed into a 2-level
+   hash in RESULTS, mapping NAMESPACE -> hash<PROPNAME, PROPVALUE>, which
+   is allocated in RESULT_POOL.
+
+   If REVISION is SVN_INVALID_REVNUM, then the properties are fetched
+   from HEAD for URL.
+
+   This function performs the request synchronously.
+
+   Temporary allocations are made in SCRATCH_POOL.  */
+svn_error_t *
+svn_ra_serf__fetch_node_props(apr_hash_t **results,
+                              svn_ra_serf__connection_t *conn,
+                              const char *url,
+                              svn_revnum_t revision,
+                              const svn_ra_serf__dav_props_t *which_props,
+                              apr_pool_t *result_pool,
+                              apr_pool_t *scratch_pool);
+
+
+/* Using CONN, fetch a DAV: property from the resource identified by URL
+   within REVISION. The PROPNAME may be one of:
+
+     "checked-in"
+     "href"
+
+   The resulting value will be allocated in RESULT_POOL, and may be NULL
+   if the property does not exist (note: "href" always exists).
+
+   This function performs the request synchronously.
+
+   Temporary allocations are made in SCRATCH_POOL.  */
+svn_error_t *
+svn_ra_serf__fetch_dav_prop(const char **value,
+                            svn_ra_serf__connection_t *conn,
+                            const char *url,
+                            svn_revnum_t revision,
+                            const char *propname,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool);
+
+
 /* Set PROPS for PATH at REV revision with a NS:NAME VAL.
  *
  * The POOL governs allocation.
@@ -941,6 +1151,15 @@ svn_ra_serf__walk_all_props(apr_hash_t *
                             void *baton,
                             apr_pool_t *pool);
 
+
+/* Like walk_all_props(), but a 2-level hash.  */
+svn_error_t *
+svn_ra_serf__walk_node_props(apr_hash_t *props,
+                             svn_ra_serf__walker_visitor_t walker,
+                             void *baton,
+                             apr_pool_t *scratch_pool);
+
+
 typedef svn_error_t *
 (*svn_ra_serf__path_rev_walker_t)(void *baton,
                                   const char *path, apr_ssize_t path_len,
@@ -979,9 +1198,8 @@ svn_ra_serf__select_revprops(apr_hash_t 
                              apr_pool_t *scratch_pool);
 
 
-/* PROPS is nested hash tables mapping REV -> PATH -> NS -> NAME -> VALUE.
-   This function takes the tree of tables identified by PATH and REVISION
-   (resulting in NS:NAME:VALUE hashes) and flattens them into a set of
+/* PROPS is nested hash tables mapping NS -> NAME -> VALUE.
+   This function takes the NS:NAME:VALUE hashes and flattens them into a set of
    names to VALUE. The names are composed of NS:NAME, with specific
    rewrite from wire names (DAV) to SVN names. This mapping is managed
    by the svn_ra_serf__set_baton_props() function.
@@ -994,8 +1212,6 @@ svn_ra_serf__select_revprops(apr_hash_t 
 svn_error_t *
 svn_ra_serf__flatten_props(apr_hash_t **flat_props,
                            apr_hash_t *props,
-                           const char *path,
-                           svn_revnum_t revision,
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool);
 
@@ -1035,25 +1251,12 @@ svn_ra_serf__set_prop(apr_hash_t *props,
                       const svn_string_t *val, apr_pool_t *pool);
 
 svn_error_t *
-svn_ra_serf__get_resource_type(svn_node_kind_t *kind,
-                               apr_hash_t *props,
-                               const char *url,
-                               svn_revnum_t revision);
+svn_ra_serf__get_resource_type(svn_kind_t *kind,
+                               apr_hash_t *props);
 
 
 /** MERGE-related functions **/
 
-typedef struct svn_ra_serf__merge_context_t svn_ra_serf__merge_context_t;
-
-svn_boolean_t*
-svn_ra_serf__merge_get_done_ptr(svn_ra_serf__merge_context_t *ctx);
-
-svn_commit_info_t*
-svn_ra_serf__merge_get_commit_info(svn_ra_serf__merge_context_t *ctx);
-
-int
-svn_ra_serf__merge_get_status(svn_ra_serf__merge_context_t *ctx);
-
 void
 svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens,
                                    const char *parent,
@@ -1067,38 +1270,48 @@ svn_ra_serf__merge_lock_token_list(apr_h
    client.  If KEEP_LOCKS is set, instruct the server to not release
    locks set on the paths included in this commit.  */
 svn_error_t *
-svn_ra_serf__merge_create_req(svn_ra_serf__merge_context_t **merge_ctx,
-                              svn_ra_serf__session_t *session,
-                              svn_ra_serf__connection_t *conn,
-                              const char *merge_resource_url,
-                              apr_hash_t *lock_tokens,
-                              svn_boolean_t keep_locks,
-                              apr_pool_t *pool);
+svn_ra_serf__run_merge(const svn_commit_info_t **commit_info,
+                       int *response_code,
+                       svn_ra_serf__session_t *session,
+                       svn_ra_serf__connection_t *conn,
+                       const char *merge_resource_url,
+                       apr_hash_t *lock_tokens,
+                       svn_boolean_t keep_locks,
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool);
+
 
 /** OPTIONS-related functions **/
 
-typedef struct svn_ra_serf__options_context_t svn_ra_serf__options_context_t;
+/* On HTTPv2 connections, run an OPTIONS request over CONN to fetch the
+   current youngest revnum, returning it in *YOUNGEST.
 
-/* Is this OPTIONS-request done yet? */
-svn_boolean_t*
-svn_ra_serf__get_options_done_ptr(svn_ra_serf__options_context_t *ctx);
+   (the revnum is headers of the OPTIONS response)
 
-const char *
-svn_ra_serf__options_get_activity_collection(svn_ra_serf__options_context_t *ctx);
+   This function performs the request synchronously.
 
-svn_revnum_t
-svn_ra_serf__options_get_youngest_rev(svn_ra_serf__options_context_t *ctx);
+   All temporary allocations will be made in SCRATCH_POOL.  */
+svn_error_t *
+svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest,
+                                    svn_ra_serf__connection_t *conn,
+                                    apr_pool_t *scratch_pool);
+
+
+/* On HTTPv1 connections, run an OPTIONS request over CONN to fetch the
+   activity collection set and return it in *ACTIVITY_URL, allocated
+   from RESULT_POOL.
+
+   (the activity-collection-set is in the body of the OPTIONS response)
+
+   This function performs the request synchronously.
+
+   All temporary allocations will be made in SCRATCH_POOL.  */
+svn_error_t *
+svn_ra_serf__v1_get_activity_collection(const char **activity_url,
+                                        svn_ra_serf__connection_t *conn,
+                                        apr_pool_t *result_pool,
+                                        apr_pool_t *scratch_pool);
 
-/* Create an OPTIONS request.  When run, ask for an
-   activity-collection-set in the request body (retrievable via
-   accessor above) and also parse the server's capability headers into
-   the SESSION->capabilites hash. */
-svn_error_t *
-svn_ra_serf__create_options_req(svn_ra_serf__options_context_t **opt_ctx,
-                                svn_ra_serf__session_t *session,
-                                svn_ra_serf__connection_t *conn,
-                                const char *path,
-                                apr_pool_t *pool);
 
 /* Set @a VCC_URL to the default VCC for our repository based on @a
  * ORIG_PATH for the session @a SESSION, ensuring that the VCC URL and
@@ -1138,34 +1351,63 @@ svn_ra_serf__get_relative_path(const cha
                                svn_ra_serf__connection_t *conn,
                                apr_pool_t *pool);
 
-/* Set *BC_URL to the baseline collection url, and set *BC_RELATIVE to
- * the path relative to that url for URL in REVISION using SESSION.
- * BC_RELATIVE will be URI decoded.
- *
- * REVISION may be SVN_INVALID_REVNUM (to mean "the current HEAD
- * revision").  If URL is NULL, use SESSION's session url.
- *
- * If LATEST_REVNUM is not NULL, set it to the baseline revision. If
- * REVISION was set to SVN_INVALID_REVNUM, this will return the current
- * HEAD revision.
- *
- * If non-NULL, use CONN for communications with the server;
- * otherwise, use the default connection.
- *
- * Use POOL for all allocations.
- */
+
+/* Using the default connection in SESSION (conns[0]), get the youngest
+   revnum from the server, returning it in *YOUNGEST.
+
+   This function operates synchronously.
+
+   All temporary allocations are performed in SCRATCH_POOL.  */
 svn_error_t *
-svn_ra_serf__get_baseline_info(const char **bc_url,
-                               const char **bc_relative,
-                               svn_ra_serf__session_t *session,
-                               svn_ra_serf__connection_t *conn,
-                               const char *url,
-                               svn_revnum_t revision,
-                               svn_revnum_t *latest_revnum,
-                               apr_pool_t *pool);
+svn_ra_serf__get_youngest_revnum(svn_revnum_t *youngest,
+                                 svn_ra_serf__session_t *session,
+                                 apr_pool_t *scratch_pool);
+
+
+/* Generate a revision-stable URL.
+
+   The RA APIs all refer to user/public URLs that float along with the
+   youngest revision. In many cases, we do NOT want to work with that URL
+   since it can change from one moment to the next. Especially if we
+   attempt to operation against multiple floating URLs -- we could end up
+   referring to two separate revisions.
+
+   The DAV RA provider(s) solve this by generating a URL that is specific
+   to a revision by using a URL into a "baseline collection".
+
+   For a specified SESSION, with an optional CONN (if NULL, then the
+   session's default connection will be used; specifically SESSION->conns[0]),
+   generate a revision-stable URL for URL at REVISION. If REVISION is
+   SVN_INVALID_REVNUM, then the stable URL will refer to the youngest
+   revision at the time this function was called.
+
+   If URL is NULL, then the session root will be used.
+
+   The stable URL will be placed into *STABLE_URL, allocated from RESULT_POOL.
+
+   If LATEST_REVNUM is not NULL, then the revision used will be placed into
+   *LATEST_REVNUM. That will be equal to youngest, or the given REVISION.
+
+   This function operates synchronously, if any communication to the server
+   is required. Communication is needed if REVISION is SVN_INVALID_REVNUM
+   (to get the current youngest revnum), or if the specified REVISION is not
+   (yet) in our cache of baseline collections.
+
+   All temporary allocations are performed in SCRATCH_POOL.  */
+svn_error_t *
+svn_ra_serf__get_stable_url(const char **stable_url,
+                            svn_revnum_t *latest_revnum,
+                            svn_ra_serf__session_t *session,
+                            svn_ra_serf__connection_t *conn,
+                            const char *url,
+                            svn_revnum_t revision,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool);
+
 
 /** RA functions **/
 
+/* Implements svn_ra__vtable_t.get_log(). */
 svn_error_t *
 svn_ra_serf__get_log(svn_ra_session_t *session,
                      const apr_array_header_t *paths,
@@ -1180,6 +1422,7 @@ svn_ra_serf__get_log(svn_ra_session_t *s
                      void *receiver_baton,
                      apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.get_locations(). */
 svn_error_t *
 svn_ra_serf__get_locations(svn_ra_session_t *session,
                            apr_hash_t **locations,
@@ -1188,6 +1431,7 @@ svn_ra_serf__get_locations(svn_ra_sessio
                            const apr_array_header_t *location_revisions,
                            apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.get_location_segments(). */
 svn_error_t *
 svn_ra_serf__get_location_segments(svn_ra_session_t *session,
                                    const char *path,
@@ -1198,6 +1442,7 @@ svn_ra_serf__get_location_segments(svn_r
                                    void *receiver_baton,
                                    apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.do_diff(). */
 svn_error_t *
 svn_ra_serf__do_diff(svn_ra_session_t *session,
                      const svn_ra_reporter3_t **reporter,
@@ -1212,6 +1457,7 @@ svn_ra_serf__do_diff(svn_ra_session_t *s
                      void *diff_baton,
                      apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.do_status(). */
 svn_error_t *
 svn_ra_serf__do_status(svn_ra_session_t *ra_session,
                        const svn_ra_reporter3_t **reporter,
@@ -1223,6 +1469,7 @@ svn_ra_serf__do_status(svn_ra_session_t 
                        void *status_baton,
                        apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.do_update(). */
 svn_error_t *
 svn_ra_serf__do_update(svn_ra_session_t *ra_session,
                        const svn_ra_reporter3_t **reporter,
@@ -1235,6 +1482,7 @@ svn_ra_serf__do_update(svn_ra_session_t 
                        void *update_baton,
                        apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.do_switch(). */
 svn_error_t *
 svn_ra_serf__do_switch(svn_ra_session_t *ra_session,
                        const svn_ra_reporter3_t **reporter,
@@ -1247,6 +1495,7 @@ svn_ra_serf__do_switch(svn_ra_session_t 
                        void *switch_baton,
                        apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.get_file_revs(). */
 svn_error_t *
 svn_ra_serf__get_file_revs(svn_ra_session_t *session,
                            const char *path,
@@ -1257,12 +1506,14 @@ svn_ra_serf__get_file_revs(svn_ra_sessio
                            void *handler_baton,
                            apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.get_dated_revision(). */
 svn_error_t *
 svn_ra_serf__get_dated_revision(svn_ra_session_t *session,
                                 svn_revnum_t *revision,
                                 apr_time_t tm,
                                 apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.get_commit_editor(). */
 svn_error_t *
 svn_ra_serf__get_commit_editor(svn_ra_session_t *session,
                                const svn_delta_editor_t **editor,
@@ -1274,6 +1525,7 @@ svn_ra_serf__get_commit_editor(svn_ra_se
                                svn_boolean_t keep_locks,
                                apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.get_file(). */
 svn_error_t *
 svn_ra_serf__get_file(svn_ra_session_t *session,
                       const char *path,
@@ -1283,6 +1535,7 @@ svn_ra_serf__get_file(svn_ra_session_t *
                       apr_hash_t **props,
                       apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.change_rev_prop(). */
 svn_error_t *
 svn_ra_serf__change_rev_prop(svn_ra_session_t *session,
                              svn_revnum_t rev,
@@ -1291,6 +1544,7 @@ svn_ra_serf__change_rev_prop(svn_ra_sess
                              const svn_string_t *value,
                              apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.replay(). */
 svn_error_t *
 svn_ra_serf__replay(svn_ra_session_t *ra_session,
                     svn_revnum_t revision,
@@ -1300,6 +1554,7 @@ svn_ra_serf__replay(svn_ra_session_t *ra
                     void *edit_baton,
                     apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.replay_range(). */
 svn_error_t *
 svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
                           svn_revnum_t start_revision,
@@ -1311,6 +1566,7 @@ svn_ra_serf__replay_range(svn_ra_session
                           void *replay_baton,
                           apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.lock(). */
 svn_error_t *
 svn_ra_serf__lock(svn_ra_session_t *ra_session,
                   apr_hash_t *path_revs,
@@ -1320,6 +1576,7 @@ svn_ra_serf__lock(svn_ra_session_t *ra_s
                   void *lock_baton,
                   apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.unlock(). */
 svn_error_t *
 svn_ra_serf__unlock(svn_ra_session_t *ra_session,
                     apr_hash_t *path_tokens,
@@ -1328,12 +1585,14 @@ svn_ra_serf__unlock(svn_ra_session_t *ra
                     void *lock_baton,
                     apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.get_lock(). */
 svn_error_t *
 svn_ra_serf__get_lock(svn_ra_session_t *ra_session,
                       svn_lock_t **lock,
                       const char *path,
                       apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.get_locks(). */
 svn_error_t *
 svn_ra_serf__get_locks(svn_ra_session_t *ra_session,
                        apr_hash_t **locks,
@@ -1341,13 +1600,19 @@ svn_ra_serf__get_locks(svn_ra_session_t 
                        svn_depth_t depth,
                        apr_pool_t *pool);
 
-svn_error_t * svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session,
-                                         apr_hash_t **mergeinfo,
-                                         const apr_array_header_t *paths,
-                                         svn_revnum_t revision,
-                                         svn_mergeinfo_inheritance_t inherit,
-                                         svn_boolean_t include_descendants,
-                                         apr_pool_t *pool);
+/* Request a mergeinfo-report from the URL attached to SESSION,
+   and fill in the MERGEINFO hash with the results.
+
+   Implements svn_ra__vtable_t.get_mergeinfo().
+ */
+svn_error_t *
+svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session,
+                           apr_hash_t **mergeinfo,
+                           const apr_array_header_t *paths,
+                           svn_revnum_t revision,
+                           svn_mergeinfo_inheritance_t inherit,
+                           svn_boolean_t include_descendants,
+                           apr_pool_t *pool);
 
 /* Exchange capabilities with the server, by sending an OPTIONS
  * request announcing the client's capabilities, and by filling
@@ -1366,14 +1631,14 @@ svn_ra_serf__exchange_capabilities(svn_r
                                    const char **corrected_url,
                                    apr_pool_t *pool);
 
-/* Implements the has_capability RA layer function. */
+/* Implements svn_ra__vtable_t.has_capability(). */
 svn_error_t *
 svn_ra_serf__has_capability(svn_ra_session_t *ra_session,
                             svn_boolean_t *has,
                             const char *capability,
                             apr_pool_t *pool);
 
-/* Implements the get_deleted_rev RA layer function. */
+/* Implements svn_ra__vtable_t.get_deleted_rev(). */
 svn_error_t *
 svn_ra_serf__get_deleted_rev(svn_ra_session_t *session,
                              const char *path,
@@ -1382,6 +1647,18 @@ svn_ra_serf__get_deleted_rev(svn_ra_sess
                              svn_revnum_t *revision_deleted,
                              apr_pool_t *pool);
 
+/* Implements svn_ra__vtable_t.get_repos_root(). */
+svn_error_t *
+svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session,
+                            const char **url,
+                            apr_pool_t *pool);
+
+/* Implements svn_ra__vtable_t.register_editor_shim_callbacks(). */
+svn_error_t *
+svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *session,
+                                    svn_delta_shim_callbacks_t *callbacks);
+
+
 /*** Authentication handler declarations ***/
 
 /**
@@ -1408,16 +1685,14 @@ svn_ra_serf__error_on_status(int status_
                              const char *path,
                              const char *location);
 
-svn_error_t *
-svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *session,
-                                    svn_delta_shim_callbacks_t *callbacks);
-
-
+/* ###? */
 svn_error_t *
 svn_ra_serf__copy_into_spillbuf(svn_spillbuf_t **spillbuf,
                                 serf_bucket_t *bkt,
                                 apr_pool_t *result_pool,
                                 apr_pool_t *scratch_pool);
+
+/* ###? */
 serf_bucket_t *
 svn_ra_serf__create_sb_bucket(svn_spillbuf_t *spillbuf,
                               serf_bucket_alloc_t *allocator,
@@ -1425,12 +1700,6 @@ svn_ra_serf__create_sb_bucket(svn_spillb
                               apr_pool_t *scratch_pool);
 
 
-svn_error_t *
-svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session,
-                            const char **url,
-                            apr_pool_t *pool);
-
-
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */