You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2012/08/16 12:18:03 UTC

svn commit: r1373783 [19/50] - in /subversion/branches/compressed-pristines: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/client-side/svn-push/ contrib/client-side/svnmerge/ cont...

Modified: subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/mergeinfo.c?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/mergeinfo.c (original)
+++ subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/mergeinfo.c Thu Aug 16 10:17:48 2012
@@ -40,7 +40,7 @@
 
 /* The current state of our XML parsing. */
 typedef enum mergeinfo_state_e {
-  NONE = 0,
+  INITIAL = 0,
   MERGEINFO_REPORT,
   MERGEINFO_ITEM,
   MERGEINFO_PATH,
@@ -49,135 +49,88 @@ typedef enum mergeinfo_state_e {
 
 /* Baton for accumulating mergeinfo.  RESULT_CATALOG stores the final
    mergeinfo catalog result we are going to hand back to the caller of
-   get_mergeinfo.  curr_path and curr_info contain the value of the
-   CDATA from the mergeinfo items as we get them from the server.  */
-
+   get_mergeinfo.  */
 typedef struct mergeinfo_context_t {
   apr_pool_t *pool;
-  svn_stringbuf_t *curr_path;
-  svn_stringbuf_t *curr_info;
   svn_mergeinfo_t result_catalog;
-  svn_boolean_t done;
   const apr_array_header_t *paths;
   svn_revnum_t revision;
   svn_mergeinfo_inheritance_t inherit;
   svn_boolean_t include_descendants;
 } mergeinfo_context_t;
 
-static svn_error_t *
-start_element(svn_ra_serf__xml_parser_t *parser,
-              svn_ra_serf__dav_props_t name,
-              const char **attrs,
-              apr_pool_t *scratch_pool)
-{
-  mergeinfo_context_t *mergeinfo_ctx = parser->user_data;
-  mergeinfo_state_e state;
 
-  state = parser->state->current_state;
-  if (state == NONE && strcmp(name.name, SVN_DAV__MERGEINFO_REPORT) == 0)
-    {
-      svn_ra_serf__xml_push_state(parser, MERGEINFO_REPORT);
-    }
-  else if (state == MERGEINFO_REPORT &&
-           strcmp(name.name, SVN_DAV__MERGEINFO_ITEM) == 0)
-    {
-      svn_ra_serf__xml_push_state(parser, MERGEINFO_ITEM);
-      svn_stringbuf_setempty(mergeinfo_ctx->curr_path);
-      svn_stringbuf_setempty(mergeinfo_ctx->curr_info);
-    }
-  else if (state == MERGEINFO_ITEM &&
-           strcmp(name.name, SVN_DAV__MERGEINFO_PATH) == 0)
-    {
-      svn_ra_serf__xml_push_state(parser, MERGEINFO_PATH);
-    }
-  else if (state == MERGEINFO_ITEM &&
-           strcmp(name.name, SVN_DAV__MERGEINFO_INFO) == 0)
-    {
-      svn_ra_serf__xml_push_state(parser, MERGEINFO_INFO);
-    }
-  return SVN_NO_ERROR;
-}
+#define D_ "DAV:"
+#define S_ SVN_XML_NAMESPACE
+static const svn_ra_serf__xml_transition_t mergeinfo_ttable[] = {
+  { INITIAL, S_, SVN_DAV__MERGEINFO_REPORT, MERGEINFO_REPORT,
+    FALSE, { NULL }, FALSE },
+
+  { MERGEINFO_REPORT, S_, SVN_DAV__MERGEINFO_ITEM, MERGEINFO_ITEM,
+    FALSE, { NULL }, TRUE },
+
+  { MERGEINFO_ITEM, S_, SVN_DAV__MERGEINFO_PATH, MERGEINFO_PATH,
+    TRUE, { NULL }, TRUE },
+
+  { MERGEINFO_ITEM, S_, SVN_DAV__MERGEINFO_INFO, MERGEINFO_INFO,
+    TRUE, { NULL }, TRUE },
+
+  { 0 }
+};
+
 
+/* Conforms to svn_ra_serf__xml_closed_t  */
 static svn_error_t *
-end_element(svn_ra_serf__xml_parser_t *parser,
-            svn_ra_serf__dav_props_t name,
-            apr_pool_t *scratch_pool)
+mergeinfo_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)
 {
-  mergeinfo_context_t *mergeinfo_ctx = parser->user_data;
-  mergeinfo_state_e state;
-
-  state = parser->state->current_state;
+  mergeinfo_context_t *mergeinfo_ctx = baton;
 
-  if (state == MERGEINFO_REPORT &&
-      strcmp(name.name, SVN_DAV__MERGEINFO_REPORT) == 0)
+  if (leaving_state == MERGEINFO_ITEM)
     {
-      svn_ra_serf__xml_pop_state(parser);
-    }
-  else if (state == MERGEINFO_ITEM
-           && strcmp(name.name, SVN_DAV__MERGEINFO_ITEM) == 0)
-    {
-      if (mergeinfo_ctx->curr_info && mergeinfo_ctx->curr_path)
+      /* Placed here from the child elements.  */
+      const char *path = apr_hash_get(attrs, "path", APR_HASH_KEY_STRING);
+      const char *info = apr_hash_get(attrs, "info", APR_HASH_KEY_STRING);
+
+      if (path != NULL && info != NULL)
         {
           svn_mergeinfo_t path_mergeinfo;
-          const char *path;
 
-          SVN_ERR_ASSERT(mergeinfo_ctx->curr_path->data);
-          path = apr_pstrdup(mergeinfo_ctx->pool,
-                             mergeinfo_ctx->curr_path->data);
-          SVN_ERR(svn_mergeinfo_parse(&path_mergeinfo,
-                                      mergeinfo_ctx->curr_info->data,
-                                      mergeinfo_ctx->pool));
           /* Correct for naughty servers that send "relative" paths
              with leading slashes! */
+          if (path[0] == '/')
+            ++path;
+
+          SVN_ERR(svn_mergeinfo_parse(&path_mergeinfo, info,
+                                      mergeinfo_ctx->pool));
+
           apr_hash_set(mergeinfo_ctx->result_catalog,
-                       path[0] == '/' ? path + 1 : path,
-                       APR_HASH_KEY_STRING, path_mergeinfo);
+                       apr_pstrdup(mergeinfo_ctx->pool, path),
+                       APR_HASH_KEY_STRING,
+                       path_mergeinfo);
         }
-      svn_ra_serf__xml_pop_state(parser);
-    }
-  else if (state == MERGEINFO_PATH
-           && strcmp(name.name, SVN_DAV__MERGEINFO_PATH) == 0)
-    {
-      svn_ra_serf__xml_pop_state(parser);
     }
-  else if (state == MERGEINFO_INFO
-           && strcmp(name.name, SVN_DAV__MERGEINFO_INFO) == 0)
-    {
-      svn_ra_serf__xml_pop_state(parser);
-    }
-  return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *
-cdata_handler(svn_ra_serf__xml_parser_t *parser,
-              const char *data,
-              apr_size_t len,
-              apr_pool_t *scratch_pool)
-{
-  mergeinfo_context_t *mergeinfo_ctx = parser->user_data;
-  mergeinfo_state_e state;
-
-  state = parser->state->current_state;
-  switch (state)
+  else
     {
-    case MERGEINFO_PATH:
-      if (mergeinfo_ctx->curr_path)
-        svn_stringbuf_appendbytes(mergeinfo_ctx->curr_path, data, len);
-      break;
-
-    case MERGEINFO_INFO:
-      if (mergeinfo_ctx->curr_info)
-        svn_stringbuf_appendbytes(mergeinfo_ctx->curr_info, data, len);
-      break;
+      SVN_ERR_ASSERT(leaving_state == MERGEINFO_PATH
+                     || leaving_state == MERGEINFO_INFO);
 
-    default:
-      break;
+      /* Stash the value onto the parent MERGEINFO_ITEM.  */
+      svn_ra_serf__xml_note(xes, MERGEINFO_ITEM,
+                            leaving_state == MERGEINFO_PATH
+                              ? "path"
+                              : "info",
+                            cdata->data);
     }
 
   return SVN_NO_ERROR;
 }
 
+
 static svn_error_t *
 create_mergeinfo_body(serf_bucket_t **bkt,
                       void *baton,
@@ -229,8 +182,6 @@ create_mergeinfo_body(serf_bucket_t **bk
   return SVN_NO_ERROR;
 }
 
-/* Request a mergeinfo-report from the URL attached to SESSION,
-   and fill in the MERGEINFO hash with the results.  */
 svn_error_t *
 svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session,
                            svn_mergeinfo_catalog_t *catalog,
@@ -241,34 +192,32 @@ svn_ra_serf__get_mergeinfo(svn_ra_sessio
                            apr_pool_t *pool)
 {
   svn_error_t *err, *err2;
-  int status_code;
-
   mergeinfo_context_t *mergeinfo_ctx;
   svn_ra_serf__session_t *session = ra_session->priv;
   svn_ra_serf__handler_t *handler;
-  svn_ra_serf__xml_parser_t *parser_ctx;
-  const char *relative_url, *basecoll_url;
+  svn_ra_serf__xml_context_t *xmlctx;
   const char *path;
 
   *catalog = NULL;
 
-  SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url, session,
-                                         NULL, NULL, revision, NULL, pool));
-
-  path = svn_path_url_add_component2(basecoll_url, relative_url, pool);
+  SVN_ERR(svn_ra_serf__get_stable_url(&path, NULL /* latest_revnum */,
+                                      session, NULL /* conn */,
+                                      NULL /* url */, revision,
+                                      pool, pool));
 
   mergeinfo_ctx = apr_pcalloc(pool, sizeof(*mergeinfo_ctx));
   mergeinfo_ctx->pool = pool;
-  mergeinfo_ctx->curr_path = svn_stringbuf_create_empty(pool);
-  mergeinfo_ctx->curr_info = svn_stringbuf_create_empty(pool);
-  mergeinfo_ctx->done = FALSE;
   mergeinfo_ctx->result_catalog = apr_hash_make(pool);
   mergeinfo_ctx->paths = paths;
   mergeinfo_ctx->revision = revision;
   mergeinfo_ctx->inherit = inherit;
   mergeinfo_ctx->include_descendants = include_descendants;
 
-  handler = apr_pcalloc(pool, sizeof(*handler));
+  xmlctx = svn_ra_serf__xml_context_create(mergeinfo_ttable,
+                                           NULL, mergeinfo_closed, NULL,
+                                           mergeinfo_ctx,
+                                           pool);
+  handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
 
   handler->method = "REPORT";
   handler->path = path;
@@ -278,25 +227,10 @@ svn_ra_serf__get_mergeinfo(svn_ra_sessio
   handler->body_delegate_baton = mergeinfo_ctx;
   handler->body_type = "text/xml";
 
-  parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
-
-  parser_ctx->pool = pool;
-  parser_ctx->user_data = mergeinfo_ctx;
-  parser_ctx->start = start_element;
-  parser_ctx->end = end_element;
-  parser_ctx->cdata = cdata_handler;
-  parser_ctx->done = &mergeinfo_ctx->done;
-  parser_ctx->status_code = &status_code;
-
-  handler->response_handler = svn_ra_serf__handle_xml_parser;
-  handler->response_baton = parser_ctx;
-
-  svn_ra_serf__request_create(handler);
-
-  err = svn_ra_serf__context_run_wait(&mergeinfo_ctx->done, session, pool);
+  err = svn_ra_serf__context_run_one(handler, pool);
 
-  err2 = svn_ra_serf__error_on_status(status_code, handler->path,
-                                      parser_ctx->location);
+  err2 = svn_ra_serf__error_on_status(handler->sline.code, handler->path,
+                                      handler->location);
   if (err2)
     {
       svn_error_clear(err);
@@ -305,7 +239,7 @@ svn_ra_serf__get_mergeinfo(svn_ra_sessio
 
   SVN_ERR(err);
 
-  if (mergeinfo_ctx->done && apr_hash_count(mergeinfo_ctx->result_catalog))
+  if (handler->done && apr_hash_count(mergeinfo_ctx->result_catalog))
     *catalog = mergeinfo_ctx->result_catalog;
 
   return SVN_NO_ERROR;

Modified: subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/options.c
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/options.c?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/options.c (original)
+++ subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/options.c Thu Aug 16 10:17:48 2012
@@ -48,165 +48,68 @@
 /*
  * This enum represents the current state of our XML parsing for an OPTIONS.
  */
-typedef enum options_state_e {
+enum options_state_e {
+  INITIAL = 0,
   OPTIONS,
   ACTIVITY_COLLECTION,
   HREF
-} options_state_e;
-
-typedef struct options_state_list_t {
-  /* The current state that we are in now. */
-  options_state_e state;
-
-  /* The previous state we were in. */
-  struct options_state_list_t *prev;
-} options_state_list_t;
+};
 
-struct svn_ra_serf__options_context_t {
+typedef struct options_context_t {
   /* pool to allocate memory from */
   apr_pool_t *pool;
 
-  /* Buffer for the activity-collection  */
-  svn_stringbuf_t *acbuf;
-  svn_boolean_t collect_cdata;
-
-  /* Current state we're in */
-  options_state_list_t *state;
-  options_state_list_t *free_state;
-
-  /* HTTP Status code */
-  int status_code;
-
-  /* are we done? */
-  svn_boolean_t done;
+  /* Have we extracted options values from the headers already?  */
+  svn_boolean_t headers_processed;
 
   svn_ra_serf__session_t *session;
   svn_ra_serf__connection_t *conn;
+  svn_ra_serf__handler_t *handler;
 
-  const char *path;
+  svn_ra_serf__response_handler_t inner_handler;
+  void *inner_baton;
 
   const char *activity_collection;
   svn_revnum_t youngest_rev;
 
-  serf_response_acceptor_t acceptor;
-  serf_response_handler_t handler;
-  svn_ra_serf__xml_parser_t *parser_ctx;
+} options_context_t;
 
-};
+#define D_ "DAV:"
+#define S_ SVN_XML_NAMESPACE
+static const svn_ra_serf__xml_transition_t options_ttable[] = {
+  { INITIAL, D_, "options-response", OPTIONS,
+    FALSE, { NULL }, FALSE },
 
-static void
-push_state(svn_ra_serf__options_context_t *options_ctx, options_state_e state)
-{
-  options_state_list_t *new_state;
+  { OPTIONS, D_, "activity-collection-set", ACTIVITY_COLLECTION,
+    FALSE, { NULL }, FALSE },
 
-  if (!options_ctx->free_state)
-    {
-      new_state = apr_palloc(options_ctx->pool, sizeof(*options_ctx->state));
-    }
-  else
-    {
-      new_state = options_ctx->free_state;
-      options_ctx->free_state = options_ctx->free_state->prev;
-    }
-  new_state->state = state;
+  { ACTIVITY_COLLECTION, D_, "href", HREF,
+    TRUE, { NULL }, TRUE },
 
-  /* Add it to the state chain. */
-  new_state->prev = options_ctx->state;
-  options_ctx->state = new_state;
-}
-
-static void pop_state(svn_ra_serf__options_context_t *options_ctx)
-{
-  options_state_list_t *free_state;
-  free_state = options_ctx->state;
-  /* advance the current state */
-  options_ctx->state = options_ctx->state->prev;
-  free_state->prev = options_ctx->free_state;
-  options_ctx->free_state = free_state;
-}
-
-static svn_error_t *
-start_options(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__options_context_t *options_ctx = parser->user_data;
-
-  if (!options_ctx->state && strcmp(name.name, "options-response") == 0)
-    {
-      push_state(options_ctx, OPTIONS);
-    }
-  else if (!options_ctx->state)
-    {
-      /* Nothing to do. */
-      return SVN_NO_ERROR;
-    }
-  else if (options_ctx->state->state == OPTIONS &&
-           strcmp(name.name, "activity-collection-set") == 0)
-    {
-      push_state(options_ctx, ACTIVITY_COLLECTION);
-    }
-  else if (options_ctx->state->state == ACTIVITY_COLLECTION &&
-           strcmp(name.name, "href") == 0)
-    {
-      options_ctx->collect_cdata = TRUE;
-      push_state(options_ctx, HREF);
-    }
+  { 0 }
+};
 
-  return SVN_NO_ERROR;
-}
 
+/* Conforms to svn_ra_serf__xml_closed_t  */
 static svn_error_t *
-end_options(svn_ra_serf__xml_parser_t *parser,
-            svn_ra_serf__dav_props_t name,
-            apr_pool_t *scratch_pool)
+options_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)
 {
-  svn_ra_serf__options_context_t *options_ctx = parser->user_data;
-  options_state_list_t *cur_state;
-
-  if (!options_ctx->state)
-    {
-      return SVN_NO_ERROR;
-    }
+  options_context_t *opt_ctx = baton;
 
-  cur_state = options_ctx->state;
+  SVN_ERR_ASSERT(leaving_state == HREF);
+  SVN_ERR_ASSERT(cdata != NULL);
 
-  if (cur_state->state == OPTIONS &&
-      strcmp(name.name, "options-response") == 0)
-    {
-      pop_state(options_ctx);
-    }
-  else if (cur_state->state == ACTIVITY_COLLECTION &&
-           strcmp(name.name, "activity-collection-set") == 0)
-    {
-      pop_state(options_ctx);
-    }
-  else if (cur_state->state == HREF &&
-           strcmp(name.name, "href") == 0)
-    {
-      options_ctx->collect_cdata = FALSE;
-      options_ctx->activity_collection =
-        svn_urlpath__canonicalize(options_ctx->acbuf->data, options_ctx->pool);
-      pop_state(options_ctx);
-    }
+  opt_ctx->activity_collection = svn_urlpath__canonicalize(cdata->data,
+                                                           opt_ctx->pool);
 
   return SVN_NO_ERROR;
 }
 
-static svn_error_t *
-cdata_options(svn_ra_serf__xml_parser_t *parser,
-              const char *data,
-              apr_size_t len,
-              apr_pool_t *scratch_pool)
-{
-  svn_ra_serf__options_context_t *ctx = parser->user_data;
-
-  if (ctx->collect_cdata)
-    svn_stringbuf_appendbytes(ctx->acbuf, data, len);
-
-  return SVN_NO_ERROR;
-}
 
 static svn_error_t *
 create_options_body(serf_bucket_t **body_bkt,
@@ -227,47 +130,17 @@ create_options_body(serf_bucket_t **body
   return SVN_NO_ERROR;
 }
 
-svn_boolean_t*
-svn_ra_serf__get_options_done_ptr(svn_ra_serf__options_context_t *ctx)
-{
-  return &ctx->done;
-}
-
-const char *
-svn_ra_serf__options_get_activity_collection(svn_ra_serf__options_context_t *ctx)
-{
-  return ctx->activity_collection;
-}
-
-svn_revnum_t
-svn_ra_serf__options_get_youngest_rev(svn_ra_serf__options_context_t *ctx)
-{
-  return ctx->youngest_rev;
-}
-
-/* Context for both options_response_handler() and capabilities callback. */
-struct options_response_ctx_t {
-  /* Baton for __handle_xml_parser() */
-  svn_ra_serf__xml_parser_t *parser_ctx;
-
-  /* Session into which we'll store server capabilities */
-  svn_ra_serf__session_t *session;
-
-  /* For temporary work only. */
-  apr_pool_t *pool;
-};
-
 
 /* We use these static pointers so we can employ pointer comparison
  * of our capabilities hash members instead of strcmp()ing all over
  * the place.
  */
 /* Both server and repository support the capability. */
-static const char *capability_yes = "yes";
+static const char *const capability_yes = "yes";
 /* Either server or repository does not support the capability. */
-static const char *capability_no = "no";
+static const char *const capability_no = "no";
 /* Server supports the capability, but don't yet know if repository does. */
-static const char *capability_server_yes = "server-yes";
+static const char *const capability_server_yes = "server-yes";
 
 
 /* This implements serf_bucket_headers_do_callback_fn_t.
@@ -277,7 +150,8 @@ capabilities_headers_iterator_callback(v
                                        const char *key,
                                        const char *val)
 {
-  struct options_response_ctx_t *orc = baton;
+  options_context_t *opt_ctx = baton;
+  svn_ra_serf__session_t *session = opt_ctx->session;
 
   if (svn_cstring_casecmp(key, "dav") == 0)
     {
@@ -285,7 +159,8 @@ capabilities_headers_iterator_callback(v
            DAV: version-control,checkout,working-resource
            DAV: merge,baseline,activity,version-controlled-collection
            DAV: http://subversion.tigris.org/xmlns/dav/svn/depth */
-      apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, orc->pool);
+      apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE,
+                                                   opt_ctx->pool);
 
       /* Right now we only have a few capabilities to detect, so just
          seek for them directly.  This could be written slightly more
@@ -294,33 +169,35 @@ capabilities_headers_iterator_callback(v
 
       if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_DEPTH, vals))
         {
-          apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_DEPTH,
-                       APR_HASH_KEY_STRING, capability_yes);
+          apr_hash_set(session->capabilities,
+                       SVN_RA_CAPABILITY_DEPTH, APR_HASH_KEY_STRING,
+                       capability_yes);
         }
       if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_MERGEINFO, vals))
         {
           /* The server doesn't know what repository we're referring
              to, so it can't just say capability_yes. */
-          apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
-                       APR_HASH_KEY_STRING, capability_server_yes);
+          apr_hash_set(session->capabilities,
+                       SVN_RA_CAPABILITY_MERGEINFO, APR_HASH_KEY_STRING,
+                       capability_server_yes);
         }
       if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_LOG_REVPROPS, vals))
         {
-          apr_hash_set(orc->session->capabilities,
-                       SVN_RA_CAPABILITY_LOG_REVPROPS,
-                       APR_HASH_KEY_STRING, capability_yes);
+          apr_hash_set(session->capabilities,
+                       SVN_RA_CAPABILITY_LOG_REVPROPS, APR_HASH_KEY_STRING,
+                       capability_yes);
         }
       if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS, vals))
         {
-          apr_hash_set(orc->session->capabilities,
-                       SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
-                       APR_HASH_KEY_STRING, capability_yes);
+          apr_hash_set(session->capabilities,
+                       SVN_RA_CAPABILITY_ATOMIC_REVPROPS, APR_HASH_KEY_STRING,
+                       capability_yes);
         }
       if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY, vals))
         {
-          apr_hash_set(orc->session->capabilities,
-                       SVN_RA_CAPABILITY_PARTIAL_REPLAY,
-                       APR_HASH_KEY_STRING, capability_yes);
+          apr_hash_set(session->capabilities,
+                       SVN_RA_CAPABILITY_PARTIAL_REPLAY, APR_HASH_KEY_STRING,
+                       capability_yes);
         }
     }
 
