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

svn commit: r1532250 [18/37] - in /subversion/branches/cache-server: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/emacs/ contrib/hook-scripts/ contrib/server-side/fsfsfixer/ contrib/se...

Modified: subversion/branches/cache-server/subversion/libsvn_ra_serf/replay.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra_serf/replay.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra_serf/replay.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra_serf/replay.c Tue Oct 15 08:52:06 2013
@@ -26,16 +26,17 @@
 #include <apr_uri.h>
 #include <serf.h>
 
+#include "svn_private_config.h"
 #include "svn_pools.h"
 #include "svn_ra.h"
 #include "svn_dav.h"
+#include "svn_hash.h"
 #include "svn_xml.h"
 #include "../libsvn_ra/ra_loader.h"
 #include "svn_config.h"
 #include "svn_delta.h"
 #include "svn_base64.h"
 #include "svn_path.h"
-#include "svn_private_config.h"
 
 #include "private/svn_string_private.h"
 
@@ -46,55 +47,92 @@
  * This enum represents the current state of our XML parsing.
  */
 typedef enum replay_state_e {
-  NONE = 0,
-  REPORT,
-  OPEN_DIR,
-  ADD_DIR,
-  OPEN_FILE,
-  ADD_FILE,
-  DELETE_ENTRY,
-  APPLY_TEXTDELTA,
-  CHANGE_PROP
+  INITIAL = XML_STATE_INITIAL,
+
+  REPLAY_REPORT,
+  REPLAY_TARGET_REVISION,
+  REPLAY_OPEN_ROOT,
+  REPLAY_OPEN_DIRECTORY,
+  REPLAY_OPEN_FILE,
+  REPLAY_ADD_DIRECTORY,
+  REPLAY_ADD_FILE,
+  REPLAY_DELETE_ENTRY,
+  REPLAY_CLOSE_FILE,
+  REPLAY_CLOSE_DIRECTORY,
+  REPLAY_CHANGE_DIRECTORY_PROP,
+  REPLAY_CHANGE_FILE_PROP,
+  REPLAY_APPLY_TEXTDELTA,
 } replay_state_e;
 
-typedef struct replay_info_t replay_info_t;
+#define S_ SVN_XML_NAMESPACE
+static const svn_ra_serf__xml_transition_t replay_ttable[] = {
+  { INITIAL, S_, "editor-report", REPLAY_REPORT,
+    FALSE, { NULL }, TRUE },
 
-struct replay_info_t {
-  apr_pool_t *pool;
+  /* Replay just throws every operation as xml element directly
+     in the replay report, so we can't really use the nice exit
+     handling of the transition parser to handle clean callbacks */
 
-  void *baton;
-  svn_stream_t *stream;
+  { REPLAY_REPORT, S_, "target-revision", REPLAY_TARGET_REVISION,
+    FALSE, { "rev", NULL }, TRUE },
 
-  replay_info_t *parent;
-};
+  { REPLAY_REPORT, S_, "open-root", REPLAY_OPEN_ROOT,
+    FALSE, { "rev", NULL }, TRUE },
 
-typedef svn_error_t *
-(*change_prop_t)(void *baton,
-                 const char *name,
-                 const svn_string_t *value,
-                 apr_pool_t *pool);
+  { REPLAY_REPORT, S_, "open-directory", REPLAY_OPEN_DIRECTORY,
+    FALSE, { "name", "rev", NULL }, TRUE },
 
-typedef struct prop_info_t {
-  apr_pool_t *pool;
+  { REPLAY_REPORT, S_, "open-file", REPLAY_OPEN_FILE,
+    FALSE, { "name", "rev", NULL }, TRUE },
 
-  change_prop_t change;
+  { REPLAY_REPORT, S_, "add-directory", REPLAY_ADD_DIRECTORY,
+    FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", NULL}, TRUE },
 
-  const char *name;
-  svn_boolean_t del_prop;
+  { REPLAY_REPORT, S_, "add-file", REPLAY_ADD_FILE,
+    FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", NULL}, TRUE },
 
-  svn_stringbuf_t *prop_value;
+  { REPLAY_REPORT, S_, "delete-entry", REPLAY_DELETE_ENTRY,
+    FALSE, { "name", "rev", NULL }, TRUE },
 
-  replay_info_t *parent;
-} prop_info_t;
+  { REPLAY_REPORT, S_, "close-file", REPLAY_CLOSE_FILE,
+    FALSE, { "?checksum", NULL }, TRUE },
 
-typedef struct replay_context_t {
-  apr_pool_t *src_rev_pool;
-  apr_pool_t *dst_rev_pool;
-  /*file_pool is cleared after completion of each file. */
-  apr_pool_t *file_pool;
+  { REPLAY_REPORT, S_, "close-directory", REPLAY_CLOSE_DIRECTORY,
+    FALSE, { NULL }, TRUE },
 
-  /* Are we done fetching this file? */
-  svn_boolean_t done;
+  { REPLAY_REPORT, S_, "change-dir-prop", REPLAY_CHANGE_DIRECTORY_PROP,
+    TRUE, { "name", "?del", NULL }, TRUE },
+
+  { REPLAY_REPORT, S_, "change-file-prop", REPLAY_CHANGE_FILE_PROP,
+    TRUE, { "name", "?del", NULL }, TRUE },
+
+  { REPLAY_REPORT, S_, "apply-textdelta", REPLAY_APPLY_TEXTDELTA,
+    FALSE, { "?checksum", NULL }, TRUE },
+
+  { 0 }
+};
+
+/* Per directory/file state */
+typedef struct replay_node_t {
+  apr_pool_t *pool; /* pool allocating this node's data */
+  svn_boolean_t file; /* file or dir */
+
+  void *baton; /* node baton */
+  svn_stream_t *stream; /* stream while handling txdata */
+
+  struct replay_node_t *parent; /* parent node or NULL */
+} replay_node_t;
+
+/* Per revision replay report state */
+typedef struct revision_report_t {
+  apr_pool_t *pool; /* per revision pool */
+
+  struct replay_node_t *current_node;
+  struct replay_node_t *root_node;
+
+  /* Are we done fetching this file?
+     Handles book-keeping in multi-report case */
+  svn_boolean_t *done;
   svn_ra_serf__list_t **done_list;
   svn_ra_serf__list_t done_item;
 
@@ -126,81 +164,33 @@ typedef struct replay_context_t {
   apr_hash_t *revs_props;
   apr_hash_t *props;
 
-  /* Keep a reference to the XML parser ctx to report any errors. */
-  svn_ra_serf__xml_parser_t *parser_ctx;
-
   /* Handlers for the PROPFIND and REPORT for the current revision. */
   svn_ra_serf__handler_t *propfind_handler;
   svn_ra_serf__handler_t *report_handler;
 
-} replay_context_t;
-
-
-static void *
-push_state(svn_ra_serf__xml_parser_t *parser,
-           replay_context_t *replay_ctx,
-           replay_state_e state)
-{
-  svn_ra_serf__xml_push_state(parser, state);
-
-  if (state == OPEN_DIR || state == ADD_DIR ||
-      state == OPEN_FILE || state == ADD_FILE)
-    {
-      replay_info_t *info;
-
-      info = apr_palloc(replay_ctx->dst_rev_pool, sizeof(*info));
-
-      info->pool = replay_ctx->dst_rev_pool;
-      info->parent = parser->state->private;
-      info->baton = NULL;
-      info->stream = NULL;
-
-      parser->state->private = info;
-    }
-  else if (state == CHANGE_PROP)
-    {
-      prop_info_t *info;
-
-      info = apr_pcalloc(replay_ctx->dst_rev_pool, sizeof(*info));
-
-      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;
-    }
-
-  return parser->state->private;
-}
+} revision_report_t;
 
+/* Conforms to svn_ra_serf__xml_opened_t */
 static svn_error_t *
-start_replay(svn_ra_serf__xml_parser_t *parser,
-             svn_ra_serf__dav_props_t name,
-             const char **attrs,
-             apr_pool_t *scratch_pool)
+replay_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)
 {
-  replay_context_t *ctx = parser->user_data;
-  replay_state_e state;
+  struct revision_report_t *ctx = baton;
 
-  state = parser->state->current_state;
-
-  if (state == NONE &&
-      strcmp(name.name, "editor-report") == 0)
+  if (entered_state == REPLAY_REPORT)
     {
-      push_state(parser, ctx, REPORT);
-
       /* Before we can continue, we need the revision properties. */
       SVN_ERR_ASSERT(!ctx->propfind_handler || ctx->propfind_handler->done);
 
       /* Create a pool for the commit editor. */
-      ctx->dst_rev_pool = svn_pool_create(ctx->src_rev_pool);
-      ctx->file_pool = svn_pool_create(ctx->dst_rev_pool);
-
       SVN_ERR(svn_ra_serf__select_revprops(&ctx->props,
                                            ctx->revprop_target,
                                            ctx->revprop_rev,
                                            ctx->revs_props,
-                                           ctx->dst_rev_pool,
+                                           ctx->pool,
                                            scratch_pool));
 
       if (ctx->revstart_func)
@@ -208,382 +198,298 @@ start_replay(svn_ra_serf__xml_parser_t *
           SVN_ERR(ctx->revstart_func(ctx->revision, ctx->replay_baton,
                                      &ctx->editor, &ctx->editor_baton,
                                      ctx->props,
-                                     ctx->dst_rev_pool));
+                                     ctx->pool));
         }
     }
-  else if (state == REPORT &&
-           strcmp(name.name, "target-revision") == 0)
+  else if (entered_state == REPLAY_APPLY_TEXTDELTA)
     {
-      const char *rev;
-
-      rev = svn_xml_get_attr_value("rev", attrs);
-      if (!rev)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing revision attr in target-revision element"));
-        }
-
-      SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
-                                               SVN_STR_TO_REV(rev),
-                                               scratch_pool));
+       struct replay_node_t *node = ctx->current_node;
+       apr_hash_t *attrs;
+       const char *checksum;
+       svn_txdelta_window_handler_t handler;
+       void *handler_baton;
+
+       if (! node || ! node->file || node->stream)
+         return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+       /* ### Is there a better way to access a specific attr here? */
+       attrs = svn_ra_serf__xml_gather_since(xes, REPLAY_APPLY_TEXTDELTA);
+       checksum = svn_hash_gets(attrs, "checksum");
+
+       SVN_ERR(ctx->editor->apply_textdelta(node->baton, checksum, node->pool,
+                                            &handler, &handler_baton));
+
+       if (handler != svn_delta_noop_window_handler)
+         {
+            node->stream = svn_base64_decode(
+                                    svn_txdelta_parse_svndiff(handler,
+                                                              handler_baton,
+                                                              TRUE,
+                                                              node->pool),
+                                    node->pool);
+         }
     }
