You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2012/05/16 22:32:54 UTC

svn commit: r1339349 [18/37] - in /subversion/branches/fix-rdump-editor: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/client-side/vim/ contrib/server-side/ notes/ notes/api-errat...

Modified: subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/replay.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/replay.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/replay.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/replay.c Wed May 16 20:32:43 2012
@@ -24,9 +24,6 @@
 
 
 #include <apr_uri.h>
-
-#include <expat.h>
-
 #include <serf.h>
 
 #include "svn_pools.h"
@@ -40,6 +37,8 @@
 #include "svn_path.h"
 #include "svn_private_config.h"
 
+#include "private/svn_string_private.h"
+
 #include "ra_serf.h"
 
 
@@ -83,8 +82,7 @@ typedef struct prop_info_t {
   const char *name;
   svn_boolean_t del_prop;
 
-  const char *data;
-  apr_size_t len;
+  svn_stringbuf_t *prop_value;
 
   replay_info_t *parent;
 } prop_info_t;
@@ -165,6 +163,7 @@ push_state(svn_ra_serf__xml_parser_t *pa
 
       info->pool = replay_ctx->dst_rev_pool;
       info->parent = parser->state->private;
+      info->prop_value = svn_stringbuf_create_empty(info->pool);
 
       parser->state->private = info;
     }
@@ -174,11 +173,11 @@ push_state(svn_ra_serf__xml_parser_t *pa
 
 static svn_error_t *
 start_replay(svn_ra_serf__xml_parser_t *parser,
-             void *userData,
              svn_ra_serf__dav_props_t name,
-             const char **attrs)
+             const char **attrs,
+             apr_pool_t *scratch_pool)
 {
-  replay_context_t *ctx = userData;
+  replay_context_t *ctx = parser->user_data;
   replay_state_e state;
 
   state = parser->state->current_state;
@@ -196,13 +195,12 @@ start_replay(svn_ra_serf__xml_parser_t *
       ctx->dst_rev_pool = svn_pool_create(ctx->src_rev_pool);
       ctx->file_pool = svn_pool_create(ctx->dst_rev_pool);
 
-      /* ### it would be nice to have a proper scratch_pool.  */
       SVN_ERR(svn_ra_serf__select_revprops(&ctx->props,
                                            ctx->revprop_target,
                                            ctx->revprop_rev,
                                            ctx->revs_props,
                                            ctx->dst_rev_pool,
-                                           ctx->dst_rev_pool));
+                                           scratch_pool));
 
       if (ctx->revstart_func)
         {
@@ -226,7 +224,7 @@ start_replay(svn_ra_serf__xml_parser_t *
 
       SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
                                                SVN_STR_TO_REV(rev),
-                                               ctx->dst_rev_pool));
+                                               scratch_pool));
     }
   else if (state == REPORT &&
            strcmp(name.name, "open-root") == 0)
@@ -271,7 +269,7 @@ start_replay(svn_ra_serf__xml_parser_t *
       info = push_state(parser, ctx, DELETE_ENTRY);
 
       SVN_ERR(ctx->editor->delete_entry(file_name, SVN_STR_TO_REV(rev),
-                                        info->baton, ctx->dst_rev_pool));
+                                        info->baton, scratch_pool));
 
       svn_ra_serf__xml_pop_state(parser);
     }