@@ -329,14 +206,12 @@ capabilities_headers_iterator_callback(v
     {
       if (svn_cstring_casecmp(key, SVN_DAV_ROOT_URI_HEADER) == 0)
         {
-          orc->session->repos_root = orc->session->session_url;
-          orc->session->repos_root.path = apr_pstrdup(orc->session->pool, val);
-          orc->session->repos_root_str =
+          session->repos_root = session->session_url;
+          session->repos_root.path = apr_pstrdup(session->pool, val);
+          session->repos_root_str =
             svn_urlpath__canonicalize(
-                apr_uri_unparse(orc->session->pool,
-                                &orc->session->repos_root,
-                                0),
-                orc->session->pool);
+                apr_uri_unparse(session->pool, &session->repos_root, 0),
+                session->pool);
         }
       else if (svn_cstring_casecmp(key, SVN_DAV_ME_RESOURCE_HEADER) == 0)
         {
@@ -345,44 +220,42 @@ capabilities_headers_iterator_callback(v
 
           if (!(ignore_v2_env_var
                 && apr_strnatcasecmp(ignore_v2_env_var, "yes") == 0))
-            orc->session->me_resource = apr_pstrdup(orc->session->pool, val);
+            session->me_resource = apr_pstrdup(session->pool, val);
 #else
-          orc->session->me_resource = apr_pstrdup(orc->session->pool, val);
+          session->me_resource = apr_pstrdup(session->pool, val);
 #endif
         }
       else if (svn_cstring_casecmp(key, SVN_DAV_REV_STUB_HEADER) == 0)
         {
-          orc->session->rev_stub = apr_pstrdup(orc->session->pool, val);
+          session->rev_stub = apr_pstrdup(session->pool, val);
         }
       else if (svn_cstring_casecmp(key, SVN_DAV_REV_ROOT_STUB_HEADER) == 0)
         {
-          orc->session->rev_root_stub = apr_pstrdup(orc->session->pool, val);
+          session->rev_root_stub = apr_pstrdup(session->pool, val);
         }
       else if (svn_cstring_casecmp(key, SVN_DAV_TXN_STUB_HEADER) == 0)
         {
-          orc->session->txn_stub = apr_pstrdup(orc->session->pool, val);
+          session->txn_stub = apr_pstrdup(session->pool, val);
         }
       else if (svn_cstring_casecmp(key, SVN_DAV_TXN_ROOT_STUB_HEADER) == 0)
         {
-          orc->session->txn_root_stub = apr_pstrdup(orc->session->pool, val);
+          session->txn_root_stub = apr_pstrdup(session->pool, val);
         }
       else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_STUB_HEADER) == 0)
         {
-          orc->session->vtxn_stub = apr_pstrdup(orc->session->pool, val);
+          session->vtxn_stub = apr_pstrdup(session->pool, val);
         }
       else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_ROOT_STUB_HEADER) == 0)
         {
-          orc->session->vtxn_root_stub = apr_pstrdup(orc->session->pool, val);
+          session->vtxn_root_stub = apr_pstrdup(session->pool, val);
         }
       else if (svn_cstring_casecmp(key, SVN_DAV_REPOS_UUID_HEADER) == 0)
         {
-          orc->session->uuid = apr_pstrdup(orc->session->pool, val);
+          session->uuid = apr_pstrdup(session->pool, val);
         }
       else if (svn_cstring_casecmp(key, SVN_DAV_YOUNGEST_REV_HEADER) == 0)
         {
-          struct svn_ra_serf__options_context_t *user_data =
-            orc->parser_ctx->user_data;
-          user_data->youngest_rev = SVN_STR_TO_REV(val);
+          opt_ctx->youngest_rev = SVN_STR_TO_REV(val);
         }
     }
 