-  else if (state == REPORT &&
-           strcmp(name.name, "open-root") == 0)
-    {
-      const char *rev;
-      replay_info_t *info;
-
-      rev = svn_xml_get_attr_value("rev", attrs);
 
-      if (!rev)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing revision attr in open-root element"));
-        }
+  return SVN_NO_ERROR;
+}
 
-      info = push_state(parser, ctx, OPEN_DIR);
+/* Conforms to svn_ra_serf__xml_closed_t  */
+static svn_error_t *
+replay_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)
+{
+  struct revision_report_t *ctx = baton;
 
-      SVN_ERR(ctx->editor->open_root(ctx->editor_baton,
-                                     SVN_STR_TO_REV(rev),
-                                     ctx->dst_rev_pool,
-                                     &info->baton));
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "delete-entry") == 0)
+  if (leaving_state == REPLAY_REPORT)
     {
-      const char *file_name, *rev;
-      replay_info_t *info;
+      if (ctx->current_node)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
 
-      file_name = svn_xml_get_attr_value("name", attrs);
-      if (!file_name)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing name attr in delete-entry element"));
-        }
-      rev = svn_xml_get_attr_value("rev", attrs);
-      if (!rev)
+      if (ctx->revfinish_func)
         {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing revision attr in delete-entry element"));
+          SVN_ERR(ctx->revfinish_func(ctx->revision, ctx->replay_baton,
+                                      ctx->editor, ctx->editor_baton,
+                                      ctx->props,
+                                      scratch_pool));
         }
-
-      info = push_state(parser, ctx, DELETE_ENTRY);
-
-      SVN_ERR(ctx->editor->delete_entry(file_name, SVN_STR_TO_REV(rev),
-                                        info->baton, scratch_pool));
-
-      svn_ra_serf__xml_pop_state(parser);
     }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "open-directory") == 0)
+  else if (leaving_state == REPLAY_TARGET_REVISION)
     {
-      const char *rev, *dir_name;
-      replay_info_t *info;
-
-      dir_name = svn_xml_get_attr_value("name", attrs);
-      if (!dir_name)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing name attr in open-directory element"));
-        }
-      rev = svn_xml_get_attr_value("rev", attrs);
-      if (!rev)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing revision attr in open-directory element"));
-        }
-
-      info = push_state(parser, ctx, OPEN_DIR);
+      const char *revstr = svn_hash_gets(attrs, "rev");
+      apr_int64_t rev;
 
-      SVN_ERR(ctx->editor->open_directory(dir_name, info->parent->baton,
-                                          SVN_STR_TO_REV(rev),
-                                          ctx->dst_rev_pool, &info->baton));
+      SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+      SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
+                                               (svn_revnum_t)rev,
+                                               scratch_pool));
     }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "add-directory") == 0)
+  else if (leaving_state == REPLAY_OPEN_ROOT)
     {
-      const char *dir_name, *copyfrom, *copyrev;
-      svn_revnum_t rev;
-      replay_info_t *info;
+      const char *revstr = svn_hash_gets(attrs, "rev");
+      apr_int64_t rev;
+      apr_pool_t *root_pool = svn_pool_create(ctx->pool);
 
-      dir_name = svn_xml_get_attr_value("name", attrs);
-      if (!dir_name)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing name attr in add-directory element"));
-        }
-      copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
-      copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
+      if (ctx->current_node || ctx->root_node)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
 
-      if (copyrev)
-        rev = SVN_STR_TO_REV(copyrev);
-      else
-        rev = SVN_INVALID_REVNUM;
+      ctx->root_node = apr_pcalloc(root_pool, sizeof(*ctx->root_node));
+      ctx->root_node->pool = root_pool;
 
-      info = push_state(parser, ctx, ADD_DIR);
+      ctx->current_node = ctx->root_node;
 
-      SVN_ERR(ctx->editor->add_directory(dir_name, info->parent->baton,
-                                         copyfrom, rev,
-                                         ctx->dst_rev_pool, &info->baton));
+      SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+      SVN_ERR(ctx->editor->open_root(ctx->editor_baton, (svn_revnum_t)rev,
+                                     root_pool,
+                                     &ctx->current_node->baton));
     }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "close-directory") == 0)
+  else if (leaving_state == REPLAY_OPEN_DIRECTORY
+           || leaving_state == REPLAY_OPEN_FILE
+           || leaving_state == REPLAY_ADD_DIRECTORY
+           || leaving_state == REPLAY_ADD_FILE)
     {
-      replay_info_t *info = parser->state->private;
+      struct replay_node_t *node;
+      apr_pool_t *node_pool;
+      const char *name = svn_hash_gets(attrs, "name");
+      const char *rev_str;
+      apr_int64_t rev;
 
-      SVN_ERR(ctx->editor->close_directory(info->baton, scratch_pool));
+      if (!ctx->current_node || ctx->current_node->file)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
 
-      svn_ra_serf__xml_pop_state(parser);
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "open-file") == 0)
-    {
-      const char *file_name, *rev;
-      replay_info_t *info;
+      node_pool = svn_pool_create(ctx->current_node->pool);
+      node = apr_pcalloc(node_pool, sizeof(*node));
+      node->pool = node_pool;
+      node->parent = ctx->current_node;
 
-      svn_pool_clear(ctx->file_pool);
-      file_name = svn_xml_get_attr_value("name", attrs);
-      if (!file_name)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing name attr in open-file element"));
-        }
-      rev = svn_xml_get_attr_value("rev", attrs);
-      if (!rev)
+      if (leaving_state == REPLAY_OPEN_DIRECTORY
+          || leaving_state == REPLAY_OPEN_FILE)
         {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing revision attr in open-file element"));
+          rev_str = svn_hash_gets(attrs, "rev");
         }
+      else
+        rev_str = svn_hash_gets(attrs, "copyfrom-rev");
 
-      info = push_state(parser, ctx, OPEN_FILE);
-
-      SVN_ERR(ctx->editor->open_file(file_name, info->parent->baton,
-                                     SVN_STR_TO_REV(rev),
-                                     ctx->file_pool, &info->baton));
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "add-file") == 0)
-    {
-      const char *file_name, *copyfrom, *copyrev;
-      svn_revnum_t rev;
-      replay_info_t *info;
-
-      svn_pool_clear(ctx->file_pool);
-      file_name = svn_xml_get_attr_value("name", attrs);
-      if (!file_name)
-        {
-          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                    _("Missing name attr in add-file element"));
-        }
-      copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
-      copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
-
-      info = push_state(parser, ctx, ADD_FILE);
-
-      if (copyrev)
-        rev = SVN_STR_TO_REV(copyrev);
+      if (rev_str)
+        SVN_ERR(svn_cstring_atoi64(&rev, rev_str));
       else
         rev = SVN_INVALID_REVNUM;
 
-      SVN_ERR(ctx->editor->add_file(file_name, info->parent->baton,
-                                    copyfrom, rev,
-                                    ctx->file_pool, &info->baton));
-    }
-  else if ((state == OPEN_FILE || state == ADD_FILE) &&
-           strcmp(name.name, "apply-textdelta") == 0)
-    {
-      const char *checksum;
-      replay_info_t *info;
-      svn_txdelta_window_handler_t textdelta;
-      void *textdelta_baton;
-      svn_stream_t *delta_stream;
-
-      info = push_state(parser, ctx, APPLY_TEXTDELTA);
-
-      checksum = svn_xml_get_attr_value("checksum", attrs);
-      if (checksum)
-        {
-          checksum = apr_pstrdup(info->pool, checksum);
-        }
-
-      SVN_ERR(ctx->editor->apply_textdelta(info->baton, checksum,
-                                           ctx->file_pool,
-                                           &textdelta,
-                                           &textdelta_baton));
-
-      delta_stream = svn_txdelta_parse_svndiff(textdelta, textdelta_baton,
-                                               TRUE, info->pool);
-      info->stream = svn_base64_decode(delta_stream, info->pool);
-    }
-  else if ((state == OPEN_FILE || state == ADD_FILE) &&
-           strcmp(name.name, "close-file") == 0)
-    {
-      replay_info_t *info = parser->state->private;
-      const char *checksum;
-
-      checksum = svn_xml_get_attr_value("checksum", attrs);
-
-      SVN_ERR(ctx->editor->close_file(info->baton, checksum, scratch_pool));
-
-      svn_ra_serf__xml_pop_state(parser);
-    }
-  else if (((state == OPEN_FILE || state == ADD_FILE) &&
-            strcmp(name.name, "change-file-prop") == 0) ||
-           ((state == OPEN_DIR || state == ADD_DIR) &&
-            strcmp(name.name, "change-dir-prop") == 0))
-    {
-      const char *prop_name;
-      prop_info_t *info;
-
-      prop_name = svn_xml_get_attr_value("name", attrs);
-      if (!prop_name)
+      switch (leaving_state)
         {
-          return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                                   _("Missing name attr in %s element"),
-                                   name.name);
-        }
+          case REPLAY_OPEN_DIRECTORY:
+            node->file = FALSE;
+            SVN_ERR(ctx->editor->open_directory(name,
+                                    ctx->current_node->baton,
+                                    (svn_revnum_t)rev,
+                                    node->pool,
+                                    &node->baton));
+            break;
+          case REPLAY_OPEN_FILE:
+            node->file = TRUE;
+            SVN_ERR(ctx->editor->open_file(name,
+                                    ctx->current_node->baton,
+                                    (svn_revnum_t)rev,
+                                    node->pool,
+                                    &node->baton));
+            break;
+          case REPLAY_ADD_DIRECTORY:
+            node->file = FALSE;
+            SVN_ERR(ctx->editor->add_directory(
+                                    name,
+                                    ctx->current_node->baton,
+                                    SVN_IS_VALID_REVNUM(rev)
+                                        ? svn_hash_gets(attrs, "copyfrom-path")
+                                        : NULL,
+                                    (svn_revnum_t)rev,
+                                    node->pool,
+                                    &node->baton));
+            break;
+          case REPLAY_ADD_FILE:
+            node->file = TRUE;
+            SVN_ERR(ctx->editor->add_file(
+                                    name,
+                                    ctx->current_node->baton,
+                                    SVN_IS_VALID_REVNUM(rev)
+                                        ? svn_hash_gets(attrs, "copyfrom-path")
+                                        : NULL,
+                                    (svn_revnum_t)rev,
+                                    node->pool,
+                                    &node->baton));
+            break;
+          /* default: unreachable */
+        }
+      ctx->current_node = node;
+    }
+  else if (leaving_state == REPLAY_CLOSE_FILE)
+    {
+      struct replay_node_t *node = ctx->current_node;
+
+      if (! node || ! node->file)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+      SVN_ERR(ctx->editor->close_file(node->baton,
+                                      svn_hash_gets(attrs, "checksum"),
+                                      node->pool));
+      ctx->current_node = node->parent;
+      svn_pool_destroy(node->pool);
+    }
+  else if (leaving_state == REPLAY_CLOSE_DIRECTORY)
+    {
+      struct replay_node_t *node = ctx->current_node;
+
+      if (! node || node->file)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+      SVN_ERR(ctx->editor->close_directory(node->baton, node->pool));
+      ctx->current_node = node->parent;
+      svn_pool_destroy(node->pool);
+    }
+  else if (leaving_state == REPLAY_DELETE_ENTRY)
+    {
+      struct replay_node_t *parent_node = ctx->current_node;
+      const char *name = svn_hash_gets(attrs, "name");
+      const char *revstr = svn_hash_gets(attrs, "rev");
+      apr_int64_t rev;
+
+      if (! parent_node || parent_node->file)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+      SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+      SVN_ERR(ctx->editor->delete_entry(name,
+                                        (svn_revnum_t)rev,
+                                        parent_node->baton,
+                                        scratch_pool));
+    }
+  else if (leaving_state == REPLAY_CHANGE_FILE_PROP
+           || leaving_state == REPLAY_CHANGE_DIRECTORY_PROP)
+    {
+      struct replay_node_t *node = ctx->current_node;
+      const char *name;
+      const svn_string_t *value;
 
-      info = push_state(parser, ctx, CHANGE_PROP);
+      if (! node || node->file != (leaving_state == REPLAY_CHANGE_FILE_PROP))
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
 
+      name = svn_hash_gets(attrs, "name");
 
-      if (svn_xml_get_attr_value("del", attrs))
-        info->del_prop = TRUE;
+      if (svn_hash_gets(attrs, "del"))
+        value = NULL;
       else
-        info->del_prop = FALSE;
+        value = svn_base64_decode_string(cdata, scratch_pool);
 
-      if (state == OPEN_FILE || state == ADD_FILE)
+      if (node->file)
         {
-          info->name = apr_pstrdup(ctx->file_pool, prop_name);
-          info->change = ctx->editor->change_file_prop;
+          SVN_ERR(ctx->editor->change_file_prop(node->baton, name, value,
+                                                scratch_pool));
         }
       else
         {
-          info->name = apr_pstrdup(ctx->dst_rev_pool, prop_name);
-          info->change = ctx->editor->change_dir_prop;
+          SVN_ERR(ctx->editor->change_dir_prop(node->baton, name, value,
+                                               scratch_pool));
         }
-
     }