@@ -332,7 +330,7 @@ start_replay(svn_ra_serf__xml_parser_t *
     {
       replay_info_t *info = parser->state->private;
 
-      SVN_ERR(ctx->editor->close_directory(info->baton, ctx->dst_rev_pool));
+      SVN_ERR(ctx->editor->close_directory(info->baton, scratch_pool));
 
       svn_ra_serf__xml_pop_state(parser);
     }
@@ -424,8 +422,7 @@ start_replay(svn_ra_serf__xml_parser_t *
 
       checksum = svn_xml_get_attr_value("checksum", attrs);
 
-      SVN_ERR(ctx->editor->close_file(info->baton, checksum,
-                                      ctx->file_pool));
+      SVN_ERR(ctx->editor->close_file(info->baton, checksum, scratch_pool));
 
       svn_ra_serf__xml_pop_state(parser);
     }
@@ -471,14 +468,12 @@ start_replay(svn_ra_serf__xml_parser_t *
 
 static svn_error_t *
 end_replay(svn_ra_serf__xml_parser_t *parser,
-           void *userData,
-           svn_ra_serf__dav_props_t name)
+           svn_ra_serf__dav_props_t name,
+           apr_pool_t *scratch_pool)
 {
-  replay_context_t *ctx = userData;
+  replay_context_t *ctx = parser->user_data;
   replay_state_e state;
 
-  UNUSED_CTX(ctx);
-
   state = parser->state->current_state;
 
   if (state == REPORT &&
@@ -535,15 +530,17 @@ end_replay(svn_ra_serf__xml_parser_t *pa
         }
       else
         {
-          svn_string_t tmp_prop;
+          const svn_string_t *morph;
 
-          tmp_prop.data = info->data;
-          tmp_prop.len = info->len;
+          morph = svn_stringbuf__morph_into_string(info->prop_value);
+#ifdef SVN_DEBUG
+          info->prop_value = NULL;  /* morph killed the stringbuf.  */
+#endif
 
           if (strcmp(name.name, "change-file-prop") == 0)
-            prop_val = svn_base64_decode_string(&tmp_prop, ctx->file_pool);
+            prop_val = svn_base64_decode_string(morph, ctx->file_pool);
           else
-            prop_val = svn_base64_decode_string(&tmp_prop, ctx->dst_rev_pool);
+            prop_val = svn_base64_decode_string(morph, ctx->dst_rev_pool);
         }
 
       SVN_ERR(info->change(info->parent->baton, info->name, prop_val,
@@ -556,11 +553,11 @@ end_replay(svn_ra_serf__xml_parser_t *pa
 
 static svn_error_t *
 cdata_replay(svn_ra_serf__xml_parser_t *parser,
-             void *userData,
              const char *data,
-             apr_size_t len)
+             apr_size_t len,
+             apr_pool_t *scratch_pool)
 {
-  replay_context_t *replay_ctx = userData;
+  replay_context_t *replay_ctx = parser->user_data;
   replay_state_e state;
 
   UNUSED_CTX(replay_ctx);
@@ -584,8 +581,7 @@ cdata_replay(svn_ra_serf__xml_parser_t *
     {
       prop_info_t *info = parser->state->private;
 
-      svn_ra_serf__expand_string(&info->data, &info->len,
-                                 data, len, parser->state->pool);
+      svn_stringbuf_appendbytes(info->prop_value, data, len);
     }
 
   return SVN_NO_ERROR;
@@ -642,10 +638,6 @@ svn_ra_serf__replay(svn_ra_session_t *ra
   svn_ra_serf__xml_parser_t *parser_ctx;
   svn_error_t *err;
   const char *report_target;
-  /* We're not really interested in the status code here in replay, but
-     the XML parsing code will abort on error if it doesn't have a place
-     to store the response status code. */
-  int status_code;
 
   SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
 
@@ -662,6 +654,7 @@ svn_ra_serf__replay(svn_ra_session_t *ra
 
   handler = apr_pcalloc(pool, sizeof(*handler));
 
+  handler->handler_pool = pool;
   handler->method = "REPORT";
   handler->path = session->session_url_str;
   handler->body_delegate = create_replay_body;
@@ -677,7 +670,6 @@ svn_ra_serf__replay(svn_ra_session_t *ra
   parser_ctx->start = start_replay;
   parser_ctx->end = end_replay;
   parser_ctx->cdata = cdata_replay;
-  parser_ctx->status_code = &status_code;
   parser_ctx->done = &replay_ctx->done;
 
   handler->response_handler = svn_ra_serf__handle_xml_parser;
@@ -751,10 +743,6 @@ svn_ra_serf__replay_range(svn_ra_session
       svn_ra_serf__list_t *done_list;
       svn_ra_serf__list_t *done_reports = NULL;
       replay_context_t *replay_ctx;
-      /* We're not really interested in the status code here in replay, but
-         the XML parsing code will abort on error if it doesn't have a place
-         to store the response status code. */
-      int status_code;
 
       if (session->cancel_func)
         SVN_ERR(session->cancel_func(session->cancel_baton));
@@ -805,6 +793,7 @@ svn_ra_serf__replay_range(svn_ra_session
           /* Send the replay report request. */
           handler = apr_pcalloc(replay_ctx->src_rev_pool, sizeof(*handler));
 
+          handler->handler_pool = replay_ctx->src_rev_pool;
           handler->method = "REPORT";
           handler->path = session->session_url_str;
           handler->body_delegate = create_replay_body;
@@ -827,7 +816,6 @@ svn_ra_serf__replay_range(svn_ra_session
           parser_ctx->start = start_replay;
           parser_ctx->end = end_replay;
           parser_ctx->cdata = cdata_replay;
-          parser_ctx->status_code = &status_code;
           parser_ctx->done = &replay_ctx->done;
           parser_ctx->done_list = &done_reports;
           parser_ctx->done_item = &replay_ctx->done_item;
@@ -845,7 +833,26 @@ svn_ra_serf__replay_range(svn_ra_session
 
       /* Run the serf loop, send outgoing and process incoming requests.
          This request will block when there are no more requests to send or
-         responses to receive, so we have to be careful on our bookkeeping. */
+         responses to receive, so we have to be careful on our bookkeeping.
+
+         ### we should probably adjust this timeout. if we get (say) 3
+         ### requests completed, then we want to exit immediately rather
+         ### than block for a few seconds. that will allow us to clear up
+         ### those 3 requests. if we have queued all of our revisions,
+         ### then we may want to block until timeout since we really don't
+         ### have much work other than destroying memory. (though that
+         ### is important, as we could end up with 50 src_rev_pool pools)
+
+         ### idea: when a revision is marked DONE, we can probably destroy
+         ### most of the memory. that will reduce pressue to have serf
+         ### return control to us, to complete the major memory disposal.
+
+         ### theoretically, we should use an iterpool here, but it turns
+         ### out that serf doesn't even use the pool param. if we grow
+         ### an iterpool in this loop for other purposes, then yeah: go
+         ### ahead and apply it here, too, in case serf eventually uses
+         ### that parameter.
+      */
       status = serf_context_run(session->context, session->timeout,
                                 pool);
 

Modified: subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/serf.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/serf.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/serf.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/serf.c Wed May 16 20:32:43 2012
@@ -27,9 +27,6 @@
 #include <apr_want.h>
 
 #include <apr_uri.h>
-
-#include <expat.h>
-
 #include <serf.h>
 
 #include "svn_pools.h"
@@ -48,6 +45,7 @@
 #include "private/svn_dav_protocol.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_fspath.h"
+#include "private/svn_subr_private.h"
 #include "svn_private_config.h"
 
 #include "ra_serf.h"
@@ -397,16 +395,16 @@ svn_ra_serf__open(svn_ra_session_t *sess
 
   serf_sess->conns[0] = apr_pcalloc(serf_sess->pool,
                                     sizeof(*serf_sess->conns[0]));
+  serf_sess->conns[0]->http10 = TRUE;  /* until we confirm HTTP/1.1  */
+  serf_sess->conns[0]->http10 = FALSE; /* ### don't change behavior yet  */
   serf_sess->conns[0]->bkt_alloc =
           serf_bucket_allocator_create(serf_sess->pool, NULL, NULL);
   serf_sess->conns[0]->session = serf_sess;
   serf_sess->conns[0]->last_status_code = -1;
 
   serf_sess->conns[0]->using_ssl = serf_sess->using_ssl;
-  serf_sess->conns[0]->server_cert_failures = 0;
   serf_sess->conns[0]->using_compression = serf_sess->using_compression;
   serf_sess->conns[0]->hostname = url.hostname;
-  serf_sess->conns[0]->useragent = NULL;
 
   /* create the user agent string */
   if (callbacks->get_client_string)
@@ -497,13 +495,10 @@ svn_ra_serf__get_latest_revnum(svn_ra_se
                                svn_revnum_t *latest_revnum,
                                apr_pool_t *pool)
 {
-  const char *relative_url, *basecoll_url;
   svn_ra_serf__session_t *session = ra_session->priv;
 
-  return svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url, session,
-                                        NULL, session->session_url.path,
-                                        SVN_INVALID_REVNUM, latest_revnum,
-                                        pool);
+  return svn_error_trace(svn_ra_serf__get_youngest_revnum(
+                           latest_revnum, session, pool));
 }
 
 static svn_error_t *
@@ -532,6 +527,7 @@ svn_ra_serf__rev_proplist(svn_ra_session
       SVN_ERR(svn_ra_serf__discover_vcc(&propfind_path, session, NULL, pool));
     }
 
+  /* ### fix: fetch hash of *just* the PATH@REV props. no nested hash.  */
   SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0],
                                       propfind_path, rev, "0", all_props,
                                       pool, pool));
@@ -559,67 +555,38 @@ svn_ra_serf__rev_prop(svn_ra_session_t *
 }
 
 static svn_error_t *
-fetch_path_props(svn_ra_serf__propfind_context_t **ret_prop_ctx,
-                 apr_hash_t **ret_props,
-                 const char **ret_path,
-                 svn_revnum_t *ret_revision,
+fetch_path_props(apr_hash_t **props,
                  svn_ra_serf__session_t *session,
-                 const char *rel_path,
+                 const char *session_relpath,
                  svn_revnum_t revision,
                  const svn_ra_serf__dav_props_t *desired_props,
-                 apr_pool_t *pool)
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
 {
-  svn_ra_serf__propfind_context_t *prop_ctx;
-  apr_hash_t *props;
-  const char *path;
+  const char *url;
 
-  path = session->session_url.path;
+  url = session->session_url.path;
 
   /* If we have a relative path, append it. */
-  if (rel_path)
-    {
-      path = svn_path_url_add_component2(path, rel_path, pool);
-    }
+  if (session_relpath)
+    url = svn_path_url_add_component2(url, session_relpath, scratch_pool);
 
-  props = apr_hash_make(pool);
-
-  /* If we were given a specific revision, we have to fetch the VCC and
-   * do a PROPFIND off of that.
-   */
-  if (!SVN_IS_VALID_REVNUM(revision))
-    {
-      SVN_ERR(svn_ra_serf__deliver_props(&prop_ctx, props, session,
-                                         session->conns[0], path, revision,
-                                         "0", desired_props, NULL,
-                                         pool));
-    }
-  else
-    {
-      const char *relative_url, *basecoll_url;
-
-      SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url,
-                                             session, NULL, path,
-                                             revision, NULL, pool));
-
-      /* We will try again with our new path; however, we're now
-       * technically an unversioned resource because we are accessing
-       * the revision's baseline-collection.
-       */
-      path = svn_path_url_add_component2(basecoll_url, relative_url, pool);
-      revision = SVN_INVALID_REVNUM;
-      SVN_ERR(svn_ra_serf__deliver_props(&prop_ctx, props, session,
-                                         session->conns[0], path, revision,
-                                         "0", desired_props, NULL,
-                                         pool));
-    }
-
-  /* ### switch to svn_ra_serf__retrieve_props?  */
-  SVN_ERR(svn_ra_serf__wait_for_props(prop_ctx, session, pool));
-
-  *ret_path = path;
-  *ret_prop_ctx = prop_ctx;
-  *ret_props = props;
-  *ret_revision = revision;
+  /* If we were given a specific revision, get a URL that refers to that
+     specific revision (rather than floating with HEAD).  */
+  if (SVN_IS_VALID_REVNUM(revision))
+    {
+      SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */,
+                                          session, NULL /* conn */,
+                                          url, revision,
+                                          scratch_pool, scratch_pool));
+    }
+
+  /* URL is stable, so we use SVN_INVALID_REVNUM since it is now irrelevant.
+     Or we started with SVN_INVALID_REVNUM and URL may be floating.  */
+  SVN_ERR(svn_ra_serf__fetch_node_props(props, session->conns[0],
+                                        url, SVN_INVALID_REVNUM,
+                                        desired_props,
+                                        result_pool, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -633,13 +600,10 @@ svn_ra_serf__check_path(svn_ra_session_t
 {
   svn_ra_serf__session_t *session = ra_session->priv;
   apr_hash_t *props;
-  svn_ra_serf__propfind_context_t *prop_ctx;
-  const char *path;
-  svn_revnum_t fetched_rev;
 
-  svn_error_t *err = fetch_path_props(&prop_ctx, &props, &path, &fetched_rev,
-                                      session, rel_path,
-                                      revision, check_path_props, pool);
+  svn_error_t *err = fetch_path_props(&props, session, rel_path,
+                                      revision, check_path_props,
+                                      pool, pool);
 
   if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
     {
@@ -648,11 +612,14 @@ svn_ra_serf__check_path(svn_ra_session_t
     }
   else
     {
+      svn_kind_t res_kind;
+
       /* Any other error, raise to caller. */
       if (err)
-        return err;
+        return svn_error_trace(err);
 
-      SVN_ERR(svn_ra_serf__get_resource_type(kind, props, path, fetched_rev));
+      SVN_ERR(svn_ra_serf__get_resource_type(&res_kind, props));
+      *kind = svn__node_kind_from_kind(res_kind);
     }
 
   return SVN_NO_ERROR;
@@ -871,17 +838,14 @@ svn_ra_serf__stat(svn_ra_session_t *ra_s
 {
   svn_ra_serf__session_t *session = ra_session->priv;
   apr_hash_t *props;
-  svn_ra_serf__propfind_context_t *prop_ctx;
-  const char *path;
-  svn_revnum_t fetched_rev;
   svn_error_t *err;
   struct dirent_walker_baton_t dwb;
   svn_tristate_t deadprop_count = svn_tristate_unknown;
 
-  err = fetch_path_props(&prop_ctx, &props, &path, &fetched_rev,
+  err = fetch_path_props(&props,
                          session, rel_path, revision,
                          get_dirent_props(SVN_DIRENT_ALL, session, pool),
-                         pool);
+                         pool, pool);
   if (err)
     {
       if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
@@ -897,9 +861,7 @@ svn_ra_serf__stat(svn_ra_session_t *ra_s
   dwb.entry = apr_pcalloc(pool, sizeof(*dwb.entry));
   dwb.supports_deadprop_count = &deadprop_count;
   dwb.result_pool = pool;
-  SVN_ERR(svn_ra_serf__walk_all_props(props, path, fetched_rev,
-                                      dirent_walker, &dwb,
-                                      pool));
+  SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool));
 
   if (deadprop_count == svn_tristate_false
       && session->supports_deadprop_count == svn_tristate_unknown
@@ -909,14 +871,12 @@ svn_ra_serf__stat(svn_ra_session_t *ra_s
          information */
       session->supports_deadprop_count = svn_tristate_false;
 
-      SVN_ERR(fetch_path_props(&prop_ctx, &props, &path, &fetched_rev,
-                               session, rel_path, fetched_rev,
+      SVN_ERR(fetch_path_props(&props,
+                               session, rel_path, SVN_INVALID_REVNUM,
                                get_dirent_props(SVN_DIRENT_ALL, session, pool),
-                               pool));
+                               pool, pool));
 
-      SVN_ERR(svn_ra_serf__walk_all_props(props, path, fetched_rev,
-                                      dirent_walker, &dwb,
-                                      pool));
+      SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool));
     }
 
   if (deadprop_count != svn_tristate_unknown)
@@ -932,15 +892,13 @@ svn_ra_serf__stat(svn_ra_session_t *ra_s
  * SVN_ERR_FS_NOT_DIRECTORY if not.
  */
 static svn_error_t *
-resource_is_directory(apr_hash_t *props,
-                      const char *path,
-                      svn_revnum_t revision)
+resource_is_directory(apr_hash_t *props)
 {
-  svn_node_kind_t kind;
+  svn_kind_t kind;
 
-  SVN_ERR(svn_ra_serf__get_resource_type(&kind, props, path, revision));
+  SVN_ERR(svn_ra_serf__get_resource_type(&kind, props));
 
-  if (kind != svn_node_dir)
+  if (kind != svn_kind_dir)
     {
       return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
                               _("Can't get entries of non-directory"));
@@ -975,34 +933,37 @@ svn_ra_serf__get_dir(svn_ra_session_t *r
      public url. */
   if (SVN_IS_VALID_REVNUM(revision) || fetched_rev)
     {
-      const char *relative_url, *basecoll_url;
-
-      SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url,
-                                             session, NULL, path, revision,
-                                             fetched_rev, pool));
-
-      path = svn_path_url_add_component2(basecoll_url, relative_url, pool);
+      SVN_ERR(svn_ra_serf__get_stable_url(&path, fetched_rev,
+                                          session, NULL /* conn */,
+                                          path, revision,
+                                          pool, pool));
       revision = SVN_INVALID_REVNUM;
     }
+  /* REVISION is always SVN_INVALID_REVNUM  */
+  SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision));
 
   /* If we're asked for children, fetch them now. */
   if (dirents)
     {
       struct path_dirent_visitor_t dirent_walk;
       apr_hash_t *props;
+      const char *rtype;
 
       /* Always request node kind to check that path is really a
        * directory.
        */
       dirent_fields |= SVN_DIRENT_KIND;
       SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0],
-                                          path, revision, "1",
+                                          path, SVN_INVALID_REVNUM, "1",
                                           get_dirent_props(dirent_fields,
                                                            session, pool),
                                           pool, pool));
 
       /* Check if the path is really a directory. */
-      SVN_ERR(resource_is_directory(props, path, revision));
+      rtype = svn_ra_serf__get_prop(props, path, "DAV:", "resourcetype");
+      if (rtype == NULL || strcmp(rtype, "collection") != 0)
+        return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
+                                _("Can't get entries of non-directory"));
 
       /* We're going to create two hashes to help the walker along.
        * We're going to return the 2nd one back to the caller as it
@@ -1014,8 +975,9 @@ svn_ra_serf__get_dir(svn_ra_session_t *r
       dirent_walk.supports_deadprop_count = svn_tristate_unknown;
       dirent_walk.result_pool = pool;
 
-      SVN_ERR(svn_ra_serf__walk_all_paths(props, revision, path_dirent_walker,
-                                          &dirent_walk, pool));
+      SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM,
+                                          path_dirent_walker, &dirent_walk,
+                                          pool));
 
       if (dirent_walk.supports_deadprop_count == svn_tristate_false
           && session->supports_deadprop_count == svn_tristate_unknown
@@ -1026,7 +988,7 @@ svn_ra_serf__get_dir(svn_ra_session_t *r
           session->supports_deadprop_count = svn_tristate_false;
           SVN_ERR(svn_ra_serf__retrieve_props(&props, session,
                                               session->conns[0],
-                                              path, revision, "1",
+                                              path, SVN_INVALID_REVNUM, "1",
                                               get_dirent_props(dirent_fields,
                                                                session, pool),
                                               pool, pool));
@@ -1034,7 +996,7 @@ svn_ra_serf__get_dir(svn_ra_session_t *r
           SVN_ERR(svn_hash__clear(dirent_walk.full_paths, pool));
           SVN_ERR(svn_hash__clear(dirent_walk.base_paths, pool));
 
-          SVN_ERR(svn_ra_serf__walk_all_paths(props, revision,
+          SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM,
                                               path_dirent_walker,
                                               &dirent_walk, pool));
         }
@@ -1050,20 +1012,23 @@ svn_ra_serf__get_dir(svn_ra_session_t *r
     {
       apr_hash_t *props;
 
-      SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0],
-                                          path, revision, "0", all_props,
-                                          pool, pool));
+      SVN_ERR(svn_ra_serf__fetch_node_props(&props, session->conns[0],
+                                            path, SVN_INVALID_REVNUM,
+                                            all_props,
+                                            pool, pool));
+
       /* Check if the path is really a directory. */
-      SVN_ERR(resource_is_directory(props, path, revision));
+      SVN_ERR(resource_is_directory(props));
 
-      SVN_ERR(svn_ra_serf__flatten_props(ret_props, props, path, revision,
-                                         pool, pool));
+      /* ### flatten_props() does not copy PROPVALUE, but fetch_node_props()
+         ### put them into POOL, so we're okay.  */
+      SVN_ERR(svn_ra_serf__flatten_props(ret_props, props, pool, pool));
     }
 
   return SVN_NO_ERROR;
 }
 
-static svn_error_t *
+svn_error_t *
 svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session,
                             const char **url,
                             apr_pool_t *pool)
@@ -1196,6 +1161,7 @@ svn_ra_serf__init(const svn_version_t *l
       || serf_minor < SERF_MINOR_VERSION)
     {
       return svn_error_createf(
+         /* ### should return a unique error  */
          SVN_ERR_VERSION_MISMATCH, NULL,
          _("ra_serf was compiled for serf %d.%d.%d but loaded "
            "an incompatible %d.%d.%d library"),

Modified: subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/update.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/update.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/libsvn_ra_serf/update.c Wed May 16 20:32:43 2012
@@ -43,6 +43,7 @@
 #include "svn_private_config.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_fspath.h"
+#include "private/svn_string_private.h"
 
 #include "ra_serf.h"
 #include "../libsvn_ra/ra_loader.h"
@@ -125,17 +126,10 @@ typedef struct report_dir_t
   /* Our base revision - SVN_INVALID_REVNUM if we're adding this dir. */
   svn_revnum_t base_rev;
 
-  /* The target revision we're retrieving. */
-  svn_revnum_t target_rev;
-
   /* controlling dir baton - this is only created in open_dir() */
   void *dir_baton;
   apr_pool_t *dir_baton_pool;
 
-  /* Our master update editor and baton. */
-  const svn_delta_editor_t *update_editor;
-  void *update_baton;
-
   /* How many references to this directory do we still have open? */
   apr_size_t ref_count;
 
@@ -199,9 +193,6 @@ typedef struct report_info_t
   /* Our base revision - SVN_INVALID_REVNUM if we're adding this file. */
   svn_revnum_t base_rev;
 
-  /* The target revision we're retrieving. */
-  svn_revnum_t target_rev;
-
   /* our delta base, if present (NULL if we're adding the file) */
   const char *delta_base;
 
@@ -236,13 +227,16 @@ typedef struct report_info_t
   /* Checksum for close_file */
   const char *final_checksum;
 
+  /* Stream containing file contents already cached in the working
+     copy (which may be used to avoid a GET request for the same). */
+  svn_stream_t *cached_contents;
+
   /* temporary property for this file which is currently being parsed
    * It will eventually be stored in our parent directory's property hash.
    */
   const char *prop_ns;
   const char *prop_name;
-  const char *prop_val;
-  apr_size_t prop_val_len;
+  svn_stringbuf_t *prop_value;
   const char *prop_encoding;
 } report_info_t;
 
@@ -252,6 +246,9 @@ typedef struct report_info_t
  */
 typedef struct report_fetch_t {
 
+  /* The handler representing this particular fetch.  */
+  svn_ra_serf__handler_t *handler;
+
   /* The session we should use to fetch the file. */
   svn_ra_serf__session_t *sess;
 
@@ -349,7 +346,7 @@ struct report_context_t {
   /* number of pending PROPFIND requests */
   unsigned int active_propfinds;
 
-  /* completed PROPFIND requests (contains propfind_context_t) */
+  /* completed PROPFIND requests (contains svn_ra_serf__propfind_context_t) */
   svn_ra_serf__list_t *done_propfinds;
 
   /* list of files that only have prop changes (contains report_info_t) */
@@ -397,6 +394,7 @@ push_state(svn_ra_serf__xml_parser_t *pa
       new_info = apr_pcalloc(info_parent_pool, sizeof(*new_info));
       new_info->pool = svn_pool_create(info_parent_pool);
       new_info->lock_token = NULL;
+      new_info->prop_value = svn_stringbuf_create_empty(new_info->pool);
 
       new_info->dir = apr_pcalloc(new_info->pool, sizeof(*new_info->dir));
       new_info->dir->pool = new_info->pool;
@@ -406,9 +404,6 @@ push_state(svn_ra_serf__xml_parser_t *pa
       new_info->props = new_info->dir->props;
       new_info->dir->removed_props = apr_hash_make(new_info->pool);
 
-      /* Point to the update_editor */
-      new_info->dir->update_editor = ctx->update_editor;
-      new_info->dir->update_baton = ctx->update_baton;
       new_info->dir->report_context = ctx;
 
       if (info)
@@ -441,6 +436,7 @@ push_state(svn_ra_serf__xml_parser_t *pa
       new_info->file_baton = NULL;
       new_info->lock_token = NULL;
       new_info->fetch_file = FALSE;
+      new_info->prop_value = svn_stringbuf_create_empty(new_info->pool);
 
       /* Point at our parent's directory state. */
       new_info->dir = info->dir;
@@ -465,7 +461,7 @@ set_file_props(void *baton,
                apr_pool_t *scratch_pool)
 {
   report_info_t *info = baton;
-  const svn_delta_editor_t *editor = info->dir->update_editor;
+  const svn_delta_editor_t *editor = info->dir->report_context->update_editor;
   const char *prop_name;
 
   if (strcmp(name, "md5-checksum") == 0
@@ -490,7 +486,7 @@ set_dir_props(void *baton,
               apr_pool_t *scratch_pool)
 {
   report_dir_t *dir = baton;
-  const svn_delta_editor_t *editor = dir->update_editor;
+  const svn_delta_editor_t *editor = dir->report_context->update_editor;
   const char *prop_name;
 
   prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
@@ -511,7 +507,7 @@ remove_file_props(void *baton,
                   apr_pool_t *scratch_pool)
 {
   report_info_t *info = baton;
-  const svn_delta_editor_t *editor = info->dir->update_editor;
+  const svn_delta_editor_t *editor = info->dir->report_context->update_editor;
   const char *prop_name;
 
   prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
@@ -532,7 +528,7 @@ remove_dir_props(void *baton,
                  apr_pool_t *scratch_pool)
 {
   report_dir_t *dir = baton;
-  const svn_delta_editor_t *editor = dir->update_editor;
+  const svn_delta_editor_t *editor = dir->report_context->update_editor;
   const char *prop_name;
 
   prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
@@ -550,6 +546,8 @@ remove_dir_props(void *baton,
 static svn_error_t*
 open_dir(report_dir_t *dir)
 {
+  report_context_t *ctx = dir->report_context;
+
   /* if we're already open, return now */
   if (dir->dir_baton)
     {
@@ -560,16 +558,16 @@ open_dir(report_dir_t *dir)
     {
       dir->dir_baton_pool = svn_pool_create(dir->pool);
 
-      if (dir->report_context->destination &&
-          dir->report_context->sess->wc_callbacks->invalidate_wc_props)
+      if (ctx->destination
+          && ctx->sess->wc_callbacks->invalidate_wc_props)
         {
-          SVN_ERR(dir->report_context->sess->wc_callbacks->invalidate_wc_props(
-                      dir->report_context->sess->wc_callback_baton,
-                      dir->report_context->update_target,
+          SVN_ERR(ctx->sess->wc_callbacks->invalidate_wc_props(
+                      ctx->sess->wc_callback_baton,
+                      ctx->update_target,
                       SVN_RA_SERF__WC_CHECKED_IN_URL, dir->pool));
         }
 
-      SVN_ERR(dir->update_editor->open_root(dir->update_baton, dir->base_rev,
+      SVN_ERR(ctx->update_editor->open_root(ctx->update_baton, dir->base_rev,
                                             dir->dir_baton_pool,
                                             &dir->dir_baton));
     }
@@ -581,7 +579,7 @@ open_dir(report_dir_t *dir)
 
       if (SVN_IS_VALID_REVNUM(dir->base_rev))
         {
-          SVN_ERR(dir->update_editor->open_directory(dir->name,
+          SVN_ERR(ctx->update_editor->open_directory(dir->name,
                                                      dir->parent_dir->dir_baton,
                                                      dir->base_rev,
                                                      dir->dir_baton_pool,
@@ -589,7 +587,7 @@ open_dir(report_dir_t *dir)
         }
       else
         {
-          SVN_ERR(dir->update_editor->add_directory(dir->name,
+          SVN_ERR(ctx->update_editor->add_directory(dir->name,
                                                     dir->parent_dir->dir_baton,
                                                     NULL, SVN_INVALID_REVNUM,
                                                     dir->dir_baton_pool,
@@ -623,12 +621,13 @@ close_dir(report_dir_t *dir)
   if (dir->fetch_props)
     {
       SVN_ERR(svn_ra_serf__walk_all_props(dir->props, dir->url,
-                                          dir->target_rev,
+                                          dir->report_context->target_rev,
                                           set_dir_props, dir,
                                           scratch_pool));
     }
 
-  SVN_ERR(dir->update_editor->close_directory(dir->dir_baton, scratch_pool));
+  SVN_ERR(dir->report_context->update_editor->close_directory(
+            dir->dir_baton, scratch_pool));
 
   /* remove us from our parent's children list */
   if (dir->parent_dir)
@@ -692,7 +691,7 @@ check_lock(report_info_t *info)
   const char *lock_val;
 
   lock_val = svn_ra_serf__get_ver_prop(info->props, info->url,
-                                       info->target_rev,
+                                       info->dir->report_context->target_rev,
                                        "DAV:", "lockdiscovery");
 
   if (lock_val)
@@ -799,6 +798,105 @@ error_fetch(serf_request_t *request,
   return err;
 }
 
+/* Wield the editor referenced by INFO to open (or add) the file
+   file also associated with INFO, setting properties on the file and
+   calling the editor's apply_textdelta() function on it if necessary
+   (or if FORCE_APPLY_TEXTDELTA is set).
+
+   Callers will probably want to also see the function that serves
+   the opposite purpose of this one, close_updated_file().  */
+static svn_error_t *
+open_updated_file(report_info_t *info,
+                  svn_boolean_t force_apply_textdelta,
+                  apr_pool_t *scratch_pool)
+{
+  report_context_t *ctx = info->dir->report_context;
+  const svn_delta_editor_t *update_editor = ctx->update_editor;
+
+  /* Ensure our parent is open. */
+  SVN_ERR(open_dir(info->dir));
+  info->editor_pool = svn_pool_create(info->dir->dir_baton_pool);
+
+  /* Expand our full name now if we haven't done so yet. */
+  if (!info->name)
+    {
+      info->name = svn_relpath_join(info->dir->name, info->base_name,
+                                    info->editor_pool);
+    }
+
+  /* Open (or add) the file. */
+  if (SVN_IS_VALID_REVNUM(info->base_rev))
+    {
+      SVN_ERR(update_editor->open_file(info->name,
+                                       info->dir->dir_baton,
+                                       info->base_rev,
+                                       info->editor_pool,
+                                       &info->file_baton));
+    }
+  else
+    {
+      SVN_ERR(update_editor->add_file(info->name,
+                                      info->dir->dir_baton,
+                                      info->copyfrom_path,
+                                      info->copyfrom_rev,
+                                      info->editor_pool,
+                                      &info->file_baton));
+    }
+
+  /* Check for lock information. */
+  if (info->lock_token)
+    check_lock(info);
+
+  /* Set all of the properties we received */
+  SVN_ERR(svn_ra_serf__walk_all_props(info->props,
+                                      info->base_name,
+                                      info->base_rev,
+                                      set_file_props, info,
+                                      scratch_pool));
+  SVN_ERR(svn_ra_serf__walk_all_props(info->dir->removed_props,
+                                      info->base_name,
+                                      info->base_rev,
+                                      remove_file_props, info,
+                                      scratch_pool));
+  if (info->fetch_props)
+    {
+      SVN_ERR(svn_ra_serf__walk_all_props(info->props,
+                                          info->url,
+                                          ctx->target_rev,
+                                          set_file_props, info,
+                                          scratch_pool));
+    }
+
+  /* Get (maybe) a textdelta window handler for transmitting file
+     content changes. */
+  if (info->fetch_file || force_apply_textdelta)
+    {
+      SVN_ERR(update_editor->apply_textdelta(info->file_baton,
+                                             info->base_checksum,
+                                             info->editor_pool,
+                                             &info->textdelta,
+                                             &info->textdelta_baton));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Close the file associated with INFO->file_baton, and cleanup other
+   bits of that structure managed by open_updated_file(). */
+static svn_error_t *
+close_updated_file(report_info_t *info,
+                   apr_pool_t *scratch_pool)
+{
+  /* Close the file via the editor. */
+  SVN_ERR(info->dir->report_context->update_editor->close_file(
+            info->file_baton, info->final_checksum, scratch_pool));
+
+  /* We're done with our editor pool. */
+  svn_pool_destroy(info->editor_pool);
+
+  return SVN_NO_ERROR;
+}
+
 /* Implements svn_ra_serf__response_handler_t */
 static svn_error_t *
 handle_fetch(serf_request_t *request,
@@ -811,7 +909,9 @@ handle_fetch(serf_request_t *request,
   apr_status_t status;
   report_fetch_t *fetch_ctx = handler_baton;
   svn_error_t *err;
-  serf_status_line sl;
+
+  /* ### new field. make sure we didn't miss some initialization.  */
+  SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
 
   if (fetch_ctx->read_headers == FALSE)
     {
@@ -823,50 +923,8 @@ handle_fetch(serf_request_t *request,
       val = serf_bucket_headers_get(hdrs, "Content-Type");
       info = fetch_ctx->info;
 
-      err = open_dir(info->dir);
-      if (err)
-        {
-          return error_fetch(request, fetch_ctx, err);
-        }
-
-      info->editor_pool = svn_pool_create(info->dir->dir_baton_pool);
-
-      /* Expand our full name now if we haven't done so yet. */
-      if (!info->name)
-        {
-          info->name = svn_relpath_join(info->dir->name, info->base_name,
-                                        info->editor_pool);
-        }
-
-      if (SVN_IS_VALID_REVNUM(info->base_rev))
-        {
-          err = info->dir->update_editor->open_file(info->name,
-                                                    info->dir->dir_baton,
-                                                    info->base_rev,
-                                                    info->editor_pool,
-                                                    &info->file_baton);
-        }
-      else
-        {
-          err = info->dir->update_editor->add_file(info->name,
-                                                   info->dir->dir_baton,
-                                                   info->copyfrom_path,
-                                                   info->copyfrom_rev,
-                                                   info->editor_pool,
-                                                   &info->file_baton);
-        }
-
-      if (err)
-        {
-          return error_fetch(request, fetch_ctx, err);
-        }
-
-      err = info->dir->update_editor->apply_textdelta(info->file_baton,
-                                                      info->base_checksum,
-                                                      info->editor_pool,
-                                                      &info->textdelta,
-                                                      &info->textdelta_baton);
-
+      /* Open the file for editing. */
+      err = open_updated_file(info, FALSE, info->pool);
       if (err)
         {
           return error_fetch(request, fetch_ctx, err);
@@ -889,16 +947,12 @@ handle_fetch(serf_request_t *request,
 
   /* If the error code wasn't 200, something went wrong. Don't use the returned
      data as its probably an error message. Just bail out instead. */
-  status = serf_bucket_response_status(response, &sl);
-  if (SERF_BUCKET_READ_ERROR(status))
-    {
-      return svn_error_wrap_apr(status, NULL);
-    }
-  if (sl.code != 200)
+  if (fetch_ctx->handler->sline.code != 200)
     {
       err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
                               _("GET request failed: %d %s"),
-                              sl.code, sl.reason);
+                              fetch_ctx->handler->sline.code,
+                              fetch_ctx->handler->sline.reason);
       return error_fetch(request, fetch_ctx, err);
     }
 
@@ -981,51 +1035,17 @@ handle_fetch(serf_request_t *request,
         {
           report_info_t *info = fetch_ctx->info;
 
-          /* ### this doesn't feel quite right. but it gets tossed at the
-             ### end of this block, so it will work for now.  */
-          apr_pool_t *scratch_pool = info->editor_pool;
-
           if (fetch_ctx->delta_stream)
             err = svn_error_trace(svn_stream_close(fetch_ctx->delta_stream));
           else
             err = svn_error_trace(info->textdelta(NULL,
                                                   info->textdelta_baton));
-
           if (err)
             {
               return error_fetch(request, fetch_ctx, err);
             }
 
-          if (info->lock_token)
-            check_lock(info);
-
-          /* set all of the properties we received */
-          err = svn_ra_serf__walk_all_props(info->props,
-                                            info->base_name,
-                                            info->base_rev,
-                                            set_file_props, info,
-                                            scratch_pool);
-
-          if (!err)
-            err = svn_ra_serf__walk_all_props(info->dir->removed_props,
-                                              info->base_name,
-                                              info->base_rev,
-                                              remove_file_props, info,
-                                              scratch_pool);
-          if (!err && info->fetch_props)
-            {
-              err = svn_ra_serf__walk_all_props(info->props,
-                                                info->url,
-                                                info->target_rev,
-                                                set_file_props, info,
-                                                scratch_pool);
-            }
-
-          if (!err)
-            err = info->dir->update_editor->close_file(info->file_baton,
-                                                       info->final_checksum,
-                                                       scratch_pool);
-
+          err = close_updated_file(info, info->pool);
           if (err)
             {
               return svn_error_trace(error_fetch(request, fetch_ctx, err));
@@ -1037,8 +1057,7 @@ handle_fetch(serf_request_t *request,
           fetch_ctx->done_item.next = *fetch_ctx->done_list;
           *fetch_ctx->done_list = &fetch_ctx->done_item;
 
-          /* We're done with our pools. */
-          svn_pool_destroy(info->editor_pool);
+          /* We're done with our pool. */
           svn_pool_destroy(info->pool);
 
           if (status)
@@ -1060,26 +1079,22 @@ handle_stream(serf_request_t *request,
               apr_pool_t *pool)
 {
   report_fetch_t *fetch_ctx = handler_baton;
-  serf_status_line sl;
   const char *location;
   svn_error_t *err;
   apr_status_t status;
 
-  status = serf_bucket_response_status(response, &sl);
-  if (SERF_BUCKET_READ_ERROR(status))
-    {
-      return svn_error_wrap_apr(status, NULL);
-    }
+  /* ### new field. make sure we didn't miss some initialization.  */
+  SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
 
   /* Woo-hoo.  Nothing here to see.  */
   location = svn_ra_serf__response_get_location(response, pool);
 
-  err = svn_ra_serf__error_on_status(sl.code,
+  err = svn_ra_serf__error_on_status(fetch_ctx->handler->sline.code,
                                      fetch_ctx->info->name,
                                      location);
   if (err)
     {
-      fetch_ctx->done = TRUE;
+      fetch_ctx->handler->done = TRUE;
 
       err = svn_error_compose_create(
                   err,
@@ -1155,78 +1170,100 @@ static svn_error_t *
 handle_propchange_only(report_info_t *info,
                        apr_pool_t *scratch_pool)
 {
-  /* Ensure our parent is open. */
-  SVN_ERR(open_dir(info->dir));
+  /* Open the file for editing (without forcing an apply_textdelta),
+     pass along any propchanges we've recorded for it, and then close
+     the file. */
+  SVN_ERR(open_updated_file(info, FALSE, scratch_pool));
+  SVN_ERR(close_updated_file(info, scratch_pool));
+  
+  /* We're done with our pool. */
+  svn_pool_destroy(info->pool);
 
-  info->editor_pool = svn_pool_create(info->dir->dir_baton_pool);
+  info->dir->ref_count--;
 
-  /* Expand our full name now if we haven't done so yet. */
-  if (!info->name)
-    {
-      info->name = svn_relpath_join(info->dir->name, info->base_name,
-                                    info->editor_pool);
-    }
+  return SVN_NO_ERROR;
+}
 
-  if (SVN_IS_VALID_REVNUM(info->base_rev))
-    {
-      SVN_ERR(info->dir->update_editor->open_file(info->name,
-                                                  info->dir->dir_baton,
-                                                  info->base_rev,
-                                                  info->editor_pool,
-                                                  &info->file_baton));
-    }
-  else
-    {
-      SVN_ERR(info->dir->update_editor->add_file(info->name,
-                                                 info->dir->dir_baton,
-                                                 info->copyfrom_path,
-                                                 info->copyfrom_rev,
-                                                 info->editor_pool,
-                                                 &info->file_baton));
-    }
+/* "Fetch" a file whose contents were made available via the
+   get_wc_contents() callback (as opposed to requiring a GET to the
+   server), and feed the information through the associated update
+   editor. */
+static svn_error_t *
+local_fetch(report_info_t *info,
+            apr_pool_t *scratch_pool)
+{
+  SVN_ERR(open_updated_file(info, TRUE, scratch_pool));
+
+  SVN_ERR(svn_txdelta_send_stream(info->cached_contents, info->textdelta,
+                                  info->textdelta_baton, NULL, scratch_pool));
+  SVN_ERR(svn_stream_close(info->cached_contents));
+  info->cached_contents = NULL;
+  SVN_ERR(close_updated_file(info, scratch_pool));
 
-  if (info->fetch_file)
-    {
-      SVN_ERR(info->dir->update_editor->apply_textdelta(info->file_baton,
-                                                    info->base_checksum,
-                                                    info->editor_pool,
-                                                    &info->textdelta,
-                                                    &info->textdelta_baton));
-    }
+  /* We're done with our pool. */
+  svn_pool_destroy(info->pool);
 
-  if (info->lock_token)
-    check_lock(info);
+  return SVN_NO_ERROR;
+}
 
-  /* set all of the properties we received */
-  SVN_ERR(svn_ra_serf__walk_all_props(info->props,
-                                      info->base_name, info->base_rev,
-                                      set_file_props, info,
-                                      scratch_pool));
-  SVN_ERR(svn_ra_serf__walk_all_props(info->dir->removed_props,
-                                      info->base_name, info->base_rev,
-                                      remove_file_props, info,
-                                      scratch_pool));
-  if (info->fetch_props)
-    {
-      SVN_ERR(svn_ra_serf__walk_all_props(info->props, info->url,
-                                          info->target_rev,
-                                          set_file_props, info,
-                                          scratch_pool));
-    }
+/* Implements svn_ra_serf__response_handler_t */
+static svn_error_t *
+handle_local_fetch(serf_request_t *request,
+                   serf_bucket_t *response,
+                   void *handler_baton,
+                   apr_pool_t *pool)
+{
+  report_fetch_t *fetch_ctx = handler_baton;
+  apr_status_t status;
+  svn_error_t *err;
+  const char *data;
+  apr_size_t len;
 
-  SVN_ERR(info->dir->update_editor->close_file(info->file_baton,
-                                               info->final_checksum,
-                                               scratch_pool));
+  /* ### new field. make sure we didn't miss some initialization.  */
+  SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
 
-  /* We're done with our pools. */
-  svn_pool_destroy(info->editor_pool);
-  svn_pool_destroy(info->pool);
+  /* If the error code wasn't 200, something went wrong. Don't use the returned
+     data as its probably an error message. Just bail out instead. */
+  if (fetch_ctx->handler->sline.code != 200)
+    {
+      err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+                              _("HEAD request failed: %d %s"),
+                              fetch_ctx->handler->sline.code,
+                              fetch_ctx->handler->sline.reason);
+      return error_fetch(request, fetch_ctx, err);
+    }
 
-  info->dir->ref_count--;
+  while (1)
+    {
+      status = serf_bucket_read(response, 8000, &data, &len);
+      if (SERF_BUCKET_READ_ERROR(status))
+        {
+          return svn_error_wrap_apr(status, NULL);
+        }
+      if (APR_STATUS_IS_EOF(status))
+        {
+          err = local_fetch(fetch_ctx->info, fetch_ctx->info->pool);
+          if (err)
+            {
+              return error_fetch(request, fetch_ctx, err);
+            }
 
-  return SVN_NO_ERROR;
+          fetch_ctx->done = TRUE;
+          fetch_ctx->done_item.data = fetch_ctx;
+          fetch_ctx->done_item.next = *fetch_ctx->done_list;
+          *fetch_ctx->done_list = &fetch_ctx->done_item;
+          return svn_error_wrap_apr(status, NULL);
+        }
+      if (APR_STATUS_IS_EAGAIN(status))
+        {
+          return svn_error_wrap_apr(status, NULL);
+        }
+    }
+  /* not reached */
 }
 
+/* --------------------------------------------------------- */
+
 static svn_error_t *
 fetch_file(report_context_t *ctx, report_info_t *info)
 {
@@ -1237,9 +1274,8 @@ fetch_file(report_context_t *ctx, report
   conn = ctx->sess->conns[ctx->sess->cur_conn];
 
   /* go fetch info->name from DAV:checked-in */
-  info->url =
-      svn_ra_serf__get_ver_prop(info->props, info->base_name,
-                                info->base_rev, "DAV:", "checked-in");
+  info->url = svn_ra_serf__get_ver_prop(info->props, info->base_name,
+                                        info->base_rev, "DAV:", "checked-in");
 
   if (!info->url)
     {
@@ -1254,7 +1290,7 @@ fetch_file(report_context_t *ctx, report
     {
       SVN_ERR(svn_ra_serf__deliver_props(&info->propfind, info->props,
                                          ctx->sess, conn, info->url,
-                                         info->target_rev, "0", all_props,
+                                         ctx->target_rev, "0", all_props,
                                          &ctx->done_propfinds,
                                          info->dir->pool));
       SVN_ERR_ASSERT(info->propfind);
@@ -1267,34 +1303,104 @@ fetch_file(report_context_t *ctx, report
    */
   if (info->fetch_file && ctx->text_deltas)
     {
-      report_fetch_t *fetch_ctx;
+      svn_stream_t *contents = NULL;
+
+      if (ctx->sess->wc_callbacks->get_wc_contents
+          && info->final_sha1_checksum)
+        {
+          svn_error_t *err;
+          svn_checksum_t *sha1_checksum;
+
+          err = svn_checksum_parse_hex(&sha1_checksum, svn_checksum_sha1,
+                                       info->final_sha1_checksum, info->pool);
+          if (!err)
+            {
+              err = ctx->sess->wc_callbacks->get_wc_contents(
+                        ctx->sess->wc_callback_baton, &contents,
+                        sha1_checksum, info->pool);
+            }
 
-      fetch_ctx = apr_pcalloc(info->dir->pool, sizeof(*fetch_ctx));
-      fetch_ctx->info = info;
-      fetch_ctx->done_list = &ctx->done_fetches;
-      fetch_ctx->sess = ctx->sess;
-      fetch_ctx->conn = conn;
+          if (err)
+            {
+              /* Meh.  Maybe we'll care one day why we're in an
+                 errorful state, but this codepath is optional.  */
+              svn_error_clear(err);
+            }
+          else
+            {
+              info->cached_contents = contents;
+            }
+        }          
 
-      handler = apr_pcalloc(info->dir->pool, sizeof(*handler));
+      /* If the working copy can provided cached contents for this
+         file, we'll send a simple HEAD request (which I'll claim is
+         to verify readability, but really is just so I can provide a
+         Serf-queued-request-compliant way of processing the contents
+         after the PROPFIND for the file's properties ... ugh).
 
-      handler->method = "GET";
-      handler->path = fetch_ctx->info->url;
+         Otherwise, we use a GET request for the file's contents. */
+      if (info->cached_contents)
+        {
+          report_fetch_t *fetch_ctx;
 
-      handler->conn = conn;
-      handler->session = ctx->sess;
+          fetch_ctx = apr_pcalloc(info->dir->pool, sizeof(*fetch_ctx));
+          fetch_ctx->info = info;
+          fetch_ctx->done_list = &ctx->done_fetches;
+          fetch_ctx->sess = ctx->sess;
+          fetch_ctx->conn = conn;
 
-      handler->header_delegate = headers_fetch;
-      handler->header_delegate_baton = fetch_ctx;
+          handler = apr_pcalloc(info->dir->pool, sizeof(*handler));
 
-      handler->response_handler = handle_fetch;
-      handler->response_baton = fetch_ctx;
+          handler->handler_pool = info->dir->pool;
+          handler->method = "HEAD";
+          handler->path = fetch_ctx->info->url;
 
-      handler->response_error = cancel_fetch;
-      handler->response_error_baton = fetch_ctx;
+          handler->conn = conn;
+          handler->session = ctx->sess;
 
-      svn_ra_serf__request_create(handler);
+          handler->response_handler = handle_local_fetch;
+          handler->response_baton = fetch_ctx;
 
-      ctx->active_fetches++;
+          fetch_ctx->handler = handler;
+
+          svn_ra_serf__request_create(handler);
+
+          ctx->active_fetches++;
+        }
+      else
+        {
+          report_fetch_t *fetch_ctx;
+
+          fetch_ctx = apr_pcalloc(info->dir->pool, sizeof(*fetch_ctx));
+          fetch_ctx->info = info;
+          fetch_ctx->done_list = &ctx->done_fetches;
+          fetch_ctx->sess = ctx->sess;
+          fetch_ctx->conn = conn;
+
+          handler = apr_pcalloc(info->dir->pool, sizeof(*handler));
+
+          handler->handler_pool = info->dir->pool;
+          handler->method = "GET";
+          handler->path = fetch_ctx->info->url;
+
+          handler->conn = conn;
+          handler->session = ctx->sess;
+
+          handler->header_delegate = headers_fetch;
+          handler->header_delegate_baton = fetch_ctx;
+
+          handler->response_handler = handle_fetch;
+          handler->response_baton = fetch_ctx;
+
+          handler->response_error = cancel_fetch;
+          handler->response_error_baton = fetch_ctx;
+
+          fetch_ctx->handler = handler;
+
+          svn_ra_serf__request_create(handler);
+
+          ctx->active_fetches++;
+        }
     }
   else if (info->propfind)
     {
@@ -1327,11 +1433,11 @@ fetch_file(report_context_t *ctx, report
 
 static svn_error_t *
 start_report(svn_ra_serf__xml_parser_t *parser,
-             void *userData,
              svn_ra_serf__dav_props_t name,
-             const char **attrs)
+             const char **attrs,
+             apr_pool_t *scratch_pool)
 {
-  report_context_t *ctx = userData;
+  report_context_t *ctx = parser->user_data;
   report_state_e state;
 
   state = parser->state->current_state;
@@ -1371,7 +1477,6 @@ start_report(svn_ra_serf__xml_parser_t *
 
       info->base_rev = SVN_STR_TO_REV(rev);
       info->dir->base_rev = info->base_rev;
-      info->dir->target_rev = ctx->target_rev;
       info->fetch_props = TRUE;
 
       info->dir->base_name = "";
@@ -1424,7 +1529,6 @@ start_report(svn_ra_serf__xml_parser_t *
 
       info->base_rev = SVN_STR_TO_REV(rev);
       dir->base_rev = info->base_rev;
-      dir->target_rev = ctx->target_rev;
 
       info->fetch_props = FALSE;
 
@@ -1478,7 +1582,6 @@ start_report(svn_ra_serf__xml_parser_t *
       /* Mark that we don't have a base. */
       info->base_rev = SVN_INVALID_REVNUM;
       dir->base_rev = info->base_rev;
-      dir->target_rev = ctx->target_rev;
       dir->fetch_props = TRUE;
 
       dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath,
@@ -1511,7 +1614,6 @@ start_report(svn_ra_serf__xml_parser_t *
       info = push_state(parser, ctx, OPEN_FILE);
 
       info->base_rev = SVN_STR_TO_REV(rev);
-      info->target_rev = ctx->target_rev;
       info->fetch_props = FALSE;
 
       info->base_name = apr_pstrdup(info->pool, file_name);
@@ -1537,7 +1639,6 @@ start_report(svn_ra_serf__xml_parser_t *
       info = push_state(parser, ctx, ADD_FILE);
 
       info->base_rev = SVN_INVALID_REVNUM;
-      info->target_rev = ctx->target_rev;
       info->fetch_props = TRUE;
       info->fetch_file = TRUE;
 
@@ -1557,9 +1658,11 @@ start_report(svn_ra_serf__xml_parser_t *
            strcmp(name.name, "delete-entry") == 0)
     {
       const char *file_name;
+      const char *rev_str;
       report_info_t *info;
       apr_pool_t *tmppool;
       const char *full_path;
+      svn_revnum_t delete_rev = SVN_INVALID_REVNUM;
 
       file_name = svn_xml_get_attr_value("name", attrs);
 
@@ -1570,6 +1673,10 @@ start_report(svn_ra_serf__xml_parser_t *
             _("Missing name attr in delete-entry element"));
         }
 
+      rev_str = svn_xml_get_attr_value("rev", attrs);
+      if (rev_str) /* Not available on older repositories! */
+        delete_rev = SVN_STR_TO_REV(rev_str);
+
       info = parser->state->private;
 
       SVN_ERR(open_dir(info->dir));
@@ -1578,10 +1685,10 @@ start_report(svn_ra_serf__xml_parser_t *
 
       full_path = svn_relpath_join(info->dir->name, file_name, tmppool);
 
-      SVN_ERR(info->dir->update_editor->delete_entry(full_path,
-                                                     SVN_INVALID_REVNUM,
-                                                     info->dir->dir_baton,
-                                                     tmppool));
+      SVN_ERR(ctx->update_editor->delete_entry(full_path,
+                                               delete_rev,
+                                               info->dir->dir_baton,
+                                               tmppool));
 
       svn_pool_destroy(tmppool);
     }
@@ -1645,8 +1752,7 @@ start_report(svn_ra_serf__xml_parser_t *
           info->prop_ns = name.namespace;
           info->prop_name = apr_pstrdup(parser->state->pool, name.name);
           info->prop_encoding = NULL;
-          info->prop_val = NULL;
-          info->prop_val_len = 0;
+          svn_stringbuf_setempty(info->prop_value);
         }
       else if (strcmp(name.name, "set-prop") == 0 ||
                strcmp(name.name, "remove-prop") == 0)
@@ -1675,8 +1781,7 @@ start_report(svn_ra_serf__xml_parser_t *
                                          colon - full_prop_name);
           info->prop_name = apr_pstrdup(parser->state->pool, colon);
           info->prop_encoding = svn_xml_get_attr_value("encoding", attrs);
-          info->prop_val = NULL;
-          info->prop_val_len = 0;
+          svn_stringbuf_setempty(info->prop_value);
         }
       else if (strcmp(name.name, "prop") == 0)
         {
@@ -1707,8 +1812,7 @@ start_report(svn_ra_serf__xml_parser_t *
           info->prop_ns = name.namespace;
           info->prop_name = apr_pstrdup(parser->state->pool, name.name);
           info->prop_encoding = NULL;
-          info->prop_val = NULL;
-          info->prop_val_len = 0;
+          svn_stringbuf_setempty(info->prop_value);
         }
       else if (strcmp(name.name, "prop") == 0)
         {
@@ -1763,8 +1867,7 @@ start_report(svn_ra_serf__xml_parser_t *
                                          colon - full_prop_name);
           info->prop_name = apr_pstrdup(parser->state->pool, colon);
           info->prop_encoding = svn_xml_get_attr_value("encoding", attrs);
-          info->prop_val = NULL;
-          info->prop_val_len = 0;
+          svn_stringbuf_setempty(info->prop_value);
         }
       else
         {
@@ -1787,8 +1890,7 @@ start_report(svn_ra_serf__xml_parser_t *
       info->prop_ns = name.namespace;
       info->prop_name = apr_pstrdup(parser->state->pool, name.name);
       info->prop_encoding = svn_xml_get_attr_value("encoding", attrs);
-      info->prop_val = NULL;
-      info->prop_val_len = 0;
+      svn_stringbuf_setempty(info->prop_value);
     }
 
   return SVN_NO_ERROR;
@@ -1796,10 +1898,10 @@ start_report(svn_ra_serf__xml_parser_t *
 
 static svn_error_t *
 end_report(svn_ra_serf__xml_parser_t *parser,
-           void *userData,
-           svn_ra_serf__dav_props_t name)
+           svn_ra_serf__dav_props_t name,
+           apr_pool_t *scratch_pool)
 {
-  report_context_t *ctx = userData;
+  report_context_t *ctx = parser->user_data;
   report_state_e state;
 
   state = parser->state->current_state;
@@ -1849,7 +1951,7 @@ end_report(svn_ra_serf__xml_parser_t *pa
                                              info->dir->props, ctx->sess,
                                              ctx->sess->conns[ctx->sess->cur_conn],
                                              info->dir->url,
-                                             info->dir->target_rev, "0",
+                                             ctx->target_rev, "0",
                                              all_props,
                                              &ctx->done_propfinds,
                                              info->dir->pool));
@@ -1950,7 +2052,7 @@ end_report(svn_ra_serf__xml_parser_t *pa
     }
   else if (state == PROP)
     {
-      /* We need to move the prop_ns, prop_name, and prop_val into the
+      /* We need to move the prop_ns, prop_name, and prop_value into the
        * same lifetime as the dir->pool.
        */
       svn_ra_serf__ns_t *ns, *ns_name_match;
@@ -1958,8 +2060,7 @@ end_report(svn_ra_serf__xml_parser_t *pa
       report_info_t *info;
       report_dir_t *dir;
       apr_hash_t *props;
-      const char *set_val;
-      svn_string_t *set_val_str;
+      const svn_string_t *set_val_str;
       apr_pool_t *pool;
 
       info = parser->state->private;
@@ -2009,24 +2110,21 @@ end_report(svn_ra_serf__xml_parser_t *pa
         {
           props = dir->removed_props;
           pool = dir->pool;
-          info->prop_val = "";
-          info->prop_val_len = 1;
+          svn_stringbuf_setempty(info->prop_value);
         }
 
       if (info->prop_encoding)
         {
           if (strcmp(info->prop_encoding, "base64") == 0)
             {
-              svn_string_t encoded;
-              const svn_string_t *decoded;
-
-              encoded.data = info->prop_val;
-              encoded.len = info->prop_val_len;
+              svn_string_t tmp;
 
-              decoded = svn_base64_decode_string(&encoded, parser->state->pool);
+              /* Don't use morph_info_string cuz we need prop_value to
+                 remain usable.  */
+              tmp.data = info->prop_value->data;
+              tmp.len = info->prop_value->len;
 
-              info->prop_val = decoded->data;
-              info->prop_val_len = decoded->len;
+              set_val_str = svn_base64_decode_string(&tmp, pool);
             }
           else
             {
@@ -2035,11 +2133,11 @@ end_report(svn_ra_serf__xml_parser_t *pa
                                        _("Got unrecognized encoding '%s'"),
                                        info->prop_encoding);
             }
-
         }
-
-      set_val = apr_pmemdup(pool, info->prop_val, info->prop_val_len);
-      set_val_str = svn_string_ncreate(set_val, info->prop_val_len, pool);
+      else
+        {
+          set_val_str = svn_string_create_from_buf(info->prop_value, pool);
+        }
 
       svn_ra_serf__set_ver_prop(props, info->base_name, info->base_rev,
                                 ns->namespace, ns->url, set_val_str, pool);
@@ -2055,11 +2153,11 @@ end_report(svn_ra_serf__xml_parser_t *pa
 
 static svn_error_t *
 cdata_report(svn_ra_serf__xml_parser_t *parser,
-             void *userData,
              const char *data,
-             apr_size_t len)
+             apr_size_t len,
+             apr_pool_t *scratch_pool)
 {
-  report_context_t *ctx = userData;
+  report_context_t *ctx = parser->user_data;
 
   UNUSED_CTX(ctx);
 
@@ -2067,8 +2165,7 @@ cdata_report(svn_ra_serf__xml_parser_t *
     {
       report_info_t *info = parser->state->private;
 
-      svn_ra_serf__expand_string(&info->prop_val, &info->prop_val_len,
-                                 data, len, parser->state->pool);
+      svn_stringbuf_appendbytes(info->prop_value, data, len);
     }
 
   return SVN_NO_ERROR;
@@ -2226,16 +2323,17 @@ open_connection_if_needed(svn_ra_serf__s
       int cur = sess->num_conns;
       apr_status_t status;
 
-      sess->conns[cur] = apr_palloc(sess->pool, sizeof(*sess->conns[cur]));
+      sess->conns[cur] = apr_pcalloc(sess->pool, sizeof(*sess->conns[cur]));
+      sess->conns[cur]->http10 = TRUE;  /* until we confirm HTTP/1.1  */
+      sess->conns[cur]->http10 = FALSE; /* ### don't change behavior yet  */
       sess->conns[cur]->bkt_alloc = serf_bucket_allocator_create(sess->pool,
                                                                  NULL, NULL);
       sess->conns[cur]->hostname  = sess->conns[0]->hostname;
       sess->conns[cur]->using_ssl = sess->conns[0]->using_ssl;
       sess->conns[cur]->using_compression = sess->conns[0]->using_compression;
-      sess->conns[cur]->useragent = sess->conns[0]->useragent;
       sess->conns[cur]->last_status_code = -1;
-      sess->conns[cur]->ssl_context = NULL;
       sess->conns[cur]->session = sess;
+      sess->conns[cur]->useragent = sess->conns[0]->useragent;
       status = serf_connection_create2(&sess->conns[cur]->conn,
                                        sess->context,
                                        sess->session_url,
@@ -2296,7 +2394,6 @@ finish_report(void *report_baton,
   svn_ra_serf__xml_parser_t *parser_ctx;
   const char *report_target;
   svn_boolean_t closed_root;
-  int status_code;
   svn_stringbuf_t *buf = NULL;
   apr_pool_t *iterpool = svn_pool_create(pool);
   svn_error_t *err;
@@ -2326,6 +2423,7 @@ finish_report(void *report_baton,
 
   handler = apr_pcalloc(pool, sizeof(*handler));
 
+  handler->handler_pool = pool;
   handler->method = "REPORT";
   handler->path = report->path;
   handler->body_delegate = create_update_report_body;
@@ -2345,9 +2443,6 @@ finish_report(void *report_baton,
   parser_ctx->end = end_report;
   parser_ctx->cdata = cdata_report;
   parser_ctx->done = &report->done;
-  /* While we provide a location here to store the status code, we don't
-     do anything with it. The error in parser_ctx->error is sufficient. */
-  parser_ctx->status_code = &status_code;
 
   handler->response_handler = svn_ra_serf__handle_xml_parser;
   handler->response_baton = parser_ctx;
@@ -2789,7 +2884,7 @@ svn_ra_serf__get_file(svn_ra_session_t *
   svn_ra_serf__connection_t *conn;
   const char *fetch_url;
   apr_hash_t *fetch_props;
-  svn_node_kind_t res_kind;
+  svn_kind_t res_kind;
 
   /* What connection should we go on? */
   conn = session->conns[session->cur_conn];
@@ -2805,24 +2900,23 @@ svn_ra_serf__get_file(svn_ra_session_t *
    */
   if (SVN_IS_VALID_REVNUM(revision) || fetched_rev)
     {
-      const char *baseline_url, *rel_path;
-
-      SVN_ERR(svn_ra_serf__get_baseline_info(&baseline_url, &rel_path,
-                                             session, conn, fetch_url,
-                                             revision, fetched_rev, pool));
-      fetch_url = svn_path_url_add_component2(baseline_url, rel_path, pool);
+      SVN_ERR(svn_ra_serf__get_stable_url(&fetch_url, fetched_rev,
+                                          session, conn,
+                                          fetch_url, revision,
+                                          pool, pool));
       revision = SVN_INVALID_REVNUM;
     }
+  /* REVISION is always SVN_INVALID_REVNUM  */
+  SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision));
 
-  SVN_ERR(svn_ra_serf__retrieve_props(&fetch_props, session, conn, fetch_url,
-                                      revision, "0",
-                                      props ? all_props : check_path_props,
-                                      pool, pool));
-
-  /* Verify that resource type is not colelction. */
-  SVN_ERR(svn_ra_serf__get_resource_type(&res_kind, fetch_props, fetch_url,
-                                         revision));
-  if (res_kind != svn_node_file)
+  SVN_ERR(svn_ra_serf__fetch_node_props(&fetch_props, conn, fetch_url,
+                                        SVN_INVALID_REVNUM,
+                                        props ? all_props : check_path_props,
+                                        pool, pool));
+
+  /* Verify that resource type is not collection. */
+  SVN_ERR(svn_ra_serf__get_resource_type(&res_kind, fetch_props));
+  if (res_kind != svn_kind_file)
     {
       return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL,
                               _("Can't get text contents of a directory"));
@@ -2831,8 +2925,10 @@ svn_ra_serf__get_file(svn_ra_session_t *
   /* TODO Filter out all of our props into a usable format. */
   if (props)
     {
-      SVN_ERR(svn_ra_serf__flatten_props(props, fetch_props, fetch_url,
-                                         revision, pool, pool));
+      /* ### flatten_props() does not copy PROPVALUE, but fetch_node_props()
+         ### put them into POOL, so we're okay.  */
+      SVN_ERR(svn_ra_serf__flatten_props(props, fetch_props,
+                                         pool, pool));
     }
 
   if (stream)
@@ -2849,6 +2945,8 @@ svn_ra_serf__get_file(svn_ra_session_t *
       stream_ctx->info->name = fetch_url;
 
       handler = apr_pcalloc(pool, sizeof(*handler));
+
+      handler->handler_pool = pool;
       handler->method = "GET";
       handler->path = fetch_url;
       handler->conn = conn;
@@ -2863,6 +2961,8 @@ svn_ra_serf__get_file(svn_ra_session_t *
       handler->response_error = cancel_fetch;
       handler->response_error_baton = stream_ctx;
 
+      stream_ctx->handler = handler;
+
       svn_ra_serf__request_create(handler);
 
       SVN_ERR(svn_ra_serf__context_run_wait(&stream_ctx->done, session, pool));