@@ -391,7 +264,7 @@ capabilities_headers_iterator_callback(v
 
 
 /* A custom serf_response_handler_t which is mostly a wrapper around
-   svn_ra_serf__handle_xml_parser -- it just notices OPTIONS response
+   the expat-based response handler -- it just notices OPTIONS response
    headers first, before handing off to the xml parser.
    Implements svn_ra_serf__response_handler_t */
 static svn_error_t *
@@ -400,88 +273,117 @@ options_response_handler(serf_request_t 
                          void *baton,
                          apr_pool_t *pool)
 {
-  struct options_response_ctx_t *orc = baton;
-  serf_bucket_t *hdrs = serf_bucket_response_get_headers(response);
+  options_context_t *opt_ctx = baton;
+
+  if (!opt_ctx->headers_processed)
+    {
+      svn_ra_serf__session_t *session = opt_ctx->session;
+      serf_bucket_t *hdrs = serf_bucket_response_get_headers(response);
 
-  /* Start out assuming all capabilities are unsupported. */
-  apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY,
-               APR_HASH_KEY_STRING, capability_no);
-  apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_DEPTH,
-               APR_HASH_KEY_STRING, capability_no);
-  apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
-               APR_HASH_KEY_STRING, capability_no);
-  apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
-               APR_HASH_KEY_STRING, capability_no);
-  apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
-               APR_HASH_KEY_STRING, capability_no);
+      /* Start out assuming all capabilities are unsupported. */
+      apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY,
+                   APR_HASH_KEY_STRING, capability_no);
+      apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_DEPTH,
+                   APR_HASH_KEY_STRING, capability_no);
+      apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
+                   APR_HASH_KEY_STRING, capability_no);
+      apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
+                   APR_HASH_KEY_STRING, capability_no);
+      apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
+                   APR_HASH_KEY_STRING, capability_no);
+
+      /* Then see which ones we can discover. */
+      serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback,
+                             opt_ctx);
 