+  else if (leaving_state == REPLAY_APPLY_TEXTDELTA)
+    {
+      struct replay_node_t *node = ctx->current_node;
+
+      if (! node || ! node->file || ! node->stream)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+      SVN_ERR(svn_stream_close(node->stream));
 
+      node->stream = NULL;
+    }
   return SVN_NO_ERROR;
 }
 
+/* Conforms to svn_ra_serf__xml_cdata_t  */
 static svn_error_t *
-end_replay(svn_ra_serf__xml_parser_t *parser,
-           svn_ra_serf__dav_props_t name,
-           apr_pool_t *scratch_pool)
+replay_cdata(svn_ra_serf__xml_estate_t *xes,
+             void *baton,
+             int current_state,
+             const char *data,
+             apr_size_t len,
+             apr_pool_t *scratch_pool)
 {
-  replay_context_t *ctx = parser->user_data;
-  replay_state_e state;
+  struct revision_report_t *ctx = baton;
 
-  state = parser->state->current_state;
-
-  if (state == REPORT &&
-      strcmp(name.name, "editor-report") == 0)
+  if (current_state == REPLAY_APPLY_TEXTDELTA)
     {
-      svn_ra_serf__xml_pop_state(parser);
-      if (ctx->revfinish_func)
-        {
-          SVN_ERR(ctx->revfinish_func(ctx->revision, ctx->replay_baton,
-                                      ctx->editor, ctx->editor_baton,
-                                      ctx->props,
-                                      ctx->dst_rev_pool));
-        }
-      svn_pool_destroy(ctx->dst_rev_pool);
-    }
-  else if (state == OPEN_DIR && strcmp(name.name, "open-directory") == 0)
-    {
-      /* Don't do anything. */
-    }
-  else if (state == ADD_DIR && strcmp(name.name, "add-directory") == 0)
-    {
-      /* Don't do anything. */
-    }
-  else if (state == OPEN_FILE && strcmp(name.name, "open-file") == 0)
-    {
-      /* Don't do anything. */
-    }
-  else if (state == ADD_FILE && strcmp(name.name, "add-file") == 0)
-    {
-      /* Don't do anything. */
-    }
-  else if ((state == OPEN_FILE || state == ADD_FILE) &&
-           strcmp(name.name, "close-file") == 0)
-    {
-      /* Don't do anything. */
-    }
-  else if ((state == APPLY_TEXTDELTA) &&
-           strcmp(name.name, "apply-textdelta") == 0)
-    {
-      replay_info_t *info = parser->state->private;
-      SVN_ERR(svn_stream_close(info->stream));
-      svn_ra_serf__xml_pop_state(parser);
-    }
-  else if (state == CHANGE_PROP &&
-           (strcmp(name.name, "change-file-prop") == 0 ||
-            strcmp(name.name, "change-dir-prop") == 0))
-    {
-      prop_info_t *info = parser->state->private;
-      const svn_string_t *prop_val;
+      struct replay_node_t *node = ctx->current_node;
 
-      if (info->del_prop)
-        {
-          prop_val = NULL;
-        }
-      else
-        {
-          const svn_string_t *morph;
+      if (! node || ! node->file)
+        return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
 
-          morph = svn_stringbuf__morph_into_string(info->prop_value);
-#ifdef SVN_DEBUG
-          info->prop_value = NULL;  /* morph killed the stringbuf.  */
-#endif
+      if (node->stream)
+        {
+          apr_size_t written = len;
 
-          if (strcmp(name.name, "change-file-prop") == 0)
-            prop_val = svn_base64_decode_string(morph, ctx->file_pool);
-          else
-            prop_val = svn_base64_decode_string(morph, ctx->dst_rev_pool);
+          SVN_ERR(svn_stream_write(node->stream, data, &written));
+          if (written != len)
+            return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
+                                    _("Error writing stream: unexpected EOF"));
         }
-
-      SVN_ERR(info->change(info->parent->baton, info->name, prop_val,
-                           info->parent->pool));
-      svn_ra_serf__xml_pop_state(parser);
     }
 
   return SVN_NO_ERROR;
 }
 
+/* Conforms to svn_ra_serf__xml_done_t  */
 static svn_error_t *
-cdata_replay(svn_ra_serf__xml_parser_t *parser,
-             const char *data,
-             apr_size_t len,
-             apr_pool_t *scratch_pool)
+replay_done(void *baton,
+           apr_pool_t *scratch_pool)
 {
-  replay_context_t *replay_ctx = parser->user_data;
-  replay_state_e state;
-
-  UNUSED_CTX(replay_ctx);
+  struct revision_report_t *ctx = baton;
 
-  state = parser->state->current_state;
+  *ctx->done = TRUE; /* Breaks out svn_ra_serf__context_run_wait */
 
-  if (state == APPLY_TEXTDELTA)
-    {
-      replay_info_t *info = parser->state->private;
-      apr_size_t written;
-
-      written = len;
-
-      SVN_ERR(svn_stream_write(info->stream, data, &written));
-
-      if (written != len)
-        return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
-                                _("Error writing stream: unexpected EOF"));
-    }
-  else if (state == CHANGE_PROP)
-    {
-      prop_info_t *info = parser->state->private;
-
-      svn_stringbuf_appendbytes(info->prop_value, data, len);
-    }
+  ctx->done_item.data = ctx;
+  ctx->done_item.next = *ctx->done_list;
+  *ctx->done_list = &ctx->done_item;
 
   return SVN_NO_ERROR;
 }
@@ -594,7 +500,7 @@ create_replay_body(serf_bucket_t **bkt,
                    serf_bucket_alloc_t *alloc,
                    apr_pool_t *pool)
 {
-  replay_context_t *ctx = baton;
+  struct revision_report_t *ctx = baton;
   serf_bucket_t *body_bkt;
 
   body_bkt = serf_bucket_aggregate_create(alloc);
@@ -617,17 +523,17 @@ create_replay_body(serf_bucket_t **bkt,
     {
       svn_ra_serf__add_tag_buckets(body_bkt,
                                    "S:revision",
-                                   apr_ltoa(ctx->src_rev_pool, ctx->revision),
+                                   apr_ltoa(pool, ctx->revision),
                                    alloc);
     }
   svn_ra_serf__add_tag_buckets(body_bkt,
                                "S:low-water-mark",
-                               apr_ltoa(ctx->src_rev_pool, ctx->low_water_mark),
+                               apr_ltoa(pool, ctx->low_water_mark),
                                alloc);
 
   svn_ra_serf__add_tag_buckets(body_bkt,
                                "S:send-deltas",
-                               apr_ltoa(ctx->src_rev_pool, ctx->send_deltas),
+                               apr_ltoa(pool, ctx->send_deltas),
                                alloc);
 
   svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "S:replay-report");
@@ -643,60 +549,52 @@ svn_ra_serf__replay(svn_ra_session_t *ra
                     svn_boolean_t send_deltas,
                     const svn_delta_editor_t *editor,
                     void *edit_baton,
-                    apr_pool_t *pool)
+                    apr_pool_t *scratch_pool)
 {
-  replay_context_t *replay_ctx;
+  struct revision_report_t ctx = { NULL };
   svn_ra_serf__session_t *session = ra_session->priv;
   svn_ra_serf__handler_t *handler;
-  svn_ra_serf__xml_parser_t *parser_ctx;
+  svn_ra_serf__xml_context_t *xmlctx;
   svn_error_t *err;
   const char *report_target;
 
-  SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
+  SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL,
+                                       scratch_pool));
 
-  replay_ctx = apr_pcalloc(pool, sizeof(*replay_ctx));
-  replay_ctx->src_rev_pool = pool;
-  replay_ctx->editor = editor;
-  replay_ctx->editor_baton = edit_baton;
-  replay_ctx->done = FALSE;
-  replay_ctx->revision = revision;
-  replay_ctx->low_water_mark = low_water_mark;
-  replay_ctx->send_deltas = send_deltas;
-  replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
+  ctx.pool = svn_pool_create(scratch_pool);
+  ctx.editor = editor;
+  ctx.editor_baton = edit_baton;
+  ctx.done = FALSE;
+  ctx.revision = revision;
+  ctx.low_water_mark = low_water_mark;
+  ctx.send_deltas = send_deltas;
+  ctx.revs_props = apr_hash_make(scratch_pool);
+
+  xmlctx = svn_ra_serf__xml_context_create(replay_ttable,
+                                           replay_opened, replay_closed,
+                                           replay_cdata,
+                                           NULL,
+                                           &ctx,
+                                           scratch_pool);
 
-  handler = apr_pcalloc(pool, sizeof(*handler));
+  handler = svn_ra_serf__create_expat_handler(xmlctx, NULL, scratch_pool);
 
-  handler->handler_pool = pool;
   handler->method = "REPORT";
   handler->path = session->session_url.path;
   handler->body_delegate = create_replay_body;
-  handler->body_delegate_baton = replay_ctx;
+  handler->body_delegate_baton = &ctx;
   handler->body_type = "text/xml";
   handler->conn = session->conns[0];
   handler->session = session;
 
-  parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
-
-  parser_ctx->pool = pool;
-  parser_ctx->user_data = replay_ctx;
-  parser_ctx->start = start_replay;
-  parser_ctx->end = end_replay;
-  parser_ctx->cdata = cdata_replay;
-  parser_ctx->done = &replay_ctx->done;
-
-  handler->response_handler = svn_ra_serf__handle_xml_parser;
-  handler->response_baton = parser_ctx;
-
-  /* This is only needed to handle errors during XML parsing. */
-  replay_ctx->parser_ctx = parser_ctx;
-  replay_ctx->report_handler = handler; /* unused */
+  ctx.report_handler = handler; /* unused */
 
   svn_ra_serf__request_create(handler);
 
-  err = svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool);
+  err = svn_ra_serf__context_run_wait(&handler->done, session, scratch_pool);
 
   SVN_ERR(svn_error_compose_create(
-              svn_ra_serf__error_on_status(handler->sline.code,
+              svn_ra_serf__error_on_status(handler->sline,
                                            handler->path,
                                            handler->location),
               err));
@@ -751,6 +649,8 @@ svn_ra_serf__replay_range(svn_ra_session
   const char *report_target;
   int active_reports = 0;
   const char *include_path;
+  svn_boolean_t done;
+  svn_ra_serf__list_t *done_reports = NULL;
 
   SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
 
@@ -785,10 +685,6 @@ svn_ra_serf__replay_range(svn_ra_session
 
   while (active_reports || rev <= end_revision)
     {
-      svn_ra_serf__list_t *done_list;
-      svn_ra_serf__list_t *done_reports = NULL;
-      replay_context_t *replay_ctx;
-
       if (session->cancel_func)
         SVN_ERR(session->cancel_func(session->cancel_baton));
 
@@ -796,17 +692,19 @@ svn_ra_serf__replay_range(svn_ra_session
          requests to MAX_OUTSTANDING_REQUESTS. */
       if (rev <= end_revision  && active_reports < MAX_OUTSTANDING_REQUESTS)
         {
+          struct revision_report_t *replay_ctx;
           svn_ra_serf__handler_t *handler;
-          svn_ra_serf__xml_parser_t *parser_ctx;
           apr_pool_t *ctx_pool = svn_pool_create(pool);
+          svn_ra_serf__xml_context_t *xmlctx;
           const char *replay_target;
 
           replay_ctx = apr_pcalloc(ctx_pool, sizeof(*replay_ctx));
-          replay_ctx->src_rev_pool = ctx_pool;
+          replay_ctx->pool = ctx_pool;
           replay_ctx->revstart_func = revstart_func;
           replay_ctx->revfinish_func = revfinish_func;
           replay_ctx->replay_baton = replay_baton;
-          replay_ctx->done = FALSE;
+          replay_ctx->done = &done;
+          replay_ctx->done_list = &done_reports;
           replay_ctx->include_path = include_path;
           replay_ctx->revision = rev;
           replay_ctx->low_water_mark = low_water_mark;
@@ -814,7 +712,7 @@ svn_ra_serf__replay_range(svn_ra_session
           replay_ctx->done_item.data = replay_ctx;
 
           /* Request all properties of a certain revision. */
-          replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
+          replay_ctx->revs_props = apr_hash_make(replay_ctx->pool);
 
           if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
             {
@@ -835,7 +733,7 @@ svn_ra_serf__replay_range(svn_ra_session
                                              replay_ctx->revprop_rev,
                                              "0", all_props,
                                              NULL,
-                                             replay_ctx->src_rev_pool));
+                                             replay_ctx->pool));
 
           /* Spin up the serf request for the PROPFIND.  */
           svn_ra_serf__request_create(replay_ctx->propfind_handler);
@@ -851,9 +749,14 @@ svn_ra_serf__replay_range(svn_ra_session
               replay_target = session->session_url.path;
             }
 
-          handler = apr_pcalloc(replay_ctx->src_rev_pool, sizeof(*handler));
+          xmlctx = svn_ra_serf__xml_context_create(replay_ttable,
+                                           replay_opened, replay_closed,
+                                           replay_cdata, replay_done,
+                                           replay_ctx,
+                                           ctx_pool);
+
+          handler = svn_ra_serf__create_expat_handler(xmlctx, NULL, ctx_pool);
 
-          handler->handler_pool = replay_ctx->src_rev_pool;
           handler->method = "REPORT";
           handler->path = replay_target;
           handler->body_delegate = create_replay_body;
@@ -861,31 +764,7 @@ svn_ra_serf__replay_range(svn_ra_session
           handler->conn = session->conns[0];
           handler->session = session;
 
-          parser_ctx = apr_pcalloc(replay_ctx->src_rev_pool,
-                                   sizeof(*parser_ctx));
-
-          /* Setup the XML parser context.
-             Because we have not one but a list of requests, the 'done' property
-             on the replay_ctx is not of much use. Instead, use 'done_list'.
-             On each handled response (succesfully or not), the parser will add
-             done_item to done_list, so by keeping track of the state of
-             done_list we know how many requests have been handled completely.
-          */
-          parser_ctx->pool = replay_ctx->src_rev_pool;
-          parser_ctx->user_data = replay_ctx;
-          parser_ctx->start = start_replay;
-          parser_ctx->end = end_replay;
-          parser_ctx->cdata = cdata_replay;
-          parser_ctx->done = &replay_ctx->done;
-          parser_ctx->done_list = &done_reports;
-          parser_ctx->done_item = &replay_ctx->done_item;
-          handler->response_handler = svn_ra_serf__handle_xml_parser;
-          handler->response_baton = parser_ctx;
           replay_ctx->report_handler = handler;
-
-          /* This is only needed to handle errors during XML parsing. */
-          replay_ctx->parser_ctx = parser_ctx;
-
           svn_ra_serf__request_create(handler);
 
           rev++;
@@ -893,26 +772,32 @@ svn_ra_serf__replay_range(svn_ra_session
         }
 
       /* Run the serf loop. */
-      SVN_ERR(svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool));
+      SVN_ERR(svn_ra_serf__context_run_wait(&done, session, pool));
 
       /* Substract the number of completely handled responses from our
          total nr. of open requests', so we'll know when to stop this loop.
          Since the message is completely handled, we can destroy its pool. */
-      done_list = done_reports;
-      while (done_list)
-        {
-          replay_context_t *ctx = (replay_context_t *)done_list->data;
-          svn_ra_serf__handler_t *done_handler = ctx->report_handler;
+      {
+        svn_ra_serf__list_t *done_list;
 
-          done_list = done_list->next;
-          SVN_ERR(svn_ra_serf__error_on_status(done_handler->sline.code,
-                                               done_handler->path,
-                                               done_handler->location));
-          svn_pool_destroy(ctx->src_rev_pool);
-          active_reports--;
-        }
+        done_list = done_reports;
+
+        done = FALSE;
+        done_reports = NULL;
 
-      done_reports = NULL;
+        while (done_list)
+          {
+            revision_report_t *ctx = (revision_report_t *)done_list->data;
+            svn_ra_serf__handler_t *done_handler = ctx->report_handler;
+
+            done_list = done_list->next;
+            SVN_ERR(svn_ra_serf__error_on_status(done_handler->sline,
+                                                 done_handler->path,
+                                                 done_handler->location));
+            svn_pool_clear(ctx->pool);
+            active_reports--;
+          }
+      }
     }
 
   return SVN_NO_ERROR;

Modified: subversion/branches/cache-server/subversion/libsvn_ra_serf/serf.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra_serf/serf.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra_serf/serf.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra_serf/serf.c Tue Oct 15 08:52:06 2013
@@ -29,6 +29,7 @@
 #include <apr_uri.h>
 #include <serf.h>
 
+#include "svn_private_config.h"
 #include "svn_pools.h"
 #include "svn_ra.h"
 #include "svn_dav.h"
@@ -46,7 +47,6 @@
 #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"
 
@@ -61,11 +61,18 @@ ra_serf_version(void)
 #define RA_SERF_DESCRIPTION \
     N_("Module for accessing a repository via WebDAV protocol using serf.")
 
+#define RA_SERF_DESCRIPTION_VER \
+    N_("Module for accessing a repository via WebDAV protocol using serf.\n" \
+       "  - using serf %d.%d.%d")
+
 /* Implements svn_ra__vtable_t.get_description(). */
 static const char *
-ra_serf_get_description(void)
+ra_serf_get_description(apr_pool_t *pool)
 {
-  return _(RA_SERF_DESCRIPTION);
+  int major, minor, patch;
+
+  serf_lib_version(&major, &minor, &patch);
+  return apr_psprintf(pool, _(RA_SERF_DESCRIPTION_VER), major, minor, patch);
 }
 
 /* Implements svn_ra__vtable_t.get_schemes(). */
@@ -119,8 +126,9 @@ load_http_auth_types(apr_pool_t *pool, s
             *authn_types |= SERF_AUTHN_NEGOTIATE;
           else
             return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
-                                     _("Invalid config: unknown http auth"
-                                       "type '%s'"), token);
+                                     _("Invalid config: unknown %s "
+                                       "'%s'"),
+                                     SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, token);
       }
     }
   else
@@ -148,6 +156,7 @@ load_config(svn_ra_serf__session_t *sess
   const char *timeout_str = NULL;
   const char *exceptions;
   apr_port_t proxy_port;
+  svn_tristate_t chunked_requests;
 
   if (config_hash)
     {
@@ -224,6 +233,12 @@ load_config(svn_ra_serf__session_t *sess
                                SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS,
                                SVN_CONFIG_DEFAULT_OPTION_HTTP_MAX_CONNECTIONS));
 
+  /* Should we use chunked transfer encoding. */ 
+  SVN_ERR(svn_config_get_tristate(config, &chunked_requests,
+                                  SVN_CONFIG_SECTION_GLOBAL,
+                                  SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS,
+                                  "auto", svn_tristate_unknown));
+
   if (config)
     server_group = svn_config_find_group(config,
                                          session->session_url.hostname,
@@ -280,6 +295,12 @@ load_config(svn_ra_serf__session_t *sess
                                    server_group,
                                    SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS,
                                    session->max_connections));
+
+      /* Should we use chunked transfer encoding. */ 
+      SVN_ERR(svn_config_get_tristate(config, &chunked_requests,
+                                      server_group,
+                                      SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS,
+                                      "auto", chunked_requests));
     }
 
   /* Don't allow the http-max-connections value to be larger than our
@@ -354,6 +375,24 @@ load_config(svn_ra_serf__session_t *sess
       session->using_proxy = FALSE;
     }
 
+  /* Setup detect_chunking and using_chunked_requests based on
+   * the chunked_requests tristate */
+  if (chunked_requests == svn_tristate_unknown)
+    {
+      session->detect_chunking = TRUE;
+      session->using_chunked_requests = TRUE;
+    }
+  else if (chunked_requests == svn_tristate_true)
+    {
+      session->detect_chunking = FALSE;
+      session->using_chunked_requests = TRUE;
+    }
+  else /* chunked_requests == svn_tristate_false */
+    {
+      session->detect_chunking = FALSE;
+      session->using_chunked_requests = FALSE;
+    }
+
   /* Setup authentication. */
   SVN_ERR(load_http_auth_types(pool, config, server_group,
                                &session->authn_types));
@@ -377,6 +416,18 @@ svn_ra_serf__progress(void *progress_bat
     }
 }
 