-  /* Then see which ones we can discover. */
-  serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback, orc);
+      opt_ctx->headers_processed = TRUE;
+    }
 
   /* Execute the 'real' response handler to XML-parse the repsonse body. */
-  return svn_ra_serf__handle_xml_parser(request, response,
-                                        orc->parser_ctx, pool);
+  return opt_ctx->inner_handler(request, response, opt_ctx->inner_baton, pool);
 }
 
 
-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)
+static svn_error_t *
+create_options_req(options_context_t **opt_ctx,
+                   svn_ra_serf__session_t *session,
+                   svn_ra_serf__connection_t *conn,
+                   apr_pool_t *pool)
 {
-  svn_ra_serf__options_context_t *new_ctx;
+  options_context_t *new_ctx;
+  svn_ra_serf__xml_context_t *xmlctx;
   svn_ra_serf__handler_t *handler;
-  svn_ra_serf__xml_parser_t *parser_ctx;
-  struct options_response_ctx_t *options_response_ctx;
 
   new_ctx = apr_pcalloc(pool, sizeof(*new_ctx));
-
   new_ctx->pool = pool;
-
-  new_ctx->acbuf = svn_stringbuf_create_empty(pool);
-
-  new_ctx->path = path;
-  new_ctx->youngest_rev = SVN_INVALID_REVNUM;
-
   new_ctx->session = session;
   new_ctx->conn = conn;
 
-  handler = apr_pcalloc(pool, sizeof(*handler));
+  new_ctx->youngest_rev = SVN_INVALID_REVNUM;
+
+  xmlctx = svn_ra_serf__xml_context_create(options_ttable,
+                                           NULL, options_closed, NULL,
+                                           new_ctx,
+                                           pool);
+  handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
 
   handler->method = "OPTIONS";
-  handler->path = path;
+  handler->path = session->session_url.path;
   handler->body_delegate = create_options_body;
   handler->body_type = "text/xml";
   handler->conn = conn;
   handler->session = session;
 
-  parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
-
-  parser_ctx->pool = pool;
-  parser_ctx->user_data = new_ctx;
-  parser_ctx->start = start_options;
-  parser_ctx->end = end_options;
-  parser_ctx->cdata = cdata_options;
-  parser_ctx->done = &new_ctx->done;
-  parser_ctx->status_code = &new_ctx->status_code;
-
-  options_response_ctx = apr_pcalloc(pool, sizeof(*options_response_ctx));
-  options_response_ctx->parser_ctx = parser_ctx;
-  options_response_ctx->session = session;
-  options_response_ctx->pool = pool;
+  new_ctx->handler = handler;
 
+  new_ctx->inner_handler = handler->response_handler;
+  new_ctx->inner_baton = handler->response_baton;
   handler->response_handler = options_response_handler;
-  handler->response_baton = options_response_ctx;
+  handler->response_baton = new_ctx;
+
+  *opt_ctx = new_ctx;
+
+  return SVN_NO_ERROR;
+}
+
 