+/** Our User-Agent string. */
+static const char *
+get_user_agent_string(apr_pool_t *pool)
+{
+  int major, minor, patch;
+  serf_lib_version(&major, &minor, &patch);
+
+  return apr_psprintf(pool, "SVN/%s (%s) serf/%d.%d.%d",
+                      SVN_VER_NUMBER, SVN_BUILD_TARGET,
+                      major, minor, patch);
+}
+
 /* Implements svn_ra__vtable_t.open_session(). */
 static svn_error_t *
 svn_ra_serf__open(svn_ra_session_t *session,
@@ -391,6 +442,7 @@ svn_ra_serf__open(svn_ra_session_t *sess
   svn_ra_serf__session_t *serf_sess;
   apr_uri_t url;
   const char *client_string = NULL;
+  svn_error_t *err;
 
   if (corrected_url)
     *corrected_url = NULL;
@@ -440,6 +492,10 @@ svn_ra_serf__open(svn_ra_session_t *sess
      HTTP/1.1 is supported, we can upgrade. */
   serf_sess->http10 = TRUE;
 
+  /* If we switch to HTTP/1.1, then we will use chunked requests. We may disable
+     this, if we find an intervening proxy does not support chunked requests.  */
+  serf_sess->using_chunked_requests = TRUE;
+
   SVN_ERR(load_config(serf_sess, config, serf_sess->pool));
 
   serf_sess->conns[0] = apr_pcalloc(serf_sess->pool,
@@ -454,10 +510,10 @@ svn_ra_serf__open(svn_ra_session_t *sess
     SVN_ERR(callbacks->get_client_string(callback_baton, &client_string, pool));
 
   if (client_string)
-    serf_sess->useragent = apr_pstrcat(pool, USER_AGENT, " ",
+    serf_sess->useragent = apr_pstrcat(pool, get_user_agent_string(pool), " ",
                                        client_string, (char *)NULL);
   else
-    serf_sess->useragent = USER_AGENT;
+    serf_sess->useragent = get_user_agent_string(pool);
 
   /* go ahead and tell serf about the connection. */
   status =
@@ -478,7 +534,23 @@ svn_ra_serf__open(svn_ra_session_t *sess
 
   session->priv = serf_sess;
 
-  return svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, pool);
+  err = svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, pool);
+
+  /* serf should produce a usable error code instead of APR_EGENERAL */
+  if (err && err->apr_err == APR_EGENERAL)
+    err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, err,
+                            _("Connection to '%s' failed"), session_URL);
+  SVN_ERR(err);
+
+  /* We have set up a useful connection (that doesn't indication a redirect).
+     If we've been told there is possibly a worrisome proxy in our path to the
+     server AND we switched to HTTP/1.1 (chunked requests), then probe for
+     problems in any proxy.  */
+  if ((corrected_url == NULL || *corrected_url == NULL)
+      && serf_sess->detect_chunking && !serf_sess->http10)
+    SVN_ERR(svn_ra_serf__probe_proxy(serf_sess, pool));
+
+  return SVN_NO_ERROR;
 }
 
 /* Implements svn_ra__vtable_t.reparent(). */
@@ -1203,7 +1275,7 @@ svn_ra_serf__init(const svn_version_t *l
   int serf_minor;
   int serf_patch;
 
-  SVN_ERR(svn_ver_check_list(ra_serf_version(), checklist));
+  SVN_ERR(svn_ver_check_list2(ra_serf_version(), checklist, svn_ver_equal));
 
   /* Simplified version check to make sure we can safely use the
      VTABLE parameter. The RA loader does a more exhaustive check. */

Modified: subversion/branches/cache-server/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra_serf/update.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra_serf/update.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra_serf/update.c Tue Oct 15 08:52:06 2013
@@ -31,6 +31,7 @@
 
 #include <serf.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_pools.h"
 #include "svn_ra.h"
@@ -41,7 +42,6 @@
 #include "svn_base64.h"
 #include "svn_props.h"
 
-#include "svn_private_config.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_fspath.h"
 #include "private/svn_string_private.h"
@@ -49,6 +49,10 @@
 #include "ra_serf.h"
 #include "../libsvn_ra/ra_loader.h"
 
+/*
+#define USE_TRANSITION_PARSER
+*/
+
 
 /*
  * This enum represents the current state of our XML parsing for a REPORT.
@@ -64,7 +68,7 @@
  */
 typedef enum report_state_e {
     NONE = 0,
-    INITIAL = 0,
+    INITIAL = XML_STATE_INITIAL /* = 0 */,
     UPDATE_REPORT,
     TARGET_REVISION,
     OPEN_DIR,
@@ -77,6 +81,17 @@ typedef enum report_state_e {
     IGNORE_PROP_NAME,
     NEED_PROP_NAME,
     TXDELTA
+
+#ifdef NOT_USED_YET
+    ,
+
+    CHECKED_IN,
+    CHECKED_IN_HREF,
+
+    SET_PROP,
+
+    MD5_CHECKSUM
+#endif
 } report_state_e;
 
 
@@ -392,10 +407,11 @@ struct report_context_t {
 };
 
 
-#ifdef NOT_USED_YET
+#ifdef USE_TRANSITION_PARSER
 
 #define D_ "DAV:"
 #define S_ SVN_XML_NAMESPACE
+#define V_ SVN_DAV_PROP_NS_DAV
 static const svn_ra_serf__xml_transition_t update_ttable[] = {
   { INITIAL, S_, "update-report", UPDATE_REPORT,
     FALSE, { NULL }, FALSE },
@@ -410,19 +426,19 @@ static const svn_ra_serf__xml_transition
     FALSE, { "rev", "name", NULL }, TRUE },
 
   { OPEN_DIR, S_, "add-directory", ADD_DIR,
-    FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
 
   { ADD_DIR, S_, "add-directory", ADD_DIR,
-    FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
 
   { OPEN_DIR, S_, "open-file", OPEN_FILE,
     FALSE, { "rev", "name", NULL }, TRUE },
 
   { OPEN_DIR, S_, "add-file", ADD_FILE,
-    FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
 
   { ADD_DIR, S_, "add-file", ADD_FILE,
-    FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
 
   { OPEN_DIR, S_, "delete-entry", OPEN_FILE,
     FALSE, { "?rev", "name", NULL }, TRUE },
@@ -439,65 +455,45 @@ static const svn_ra_serf__xml_transition
   { ADD_DIR, S_, "absent-file", ABSENT_FILE,
     FALSE, { "name", NULL }, TRUE },
 
-  { 0 }
-};
 
+  { OPEN_DIR, D_, "checked-in", CHECKED_IN,
+    FALSE, { NULL }, FALSE },
 
+  { ADD_DIR, D_, "checked-in", CHECKED_IN,
+    FALSE, { NULL }, FALSE },
 
-/* Conforms to svn_ra_serf__xml_opened_t  */
-static svn_error_t *
-update_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)
-{
-  report_context_t *ctx = baton;
+  { ADD_FILE, D_, "checked-in", CHECKED_IN,
+    FALSE, { NULL }, FALSE },
 
-  return SVN_NO_ERROR;
-}
 
+  { OPEN_DIR, S_, "set-prop", SET_PROP,
+    TRUE, { "name", "?encoding", NULL }, TRUE },
 
+  { ADD_DIR, S_, "set-prop", SET_PROP,
+    TRUE, { "name", "?encoding", NULL }, TRUE },
 
-/* Conforms to svn_ra_serf__xml_closed_t  */
-static svn_error_t *
-update_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)
-{
-  report_context_t *ctx = baton;
+  { OPEN_FILE, S_, "set-prop", SET_PROP,
+    TRUE, { "name", "?encoding", NULL }, TRUE },
 
-  if (leaving_state == TARGET_REVISION)
-    {
-      const char *rev = svn_hash_gets(attrs, "rev");
+  { ADD_FILE, S_, "set-prop", SET_PROP,
+    TRUE, { "name", "?encoding", NULL }, TRUE },
 
-      SVN_ERR(ctx->update_editor->set_target_revision(ctx->update_baton,
-                                                      SVN_STR_TO_REV(rev),
-                                                      ctx->sess->pool));
-    }
 
-  return SVN_NO_ERROR;
-}
+  { OPEN_FILE, S_, "prop", PROP,
+    FALSE, { NULL }, FALSE },
+  { ADD_FILE, S_, "prop", PROP,
+    FALSE, { NULL }, FALSE },
 
+  { CHECKED_IN, D_, "href", CHECKED_IN_HREF,
+    TRUE, { NULL }, TRUE },
 
-/* Conforms to svn_ra_serf__xml_cdata_t  */
-static svn_error_t *
-update_cdata(svn_ra_serf__xml_estate_t *xes,
-             void *baton,
-             int current_state,
-             const char *data,
-             apr_size_t len,
-             apr_pool_t *scratch_pool)
-{
-  report_context_t *ctx = baton;
+  { PROP, V_, "md5-checksum", MD5_CHECKSUM,
+    TRUE, { NULL }, TRUE },
 
-  return SVN_NO_ERROR;
-}
+  { 0 }
+};
 
-#endif /* NOT_USED_YET */
+#endif /* USE_TRANSITION_PARSER */
 
 
 /* Returns best connection for fetching files/properties. */
@@ -1268,7 +1264,7 @@ handle_stream(serf_request_t *request,
   /* ### new field. make sure we didn't miss some initialization.  */
   SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
 
-  err = svn_ra_serf__error_on_status(fetch_ctx->handler->sline.code,
+  err = svn_ra_serf__error_on_status(fetch_ctx->handler->sline,
                                      fetch_ctx->info->name,
                                      fetch_ctx->handler->location);
   if (err)
@@ -1578,6 +1574,7 @@ fetch_file(report_context_t *ctx, report
           handler->session = ctx->sess;
 
           handler->custom_accept_encoding = TRUE;
+          handler->no_dav_headers = TRUE;
           handler->header_delegate = headers_fetch;
           handler->header_delegate_baton = fetch_ctx;
 
@@ -2539,6 +2536,73 @@ cdata_report(svn_ra_serf__xml_parser_t *
   return SVN_NO_ERROR;
 }
 
+#ifdef USE_TRANSITION_PARSER
+/* Conforms to svn_ra_serf__xml_opened_t  */
+static svn_error_t *
+update_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)
+{
+  report_context_t *ctx = baton;
+  SVN_DBG(("Enter state: %d", entered_state));
+
+  return SVN_NO_ERROR;
+}
+
+
+
+/* Conforms to svn_ra_serf__xml_closed_t  */
+static svn_error_t *
+update_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)
+{
+  report_context_t *ctx = baton;
+
+  SVN_DBG(("Leaving state: %d", leaving_state));
+
+  if (leaving_state == UPDATE_REPORT)
+    {
+      ctx->report_completed = TRUE;
+      ctx->done = TRUE;
+    }
+  else if (leaving_state == TARGET_REVISION)
+    {
+      const char *revstr = svn_hash_gets(attrs, "rev");
+      apr_int64_t rev;
+
+      SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+
+      SVN_ERR(ctx->update_editor->set_target_revision(ctx->update_baton,
+                                                      (svn_revnum_t)rev,
+                                                      ctx->sess->pool));
+    }
+
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Conforms to svn_ra_serf__xml_cdata_t  */
+static svn_error_t *
+update_cdata(svn_ra_serf__xml_estate_t *xes,
+             void *baton,
+             int current_state,
+             const char *data,
+             apr_size_t len,
+             apr_pool_t *scratch_pool)
+{
+  report_context_t *ctx = baton;
+  SVN_DBG(("Got CDATA in state %d\n", current_state));
+  return SVN_NO_ERROR;
+}
+#endif /* USE_TRANSITION_PARSER */
+
 
 /** Editor callbacks given to callers to create request body */
 
@@ -2739,7 +2803,7 @@ setup_update_report_headers(serf_bucket_
   if (report->sess->using_compression)
     {
       serf_bucket_headers_setn(headers, "Accept-Encoding",
-                               "gzip;svndiff1;q=0.9,svndiff;q=0.8");
+                               "gzip,svndiff1;q=0.9,svndiff;q=0.8");
     }
   else
     {
@@ -2758,6 +2822,9 @@ finish_report(void *report_baton,
   svn_ra_serf__session_t *sess = report->sess;
   svn_ra_serf__handler_t *handler;
   svn_ra_serf__xml_parser_t *parser_ctx;
+#ifdef USE_TRANSITION_PARSER
+  svn_ra_serf__xml_context_t *xmlctx;
+#endif
   const char *report_target;
   svn_stringbuf_t *buf = NULL;
   apr_pool_t *iterpool = svn_pool_create(pool);
@@ -2777,7 +2844,7 @@ finish_report(void *report_baton,
    * check the buffer status; but serf will fall through and create a file
    * bucket for us on the buffered svndiff handle.
    */
-  apr_file_flush(report->body_file);
+  SVN_ERR(svn_io_file_flush(report->body_file, iterpool));
 #if APR_VERSION_AT_LEAST(1, 3, 0)
   apr_file_buffer_set(report->body_file, NULL, 0);
 #endif
@@ -2787,7 +2854,16 @@ finish_report(void *report_baton,
   /* create and deliver request */
   report->path = report_target;
 
+#ifdef USE_TRANSITION_PARSER
+  xmlctx = svn_ra_serf__xml_context_create(update_ttable,
+                                           update_opened, update_closed,
+                                           update_cdata,
+                                           report,
+                                           pool);
+  handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
+#else
   handler = apr_pcalloc(pool, sizeof(*handler));
+#endif
 
   handler->handler_pool = pool;
   handler->method = "REPORT";
@@ -2801,6 +2877,7 @@ finish_report(void *report_baton,
   handler->conn = sess->conns[0];
   handler->session = sess;
 
+#ifndef USE_TRANSITION_PARSER
   parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
 
   parser_ctx->pool = pool;
@@ -2815,6 +2892,7 @@ finish_report(void *report_baton,
   handler->response_baton = parser_ctx;
 
   report->parser_ctx = parser_ctx;
+#endif
 
   svn_ra_serf__request_create(handler);
 
@@ -2865,8 +2943,10 @@ finish_report(void *report_baton,
          the connection timed out.  */
       if (APR_STATUS_IS_TIMEUP(status))
         {
-          svn_error_clear(err);
-          err = SVN_NO_ERROR;
+          /* If there is a pending error, handle it.
+             Unlikely case, as this should have made serf_context run return
+             an error but we shouldn't ignore true errors */
+          SVN_ERR(err);
           status = 0;
 
           if (sess->timeout)
@@ -2891,7 +2971,7 @@ finish_report(void *report_baton,
         {
           return svn_error_trace(
                     svn_error_compose_create(
-                        svn_ra_serf__error_on_status(handler->sline.code,
+                        svn_ra_serf__error_on_status(handler->sline,
                                                      handler->path,
                                                      handler->location),
                         err));
@@ -3250,6 +3330,14 @@ make_update_reporter(svn_ra_session_t *r
              supports inlining properties in update editor report. */
           if (sess->supports_inline_props)
             {
+              /* NOTE: both inlined properties and server->allows_bulk_update
+                 (flag SVN_DAV_ALLOW_BULK_UPDATES) were added in 1.8.0, so
+                 this code is never reached with a released version of
+                 mod_dav_svn.
+
+                 Basically by default a 1.8.0 client connecting to a 1.7.x or
+                 older server will always use bulk updates. */
+
               /* Inline props supported: do not use bulk updates. */
               use_bulk_updates = FALSE;
             }
@@ -3618,6 +3706,7 @@ svn_ra_serf__get_file(svn_ra_session_t *
           handler->session = session;
 
           handler->custom_accept_encoding = TRUE;
+          handler->no_dav_headers = TRUE;
           handler->header_delegate = headers_fetch;
           handler->header_delegate_baton = stream_ctx;
 

Modified: subversion/branches/cache-server/subversion/libsvn_ra_serf/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra_serf/util.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra_serf/util.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra_serf/util.c Tue Oct 15 08:52:06 2013
@@ -35,10 +35,10 @@
 
 #include <expat.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_dirent_uri.h"
 #include "svn_path.h"
-#include "svn_private_config.h"
 #include "svn_string.h"
 #include "svn_xml.h"
 #include "svn_props.h"
@@ -99,6 +99,7 @@ struct expat_ctx_t {
   svn_ra_serf__xml_context_t *xmlctx;
   XML_Parser parser;
   svn_ra_serf__handler_t *handler;
+  const int *expected_status;
 
   svn_error_t *inner_error;
 
@@ -191,13 +192,65 @@ construct_realm(svn_ra_serf__session_t *
 static char *
 convert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool)
 {
-  return apr_psprintf(pool, "%s, %s, %s, %s, %s (%s)",
-                      (char*)svn_hash_gets(org, "OU"),
-                      (char*)svn_hash_gets(org, "O"),
-                      (char*)svn_hash_gets(org, "L"),
-                      (char*)svn_hash_gets(org, "ST"),
-                      (char*)svn_hash_gets(org, "C"),
-                      (char*)svn_hash_gets(org, "E"));
+  const char *org_unit = svn_hash_gets(org, "OU");
+  const char *org_name = svn_hash_gets(org, "O");
+  const char *locality = svn_hash_gets(org, "L");
+  const char *state = svn_hash_gets(org, "ST");
+  const char *country = svn_hash_gets(org, "C");
+  const char *email = svn_hash_gets(org, "E");
+  svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool);
+
+  if (org_unit)
+    {
+      svn_stringbuf_appendcstr(buf, org_unit);
+      svn_stringbuf_appendcstr(buf, ", ");
+    }
+
+  if (org_name)
+    {
+      svn_stringbuf_appendcstr(buf, org_name);
+      svn_stringbuf_appendcstr(buf, ", ");
+    }
+
+  if (locality)
+    {
+      svn_stringbuf_appendcstr(buf, locality);
+      svn_stringbuf_appendcstr(buf, ", ");
+    }
+
+  if (state)
+    {
+      svn_stringbuf_appendcstr(buf, state);
+      svn_stringbuf_appendcstr(buf, ", ");
+    }
+
+  if (country)
+    {
+      svn_stringbuf_appendcstr(buf, country);
+      svn_stringbuf_appendcstr(buf, ", ");
+    }
+
+  /* Chop ', ' if any. */
+  svn_stringbuf_chop(buf, 2);
+
+  if (email)
+    {
+      svn_stringbuf_appendcstr(buf, "(");
+      svn_stringbuf_appendcstr(buf, email);
+      svn_stringbuf_appendcstr(buf, ")");
+    }
+
+  return buf->data;
+}
+
+static void append_reason(svn_stringbuf_t *errmsg, const char *reason, int *reasons)
+{
+  if (*reasons < 1)
+    svn_stringbuf_appendcstr(errmsg, _(": "));
+  else
+    svn_stringbuf_appendcstr(errmsg, _(", "));
+  svn_stringbuf_appendcstr(errmsg, reason);
+  (*reasons)++;
 }
 
 /* This function is called on receiving a ssl certificate of a server when
@@ -258,11 +311,12 @@ ssl_server_cert(void *baton, int failure
       for (i = 0; i < san->nelts; i++) {
           char *s = APR_ARRAY_IDX(san, i, char*);
           if (apr_fnmatch(s, conn->session->session_url.hostname,
-                          APR_FNM_PERIOD) == APR_SUCCESS) {
+                          APR_FNM_PERIOD | APR_FNM_CASE_BLIND) == APR_SUCCESS)
+            {
               found_matching_hostname = 1;
               cert_info.hostname = s;
               break;
-          }
+            }
       }
   }
 
@@ -270,7 +324,7 @@ ssl_server_cert(void *baton, int failure
   if (!found_matching_hostname && cert_info.hostname)
     {
       if (apr_fnmatch(cert_info.hostname, conn->session->session_url.hostname,
-                      APR_FNM_PERIOD) == APR_FNM_NOMATCH)
+                      APR_FNM_PERIOD | APR_FNM_CASE_BLIND) == APR_FNM_NOMATCH)
         {
           svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
         }
@@ -301,7 +355,35 @@ ssl_server_cert(void *baton, int failure
                          SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL);
 
   if (!server_creds)
-    return svn_error_create(SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED, NULL, NULL);
+    {
+      svn_stringbuf_t *errmsg;
+      int reasons = 0;
+
+      errmsg = svn_stringbuf_create(
+                 _("Server SSL certificate verification failed"),
+                 scratch_pool);
+
+
+      if (svn_failures & SVN_AUTH_SSL_NOTYETVALID)
+        append_reason(errmsg, _("certificate is not yet valid"), &reasons);
+
+      if (svn_failures & SVN_AUTH_SSL_EXPIRED)
+        append_reason(errmsg, _("certificate has expired"), &reasons);
+
+      if (svn_failures & SVN_AUTH_SSL_CNMISMATCH)
+        append_reason(errmsg,
+                      _("certificate issued for a different hostname"),
+                      &reasons);
+
+      if (svn_failures & SVN_AUTH_SSL_UNKNOWNCA)
+        append_reason(errmsg, _("issuer is not trusted"), &reasons);
+
+      if (svn_failures & SVN_AUTH_SSL_OTHER)
+        append_reason(errmsg, _("and other reason(s)"), &reasons);
+
+      return svn_error_create(SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED, NULL,
+                              errmsg->data);
+    }
 
   return SVN_NO_ERROR;
 }
@@ -373,10 +455,8 @@ conn_setup(apr_socket_t *sock,
         {
           conn->ssl_context = serf_bucket_ssl_encrypt_context_get(*read_bkt);
 
-#if SERF_VERSION_AT_LEAST(1,0,0)
           serf_ssl_set_hostname(conn->ssl_context,
                                 conn->session->session_url.hostname);
-#endif
 
           serf_ssl_client_cert_provider_set(conn->ssl_context,
                                             svn_ra_serf__handle_client_cert,
@@ -477,7 +557,7 @@ connection_closed(svn_ra_serf__connectio
 {
   if (why)
     {
-      SVN_ERR_MALFUNCTION();
+      return svn_error_wrap_apr(why, NULL);
     }
 
   if (conn->session->using_ssl)
@@ -625,6 +705,9 @@ apr_status_t svn_ra_serf__handle_client_
  *
  * If CONTENT_TYPE is not-NULL, it will be sent as the Content-Type header.
  *
+ * If DAV_HEADERS is non-zero, it will add standard DAV capabilites headers
+ * to request.
+ *
  * REQUEST_POOL should live for the duration of the request. Serf will
  * construct this and provide it to the request_setup callback, so we
  * should just use that one.
@@ -637,15 +720,16 @@ setup_serf_req(serf_request_t *request,
                const char *method, const char *url,
                serf_bucket_t *body_bkt, const char *content_type,
                const char *accept_encoding,
+               svn_boolean_t dav_headers,
                apr_pool_t *request_pool,
                apr_pool_t *scratch_pool)
 {
   serf_bucket_alloc_t *allocator = serf_request_get_alloc(request);
 
-#if SERF_VERSION_AT_LEAST(1, 1, 0)
   svn_spillbuf_t *buf;
+  svn_boolean_t set_CL = session->http10 || !session->using_chunked_requests;
 
-  if (session->http10 && body_bkt != NULL)
+  if (set_CL && body_bkt != NULL)
     {
       /* Ugh. Use HTTP/1.0 to talk to the server because we don't know if
          it speaks HTTP/1.1 (and thus, chunked requests), or because the
@@ -665,7 +749,6 @@ setup_serf_req(serf_request_t *request,
                                                request_pool,
                                                scratch_pool);
     }
-#endif
 
   /* Create a request bucket.  Note that this sucker is kind enough to
      add a "Host" header for us.  */
@@ -674,15 +757,13 @@ setup_serf_req(serf_request_t *request,
 
   /* Set the Content-Length value. This will also trigger an HTTP/1.0
      request (rather than the default chunked request).  */
-#if SERF_VERSION_AT_LEAST(1, 1, 0)
-  if (session->http10)
+  if (set_CL)
     {
       if (body_bkt == NULL)
         serf_bucket_request_set_CL(*req_bkt, 0);
       else
         serf_bucket_request_set_CL(*req_bkt, svn_spillbuf__get_size(buf));
     }
-#endif
 
   *hdrs_bkt = serf_bucket_request_get_headers(*req_bkt);
 
@@ -696,22 +777,25 @@ setup_serf_req(serf_request_t *request,
       serf_bucket_headers_setn(*hdrs_bkt, "Content-Type", content_type);
     }
 
-#if SERF_VERSION_AT_LEAST(1, 1, 0)
   if (session->http10)
+    {
       serf_bucket_headers_setn(*hdrs_bkt, "Connection", "keep-alive");
-#endif
+    }
 
   if (accept_encoding)
     {
       serf_bucket_headers_setn(*hdrs_bkt, "Accept-Encoding", accept_encoding);
     }
 
-  /* These headers need to be sent with every request; see issue #3255
-     ("mod_dav_svn does not pass client capabilities to start-commit
-     hooks") for why. */
-  serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH);
-  serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO);
-  serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS);
+  /* These headers need to be sent with every request except GET; see
+     issue #3255 ("mod_dav_svn does not pass client capabilities to
+     start-commit hooks") for why. */
+  if (dav_headers)
+    {
+      serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH);
+      serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO);
+      serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS);
+    }
 
   return SVN_NO_ERROR;
 }
@@ -751,8 +835,6 @@ svn_ra_serf__context_run_wait(svn_boolea
          the connection timed out.  */
       if (APR_STATUS_IS_TIMEUP(status))
         {
-          svn_error_clear(err);
-          err = SVN_NO_ERROR;
           status = 0;
 
           if (sess->timeout)
@@ -763,8 +845,11 @@ svn_ra_serf__context_run_wait(svn_boolea
                 }
               else
                 {
-                  return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
-                                          _("Connection timed out"));
+                  return
+                      svn_error_compose_create(
+                            err,
+                            svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
+                                             _("Connection timed out")));
                 }
             }
         }
@@ -776,6 +861,8 @@ svn_ra_serf__context_run_wait(svn_boolea
       SVN_ERR(err);
       if (status)
         {
+          /* ### This omits SVN_WARNING, and possibly relies on the fact that
+             ### MAX(SERF_ERROR_*) < SVN_ERR_BAD_CATEGORY_START? */
           if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST)
             {
               /* apr can't translate subversion errors to text */
@@ -810,6 +897,23 @@ svn_ra_serf__context_run_one(svn_ra_serf
   /* Wait until the response logic marks its DONE status.  */
   err = svn_ra_serf__context_run_wait(&handler->done, handler->session,
                                       scratch_pool);
+
+  /* A callback invocation has been canceled. In this simple case of
+     context_run_one, we can keep the ra-session operational by resetting
+     the connection.
+
+     If we don't do this, the next context run will notice that the connection
+     is still in the error state and will just return SVN_ERR_CEASE_INVOCATION
+     (=the last error for the connection) again  */
+  if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
+    {
+      apr_status_t status = serf_connection_reset(handler->conn->conn);
+
+      if (status)
+        err = svn_error_compose_create(err,
+                                       svn_ra_serf__wrap_err(status, NULL));
+    }
+
   if (handler->server_error)
     {
       err = svn_error_compose_create(err, handler->server_error->error);
@@ -1393,19 +1497,22 @@ inject_to_parser(svn_ra_serf__xml_parser
   int xml_status;
 
   xml_status = XML_Parse(ctx->xmlp, data, (int) len, 0);
-  if (xml_status == XML_STATUS_ERROR && !ctx->ignore_errors)
+
+  if (! ctx->ignore_errors)
     {
-      if (sl == NULL)
-        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                                 _("XML parsing failed"));
+      SVN_ERR(ctx->error);
 
-      return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                               _("XML parsing failed: (%d %s)"),
-                               sl->code, sl->reason);
-    }
+      if (xml_status != XML_STATUS_OK)
+        {
+          if (sl == NULL)
+            return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+                                     _("XML parsing failed"));
 
-  if (ctx->error && !ctx->ignore_errors)
-    return svn_error_trace(ctx->error);
+          return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+                                   _("XML parsing failed: (%d %s)"),
+                                   sl->code, sl->reason);
+        }
+     }
 
   return SVN_NO_ERROR;
 }
@@ -1486,14 +1593,26 @@ svn_ra_serf__process_pending(svn_ra_serf
   if (pending_empty &&
       parser->pending->network_eof)
     {
+      int xml_status;
       SVN_ERR_ASSERT(parser->xmlp != NULL);
 
-      /* Tell the parser that no more content will be parsed. Ignore the
-         return status. We just don't care.  */
-      (void) XML_Parse(parser->xmlp, NULL, 0, 1);
+      /* Tell the parser that no more content will be parsed. */
+      xml_status = XML_Parse(parser->xmlp, NULL, 0, 1);
 
       apr_pool_cleanup_run(parser->pool, &parser->xmlp, xml_parser_cleanup);
       parser->xmlp = NULL;
+
+      if (! parser->ignore_errors)
+        {
+          SVN_ERR(parser->error);
+
+          if (xml_status != XML_STATUS_OK)
+            {
+              return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+                                       _("XML parsing failed"));
+            }
+        }
+
       add_done_item(parser);
     }
 
@@ -1593,22 +1712,6 @@ svn_ra_serf__handle_xml_parser(serf_requ
       return svn_error_trace(err);
     }
 
-  if (ctx->headers_baton == NULL)
-    ctx->headers_baton = serf_bucket_response_get_headers(response);
-  else if (ctx->headers_baton != serf_bucket_response_get_headers(response))
-    {
-      /* We got a new response to an existing parser...
-         This tells us the connection has restarted and we should continue
-         where we stopped last time.
-       */
-
-      /* Is this a second attempt?? */
-      if (!ctx->skip_size)
-        ctx->skip_size = ctx->read_size;
-
-      ctx->read_size = 0; /* New request, nothing read */
-    }
-
   if (!ctx->xmlp)
     {
       ctx->xmlp = XML_ParserCreate(NULL);
@@ -1628,41 +1731,11 @@ svn_ra_serf__handle_xml_parser(serf_requ
       apr_size_t len;
 
       status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
-
       if (SERF_BUCKET_READ_ERROR(status))
         {
           return svn_ra_serf__wrap_err(status, NULL);
         }
 
-      ctx->read_size += len;
-
-      if (ctx->skip_size)
-        {
-          /* Handle restarted requests correctly: Skip what we already read */
-          apr_size_t skip;
-
-          if (ctx->skip_size >= ctx->read_size)
-            {
-            /* Eek.  What did the file shrink or something? */
-              if (APR_STATUS_IS_EOF(status))
-                {
-                  SVN_ERR_MALFUNCTION();
-                }
-
-              /* Skip on to the next iteration of this loop. */
-              if (APR_STATUS_IS_EAGAIN(status))
-                {
-                  return svn_ra_serf__wrap_err(status, NULL);
-                }
-              continue;
-            }
-
-          skip = (apr_size_t)(len - (ctx->read_size - ctx->skip_size));
-          data += skip;
-          len -= skip;
-          ctx->skip_size = 0;
-        }
-
       /* Note: once the callbacks invoked by inject_to_parser() sets the
          PAUSED flag, then it will not be cleared. write_to_pending() will
          only save the content. Logic outside of serf_context_run() will
@@ -1708,12 +1781,25 @@ svn_ra_serf__handle_xml_parser(serf_requ
              in the PENDING structures, then we're completely done.  */
           if (!HAS_PENDING_DATA(ctx->pending))
             {
+              int xml_status;
               SVN_ERR_ASSERT(ctx->xmlp != NULL);
 
-              /* Ignore the return status. We just don't care.  */
-              (void) XML_Parse(ctx->xmlp, NULL, 0, 1);
+              xml_status = XML_Parse(ctx->xmlp, NULL, 0, 1);
 
               apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup);
+
+              if (! ctx->ignore_errors)
+                {
+                  SVN_ERR(ctx->error);
+
+                  if (xml_status != XML_STATUS_OK)
+                    {
+                      return svn_error_create(
+                                    SVN_ERR_XML_MALFORMED, NULL,
+                                    _("The XML response contains invalid XML"));
+                    }
+                }
+
               add_done_item(ctx);
             }
 
@@ -1833,12 +1919,26 @@ handle_response(serf_request_t *request,
     {
       /* Uh-oh. Our connection died.  */
       if (handler->response_error)
-        SVN_ERR(handler->response_error(request, response, 0,
-                                        handler->response_error_baton));
-
-      /* Requeue another request for this handler.
-         ### how do we know if the handler can deal with this?!  */
-      svn_ra_serf__request_create(handler);
+        {
+          /* Give a handler chance to prevent request requeue. */
+          SVN_ERR(handler->response_error(request, response, 0,
+                                          handler->response_error_baton));
+
+          svn_ra_serf__request_create(handler);
+        }
+      /* Response error callback is not configured. Requeue another request
+         for this handler only if we didn't started to process body.
+         Return error otherwise. */
+      else if (!handler->reading_body)
+        {
+          svn_ra_serf__request_create(handler);
+        }
+      else
+        {
+          return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+                                    _("%s request on '%s' failed"),
+                                   handler->method, handler->path);
+        }
 
       return SVN_NO_ERROR;
     }
@@ -2148,7 +2248,8 @@ setup_request(serf_request_t *request,
   SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt,
                          handler->session, handler->method, handler->path,
                          body_bkt, handler->body_type, accept_encoding,
-                         request_pool, scratch_pool));
+                         !handler->no_dav_headers, request_pool,
+                         scratch_pool));
 
   if (handler->header_delegate)
     {
@@ -2158,7 +2259,7 @@ setup_request(serf_request_t *request,
                                        request_pool));
     }
 
-  return APR_SUCCESS;
+  return SVN_NO_ERROR;
 }
 
 /* Implements the serf_request_setup_t interface (which sets up both a
@@ -2390,17 +2491,17 @@ svn_ra_serf__report_resource(const char 
 }
 
 svn_error_t *
-svn_ra_serf__error_on_status(int status_code,
+svn_ra_serf__error_on_status(serf_status_line sline,
                              const char *path,
                              const char *location)
 {
-  switch(status_code)
+  switch(sline.code)
     {
       case 301:
       case 302:
       case 307:
         return svn_error_createf(SVN_ERR_RA_DAV_RELOCATED, NULL,
-                                 (status_code == 301)
+                                 (sline.code == 301)
                                  ? _("Repository moved permanently to '%s';"
                                      " please relocate")
                                  : _("Repository moved temporarily to '%s';"
@@ -2415,7 +2516,23 @@ svn_ra_serf__error_on_status(int status_
       case 423:
         return svn_error_createf(SVN_ERR_FS_NO_LOCK_TOKEN, NULL,
                                  _("'%s': no lock token available"), path);
-    }
+
+      case 411:
+        return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+                    _("DAV request failed: 411 Content length required. The "
+                      "server or an intermediate proxy does not accept "
+                      "chunked encoding. Try setting 'http-chunked-requests' "
+                      "to 'auto' or 'no' in your client configuration."));
+      case 501:
+        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+                                 _("The requested feature is not supported by "
+                                   "'%s'"), path);
+    }
+
+  if (sline.code >= 300)
+    return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+                             _("Unexpected HTTP status %d '%s' on '%s'\n"),
+                             sline.code, sline.reason, path);
 
   return SVN_NO_ERROR;
 }
@@ -2497,38 +2614,65 @@ expat_response_handler(serf_request_t *r
                        apr_pool_t *scratch_pool)
 {
   struct expat_ctx_t *ectx = baton;
+  svn_boolean_t got_expected_status;
 
-  if (!ectx->parser)
+  if (ectx->expected_status)
     {
-      ectx->parser = XML_ParserCreate(NULL);
-      apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser,
-                                xml_parser_cleanup, apr_pool_cleanup_null);
-      XML_SetUserData(ectx->parser, ectx);
-      XML_SetElementHandler(ectx->parser, expat_start, expat_end);
-      XML_SetCharacterDataHandler(ectx->parser, expat_cdata);
+      const int *status = ectx->expected_status;
+      got_expected_status = FALSE;
+
+      while (*status && ectx->handler->sline.code != *status)
+        status++;
+
+      got_expected_status = (*status) != 0;
     }
+  else
+    got_expected_status = (ectx->handler->sline.code == 200);
 
-  /* ### TODO: sline.code < 200 should really be handled by the core */
-  if ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300))
+  if ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300)
+      || ! got_expected_status)
     {
       /* By deferring to expect_empty_body(), it will make a choice on
          how to handle the body. Whatever the decision, the core handler
          will take over, and we will not be called again.  */
+
+      /* ### This handles xml bodies as svn-errors (returned via serf context
+         ### loop), but ignores non-xml errors.
+
+         Current code depends on this behavior and checks itself while other
+         continues, and then verifies if work has been performed.
+
+         ### TODO: Make error checking consistent */
+
+      /* ### If !GOT_EXPECTED_STATUS, this should always produce an error */
       return svn_error_trace(svn_ra_serf__expect_empty_body(
                                request, response, ectx->handler,
                                scratch_pool));
     }
 
+  if (!ectx->parser)
+    {
+      ectx->parser = XML_ParserCreate(NULL);
+      apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser,
+                                xml_parser_cleanup, apr_pool_cleanup_null);
+      XML_SetUserData(ectx->parser, ectx);
+      XML_SetElementHandler(ectx->parser, expat_start, expat_end);
+      XML_SetCharacterDataHandler(ectx->parser, expat_cdata);
+    }
+
   while (1)
     {
       apr_status_t status;
       const char *data;
       apr_size_t len;
       int expat_status;
+      svn_boolean_t at_eof = FALSE;
 
       status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
       if (SERF_BUCKET_READ_ERROR(status))
         return svn_ra_serf__wrap_err(status, NULL);
+      else if (APR_STATUS_IS_EOF(status))
+        at_eof = TRUE;
 
 #if 0
       /* ### move restart/skip into the core handler  */
@@ -2540,7 +2684,17 @@ expat_response_handler(serf_request_t *r
 
       /* ### should we have an IGNORE_ERRORS flag like the v1 parser?  */
 
-      expat_status = XML_Parse(ectx->parser, data, (int)len, 0 /* isFinal */);
+      expat_status = XML_Parse(ectx->parser, data, (int)len,
+                               at_eof /* isFinal */);
+
+      if (at_eof 
+          || ectx->inner_error
+          || expat_status != XML_STATUS_OK)
+        {
+          /* Release xml parser state/tables. */
+          apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
+                               xml_parser_cleanup);
+        }
 
       /* We need to check INNER_ERROR first. This is an error from the
          callbacks that has been "dropped off" for us to retrieve. On
@@ -2549,15 +2703,9 @@ expat_response_handler(serf_request_t *r
 
          If an error is not present, THEN we go ahead and look for parsing
          errors.  */
-      if (ectx->inner_error)
-        {
-          apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
-                               xml_parser_cleanup);
-          return svn_error_trace(ectx->inner_error);
-        }
-      if (expat_status == XML_STATUS_ERROR)
-        return svn_error_createf(SVN_ERR_XML_MALFORMED,
-                                 ectx->inner_error,
+      SVN_ERR(ectx->inner_error);
+      if (expat_status != XML_STATUS_OK)
+        return svn_error_createf(SVN_ERR_XML_MALFORMED, NULL,
                                  _("The %s response contains invalid XML"
                                    " (%d %s)"),
                                  ectx->handler->method,
@@ -2566,18 +2714,10 @@ expat_response_handler(serf_request_t *r
 
       /* The parsing went fine. What has the bucket told us?  */
 
-      if (APR_STATUS_IS_EOF(status))
+      if (at_eof)
         {
-          /* Tell expat we've reached the end of the content. Ignore the
-             return status. We just don't care.  */
-          (void) XML_Parse(ectx->parser, NULL, 0, 1 /* isFinal */);
-
-          svn_ra_serf__xml_context_destroy(ectx->xmlctx);
-          apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
-                               xml_parser_cleanup);
-
-          /* ### should check XMLCTX to see if it has returned to the
-             ### INITIAL state. we may have ended early...  */
+          /* Make sure we actually got xml and clean up after parsing */
+          SVN_ERR(svn_ra_serf__xml_context_done(ectx->xmlctx));
         }
 
       if (status && !SERF_BUCKET_READ_ERROR(status))
@@ -2592,6 +2732,7 @@ expat_response_handler(serf_request_t *r
 
 svn_ra_serf__handler_t *
 svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx,
+                                  const int *expected_status,
                                   apr_pool_t *result_pool)
 {
   svn_ra_serf__handler_t *handler;
@@ -2600,6 +2741,7 @@ svn_ra_serf__create_expat_handler(svn_ra
   ectx = apr_pcalloc(result_pool, sizeof(*ectx));
   ectx->xmlctx = xmlctx;
   ectx->parser = NULL;
+  ectx->expected_status = expected_status;
   ectx->cleanup_pool = result_pool;