-  svn_ra_serf__request_create(handler);
+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)
+{
+  svn_ra_serf__session_t *session = conn->session;
+  options_context_t *opt_ctx;
 
-  new_ctx->parser_ctx = parser_ctx;
+  SVN_ERR_ASSERT(SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session));
 
-  *opt_ctx = new_ctx;
+  SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool));
+  SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool));
+
+  *youngest = opt_ctx->youngest_rev;
+
+  return SVN_NO_ERROR;
+}
+
+
+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)
+{
+  svn_ra_serf__session_t *session = conn->session;
+  options_context_t *opt_ctx;
+
+  SVN_ERR_ASSERT(!SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session));
+
+  SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool));
+  SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool));
+
+  *activity_url = apr_pstrdup(result_pool, opt_ctx->activity_collection);
 
   return SVN_NO_ERROR;
+
 }
 
 
@@ -493,32 +395,29 @@ svn_ra_serf__exchange_capabilities(svn_r
                                    const char **corrected_url,
                                    apr_pool_t *pool)
 {
-  svn_ra_serf__options_context_t *opt_ctx;
+  options_context_t *opt_ctx;
   svn_error_t *err;
 
   /* This routine automatically fills in serf_sess->capabilities */
-  SVN_ERR(svn_ra_serf__create_options_req(&opt_ctx, serf_sess,
-                                          serf_sess->conns[0],
-                                          serf_sess->session_url.path, pool));
+  SVN_ERR(create_options_req(&opt_ctx, serf_sess, serf_sess->conns[0], pool));
 
-  err = svn_ra_serf__context_run_wait(
-            svn_ra_serf__get_options_done_ptr(opt_ctx), serf_sess, pool);
+  err = svn_ra_serf__context_run_one(opt_ctx->handler, pool);
 
   /* If our caller cares about server redirections, and our response
      carries such a thing, report as much.  We'll disregard ERR --
      it's most likely just a complaint about the response body not
      successfully parsing as XML or somesuch. */
-  if (corrected_url && (opt_ctx->status_code == 301))
+  if (corrected_url && (opt_ctx->handler->sline.code == 301))
     {
       svn_error_clear(err);
-      *corrected_url = opt_ctx->parser_ctx->location;
+      *corrected_url = opt_ctx->handler->location;
       return SVN_NO_ERROR;
     }
 
   return svn_error_compose_create(
-             svn_ra_serf__error_on_status(opt_ctx->status_code,
+             svn_ra_serf__error_on_status(opt_ctx->handler->sline.code,
                                           serf_sess->session_url.path,
-                                          opt_ctx->parser_ctx->location),
+                                          opt_ctx->handler->location),
              err);
 }
 
@@ -551,9 +450,7 @@ svn_ra_serf__has_capability(svn_ra_sessi
   cap_result = apr_hash_get(serf_sess->capabilities,
                             capability, APR_HASH_KEY_STRING);
 
-  /* Some capabilities depend on the repository as well as the server.
-     NOTE: svn_ra_neon__has_capability() has a very similar code block.  If
-     you change something here, check there as well. */
+  /* Some capabilities depend on the repository as well as the server. */
   if (cap_result == capability_server_yes)
     {
       if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0)

Modified: subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/property.c
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/property.c?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/property.c (original)
+++ subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/property.c Thu Aug 16 10:17:48 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;
+}