You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by vm...@apache.org on 2012/05/29 03:39:49 UTC

svn commit: r1343447 [13/27] - in /subversion/branches/javahl-ra: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/server-side/ notes/ notes/api-errata/1.8/ notes/merge-tracking/ sub...

Modified: subversion/branches/javahl-ra/subversion/libsvn_ra_serf/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_ra_serf/util.c?rev=1343447&r1=1343446&r2=1343447&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_ra_serf/util.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_ra_serf/util.c Tue May 29 01:39:41 2012
@@ -33,11 +33,14 @@
 #include <serf.h>
 #include <serf_bucket_types.h>
 
+#include <expat.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"
 
 #include "../libsvn_ra/ra_loader.h"
 #include "private/svn_dep_compat.h"
@@ -55,6 +58,17 @@
 #define XML_STATUS_ERROR 0
 #endif
 
+#ifndef XML_VERSION_AT_LEAST
+#define XML_VERSION_AT_LEAST(major,minor,patch)                  \
+(((major) < XML_MAJOR_VERSION)                                       \
+ || ((major) == XML_MAJOR_VERSION && (minor) < XML_MINOR_VERSION)    \
+ || ((major) == XML_MAJOR_VERSION && (minor) == XML_MINOR_VERSION && \
+     (patch) <= XML_MICRO_VERSION))
+#endif /* APR_VERSION_AT_LEAST */
+
+#if XML_VERSION_AT_LEAST(1, 95, 8)
+#define EXPAT_HAS_STOPPARSER
+#endif
 
 /* Read/write chunks of this size into the spillbuf.  */
 #define PARSE_CHUNK_SIZE 8000
@@ -79,6 +93,19 @@ struct svn_ra_serf__pending_t {
                              && svn_spillbuf__get_size((p)->buf) != 0)
 
 
+struct expat_ctx_t {
+  svn_ra_serf__xml_context_t *xmlctx;
+  XML_Parser parser;
+  svn_ra_serf__handler_t *handler;
+
+  svn_error_t *inner_error;
+
+  /* Do not use this pool for allocation. It is merely recorded for running
+     the cleanup handler.  */
+  apr_pool_t *cleanup_pool;
+};
+
+
 static const apr_uint32_t serf_failure_map[][2] =
 {
   { SERF_SSL_CERT_NOTYETVALID,   SVN_AUTH_SSL_NOTYETVALID },
@@ -114,6 +141,23 @@ ssl_convert_serf_failures(int failures)
   return svn_failures;
 }
 
+
+static apr_status_t
+save_error(svn_ra_serf__session_t *session,
+           svn_error_t *err)
+{
+  if (err || session->pending_error)
+    {
+      session->pending_error = svn_error_compose_create(
+                                  session->pending_error,
+                                  err);
+      return session->pending_error->apr_err;
+    }
+
+  return APR_SUCCESS;
+}
+
+
 /* Construct the realmstring, e.g. https://svn.collab.net:443. */
 static const char *
 construct_realm(svn_ra_serf__session_t *session,
@@ -273,21 +317,10 @@ ssl_server_cert_cb(void *baton, int fail
   svn_error_t *err;
 
   subpool = svn_pool_create(session->pool);
-  err = ssl_server_cert(baton, failures, cert, subpool);
-
+  err = svn_error_trace(ssl_server_cert(baton, failures, cert, subpool));
   svn_pool_destroy(subpool);
 
-  if (err || session->pending_error)
-    {
-      session->pending_error = svn_error_compose_create(
-                                                    session->pending_error,
-                                                    err);
-
-      return session->pending_error->apr_err;
-    }
-
-  return APR_SUCCESS;
-
+  return save_error(session, err);
 }
 
 static svn_error_t *
@@ -391,24 +424,14 @@ svn_ra_serf__conn_setup(apr_socket_t *so
 {
   svn_ra_serf__connection_t *conn = baton;
   svn_ra_serf__session_t *session = conn->session;
-  apr_status_t status = APR_SUCCESS;
-
-  svn_error_t *err = conn_setup(sock,
-                                read_bkt,
-                                write_bkt,
-                                baton,
-                                pool);
-
-  if (err || session->pending_error)
-    {
-      session->pending_error = svn_error_compose_create(
-                                          session->pending_error,
-                                          err);
-
-      status = session->pending_error->apr_err;
-    }
+  svn_error_t *err;
 
-  return status;
+  err = svn_error_trace(conn_setup(sock,
+                                   read_bkt,
+                                   write_bkt,
+                                   baton,
+                                   pool));
+  return save_error(session, err);
 }
 
 
@@ -447,8 +470,7 @@ accept_head(serf_request_t *request,
 }
 
 static svn_error_t *
-connection_closed(serf_connection_t *conn,
-                  svn_ra_serf__connection_t *sc,
+connection_closed(svn_ra_serf__connection_t *conn,
                   apr_status_t why,
                   apr_pool_t *pool)
 {
@@ -457,8 +479,8 @@ connection_closed(serf_connection_t *con
       SVN_ERR_MALFUNCTION();
     }
 
-  if (sc->using_ssl)
-      sc->ssl_context = NULL;
+  if (conn->using_ssl)
+    conn->ssl_context = NULL;
 
   return SVN_NO_ERROR;
 }
@@ -469,15 +491,12 @@ svn_ra_serf__conn_closed(serf_connection
                          apr_status_t why,
                          apr_pool_t *pool)
 {
-  svn_ra_serf__connection_t *sc = closed_baton;
+  svn_ra_serf__connection_t *ra_conn = closed_baton;
   svn_error_t *err;
 
-  err = connection_closed(conn, sc, why, pool);
+  err = svn_error_trace(connection_closed(ra_conn, why, pool));
 
-  if (err)
-    sc->session->pending_error = svn_error_compose_create(
-                                            sc->session->pending_error,
-                                            err);
+  (void) save_error(ra_conn->session, err);
 }
 
 
@@ -528,20 +547,11 @@ apr_status_t svn_ra_serf__handle_client_
 {
   svn_ra_serf__connection_t *conn = data;
   svn_ra_serf__session_t *session = conn->session;
-  svn_error_t *err = handle_client_cert(data,
-                                        cert_path,
-                                        session->pool);
-
-  if (err || session->pending_error)
-    {
-      session->pending_error = svn_error_compose_create(
-                                                    session->pending_error,
-                                                    err);
+  svn_error_t *err;
 
-      return session->pending_error->apr_err;
-    }
+  err = svn_error_trace(handle_client_cert(data, cert_path, session->pool));
 
-  return APR_SUCCESS;
+  return save_error(session, err);
 }
 
 /* Implementation for svn_ra_serf__handle_client_cert_pw */
@@ -590,21 +600,14 @@ apr_status_t svn_ra_serf__handle_client_
 {
   svn_ra_serf__connection_t *conn = data;
   svn_ra_serf__session_t *session = conn->session;
-  svn_error_t *err = handle_client_cert_pw(data,
-                                           cert_path,
-                                           password,
-                                           session->pool);
+  svn_error_t *err;
 
-  if (err || session->pending_error)
-    {
-      session->pending_error = svn_error_compose_create(
-                                          session->pending_error,
-                                          err);
+  err = svn_error_trace(handle_client_cert_pw(data,
+                                              cert_path,
+                                              password,
+                                              session->pool));
 
-      return session->pending_error->apr_err;
-    }
-
-  return APR_SUCCESS;
+  return save_error(session, err);
 }
 
 
@@ -756,6 +759,28 @@ svn_ra_serf__context_run_wait(svn_boolea
 }
 
 
+svn_error_t *
+svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler,
+                             apr_pool_t *scratch_pool)
+{
+  svn_error_t *err;
+
+  /* Create a serf request based on HANDLER.  */
+  svn_ra_serf__request_create(handler);
+
+  /* Wait until the response logic marks its DONE status.  */
+  err = svn_ra_serf__context_run_wait(&handler->done, handler->session,
+                                      scratch_pool);
+  if (handler->server_error)
+    {
+      err = svn_error_compose_create(err, handler->server_error->error);
+      handler->server_error = NULL;
+    }
+
+  return svn_error_trace(err);
+}
+
+
 /*
  * Expat callback invoked on a start element tag for an error response.
  */
@@ -819,15 +844,10 @@ end_error(svn_ra_serf__xml_parser_t *par
       /* On the server dav_error_response_tag() will add a leading
          and trailing newline if DEBUG_CR is defined in mod_dav.h,
          so remove any such characters here. */
-      apr_size_t len;
-      const char *cd = ctx->cdata->data;
-      if (*cd == '\n')
-        ++cd;
-      len = strlen(cd);
-      if (len > 0 && cd[len-1] == '\n')
-        --len;
+      svn_stringbuf_strip_whitespace(ctx->cdata);
 
-      ctx->error->message = apr_pstrmemdup(ctx->error->pool, cd, len);
+      ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data,
+                                           ctx->cdata->len);
       ctx->collect_cdata = FALSE;
     }
 
@@ -855,6 +875,49 @@ cdata_error(svn_ra_serf__xml_parser_t *p
   return SVN_NO_ERROR;
 }
 
+
+static apr_status_t
+drain_bucket(serf_bucket_t *bucket)
+{
+  /* Read whatever is in the bucket, and just drop it.  */
+  while (1)
+    {
+      apr_status_t status;
+      const char *data;
+      apr_size_t len;
+
+      status = serf_bucket_read(bucket, SERF_READ_ALL_AVAIL, &data, &len);
+      if (status)
+        return status;
+    }
+}
+
+
+static svn_ra_serf__server_error_t *
+begin_error_parsing(svn_ra_serf__xml_start_element_t start,
+                    svn_ra_serf__xml_end_element_t end,
+                    svn_ra_serf__xml_cdata_chunk_handler_t cdata,
+                    apr_pool_t *result_pool)
+{
+  svn_ra_serf__server_error_t *server_err;
+
+  server_err = apr_pcalloc(result_pool, sizeof(*server_err));
+  server_err->init = TRUE;
+  server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL);
+  server_err->has_xml_response = TRUE;
+  server_err->contains_precondition_error = FALSE;
+  server_err->cdata = svn_stringbuf_create_empty(server_err->error->pool);
+  server_err->collect_cdata = FALSE;
+  server_err->parser.pool = server_err->error->pool;
+  server_err->parser.user_data = server_err;
+  server_err->parser.start = start;
+  server_err->parser.end = end;
+  server_err->parser.cdata = cdata;
+  server_err->parser.ignore_errors = TRUE;
+
+  return server_err;
+}
+
 /* Implements svn_ra_serf__response_handler_t */
 svn_error_t *
 svn_ra_serf__handle_discard_body(serf_request_t *request,
@@ -877,6 +940,8 @@ svn_ra_serf__handle_discard_body(serf_re
           val = serf_bucket_headers_get(hdrs, "Content-Type");
           if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
             {
+              /* ### we should figure out how to reuse begin_error_parsing  */
+
               server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL);
               server_err->has_xml_response = TRUE;
               server_err->contains_precondition_error = FALSE;
@@ -915,9 +980,7 @@ svn_ra_serf__handle_discard_body(serf_re
 
     }
 
-  status = svn_ra_serf__response_discard_handler(request, response,
-                                                 NULL, pool);
-
+  status = drain_bucket(response);
   if (status)
     return svn_error_wrap_apr(status, NULL);
 
@@ -930,22 +993,7 @@ svn_ra_serf__response_discard_handler(se
                                       void *baton,
                                       apr_pool_t *pool)
 {
-  /* Just loop through and discard the body. */
-  while (1)
-    {
-      apr_status_t status;
-      const char *data;
-      apr_size_t len;
-
-      status = serf_bucket_read(response, SERF_READ_ALL_AVAIL, &data, &len);
-
-      if (status)
-        {
-          return status;
-        }
-
-      /* feed me */
-    }
+  return drain_bucket(response);
 }
 
 const char *
@@ -960,41 +1008,60 @@ svn_ra_serf__response_get_location(serf_
   return val ? svn_urlpath__canonicalize(val, pool) : NULL;
 }
 
+
 /* Implements svn_ra_serf__response_handler_t */
 svn_error_t *
-svn_ra_serf__handle_status_only(serf_request_t *request,
-                                serf_bucket_t *response,
-                                void *baton,
-                                apr_pool_t *pool)
+svn_ra_serf__expect_empty_body(serf_request_t *request,
+                               serf_bucket_t *response,
+                               void *baton,
+                               apr_pool_t *scratch_pool)
 {
-  svn_error_t *err;
-  svn_ra_serf__simple_request_context_t *ctx = baton;
+  svn_ra_serf__handler_t *handler = baton;
+  serf_bucket_t *hdrs;
+  const char *val;
 
-  SVN_ERR_ASSERT(ctx->pool);
+  /* This function is just like handle_multistatus_only() except for the
+     XML parsing callbacks. We want to look for the human-readable element.  */
 
-  err = svn_ra_serf__handle_discard_body(request, response,
-                                         &ctx->server_error, pool);
+  /* We should see this just once, in order to initialize SERVER_ERROR.
+     At that point, the core error processing will take over. If we choose
+     not to parse an error, then we'll never return here (because we
+     change the response handler).  */
+  SVN_ERR_ASSERT(handler->server_error == NULL);
 
-  if (err && APR_STATUS_IS_EOF(err->apr_err))
+  hdrs = serf_bucket_response_get_headers(response);
+  val = serf_bucket_headers_get(hdrs, "Content-Type");
+  if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
     {
-      serf_status_line sl;
-      apr_status_t status;
+      svn_ra_serf__server_error_t *server_err;
 
-      status = serf_bucket_response_status(response, &sl);
-      if (SERF_BUCKET_READ_ERROR(status))
-        {
-          return svn_error_wrap_apr(status, NULL);
-        }
+      server_err = begin_error_parsing(start_error, end_error, cdata_error,
+                                       handler->handler_pool);
 
-      ctx->status = sl.code;
-      ctx->reason = sl.reason ? apr_pstrdup(ctx->pool, sl.reason) : NULL;
-      ctx->location = svn_ra_serf__response_get_location(response, ctx->pool);
-      ctx->done = TRUE;
+      /* Get the parser to set our DONE flag.  */
+      server_err->parser.done = &handler->done;
+
+      handler->server_error = server_err;
+    }
+  else
+    {
+      /* ### hmm. this is a bit early. we have not seen EOF. if the
+         ### caller thinks we are "done", then it may never call into
+         ### serf_context_run() again to flush the response.  */
+      handler->done = TRUE;
+
+      /* The body was not text/xml, so we don't know what to do with it.
+         Toss anything that arrives.  */
+      handler->discard_body = TRUE;
     }
 
-  return svn_error_trace(err);
+  /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it
+     to call the response handler again. That will start up the XML parsing,
+     or it will be dropped on the floor (per the decision above).  */
+  return SVN_NO_ERROR;
 }
 
+
 /* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric
    status code into *STATUS_CODE_OUT.  Ignores leading whitespace. */
 static svn_error_t *
@@ -1076,6 +1143,9 @@ end_207(svn_ra_serf__xml_parser_t *parse
     }
   if (ctx->in_error && strcmp(name.name, "responsedescription") == 0)
     {
+      /* Remove leading newline added by DEBUG_CR on server */
+      svn_stringbuf_strip_whitespace(ctx->cdata);
+
       ctx->collect_cdata = FALSE;
       ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data,
                                            ctx->cdata->len);
@@ -1126,90 +1196,55 @@ svn_error_t *
 svn_ra_serf__handle_multistatus_only(serf_request_t *request,
                                      serf_bucket_t *response,
                                      void *baton,
-                                     apr_pool_t *pool)
+                                     apr_pool_t *scratch_pool)
 {
-  svn_error_t *err;
-  svn_ra_serf__simple_request_context_t *ctx = baton;
-  svn_ra_serf__server_error_t *server_err = &ctx->server_error;
+  svn_ra_serf__handler_t *handler = baton;
 
-  SVN_ERR_ASSERT(ctx->pool);
+  /* This function is just like expect_empty_body() except for the
+     XML parsing callbacks. We are looking for very limited pieces of
+     the multistatus response.  */
+
+  /* We should see this just once, in order to initialize SERVER_ERROR.
+     At that point, the core error processing will take over. If we choose
+     not to parse an error, then we'll never return here (because we
+     change the response handler).  */
+  SVN_ERR_ASSERT(handler->server_error == NULL);
 
-  /* If necessary, initialize our XML parser. */
-  if (server_err && !server_err->init)
     {
       serf_bucket_t *hdrs;
       const char *val;
 
-      server_err->init = TRUE;
       hdrs = serf_bucket_response_get_headers(response);
       val = serf_bucket_headers_get(hdrs, "Content-Type");
       if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
         {
-          server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL);
-          server_err->has_xml_response = TRUE;
-          server_err->contains_precondition_error = FALSE;
-          server_err->cdata = svn_stringbuf_create_empty(server_err->error->pool);
-          server_err->collect_cdata = FALSE;
-          server_err->parser.pool = server_err->error->pool;
-          server_err->parser.user_data = server_err;
-          server_err->parser.start = start_207;
-          server_err->parser.end = end_207;
-          server_err->parser.cdata = cdata_207;
-          server_err->parser.done = &ctx->done;
-          server_err->parser.ignore_errors = TRUE;
-        }
-      else
-        {
-          ctx->done = TRUE;
-          server_err->error = SVN_NO_ERROR;
-        }
-    }
+          svn_ra_serf__server_error_t *server_err;
 
-  /* If server_err->error still contains APR_SUCCESS, it means that we
-     have not successfully parsed the XML yet. */
-  if (server_err && server_err->error
-      && server_err->error->apr_err == APR_SUCCESS)
-    {
-      err = svn_ra_serf__handle_xml_parser(request, response,
-                                           &server_err->parser, pool);
+          server_err = begin_error_parsing(start_207, end_207, cdata_207,
+                                           handler->handler_pool);
 
-      /* APR_EOF will be returned when parsing is complete.  If we see
-         any other error, return it immediately.  In practice the only
-         other error we expect to see is APR_EAGAIN, which indicates that
-         we could not parse the XML because the contents are not yet
-         available to be read. */
-      if (!err || !APR_STATUS_IS_EOF(err->apr_err))
-        {
-          return svn_error_trace(err);
+          /* Get the parser to set our DONE flag.  */
+          server_err->parser.done = &handler->done;
+
+          handler->server_error = server_err;
         }
-      else if (ctx->done && server_err->error->apr_err == APR_SUCCESS)
+      else
         {
-          svn_error_clear(server_err->error);
-          server_err->error = SVN_NO_ERROR;
-        }
+          /* ### hmm. this is a bit early. we have not seen EOF. if the
+             ### caller thinks we are "done", then it may never call into
+             ### serf_context_run() again to flush the response.  */
+          handler->done = TRUE;
 
-      svn_error_clear(err);
-    }
-
-  err = svn_ra_serf__handle_discard_body(request, response, NULL, pool);
-
-  if (err && APR_STATUS_IS_EOF(err->apr_err))
-    {
-      serf_status_line sl;
-      apr_status_t status;
-
-      status = serf_bucket_response_status(response, &sl);
-      if (SERF_BUCKET_READ_ERROR(status))
-        {
-          return svn_error_wrap_apr(status, NULL);
+          /* The body was not text/xml, so we don't know what to do with it.
+             Toss anything that arrives.  */
+          handler->discard_body = TRUE;
         }
-
-      ctx->status = sl.code;
-      ctx->reason = sl.reason ? apr_pstrdup(ctx->pool, sl.reason) : NULL;
-      ctx->location = svn_ra_serf__response_get_location(response, ctx->pool);
     }
 
-  return svn_error_trace(err);
+  /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it
+     to call the response handler again. That will start up the XML parsing,
+     or it will be dropped on the floor (per the decision above).  */
+  return SVN_NO_ERROR;
 }
 
 
@@ -1326,7 +1361,7 @@ inject_to_parser(svn_ra_serf__xml_parser
 {
   int xml_status;
 
-  xml_status = XML_Parse(ctx->xmlp, data, len, 0);
+  xml_status = XML_Parse(ctx->xmlp, data, (int) len, 0);
   if (xml_status == XML_STATUS_ERROR && !ctx->ignore_errors)
     {
       if (sl == NULL)
@@ -1415,6 +1450,21 @@ svn_ra_serf__process_pending(svn_ra_serf
 }
 
 
+/* ### this is still broken conceptually. just shifting incrementally... */
+static svn_error_t *
+handle_server_error(serf_request_t *request,
+                    serf_bucket_t *response,
+                    apr_pool_t *scratch_pool)
+{
+  svn_ra_serf__server_error_t server_err = { 0 };
+
+  svn_error_clear(svn_ra_serf__handle_discard_body(request, response,
+                                                   &server_err, scratch_pool));
+
+  return server_err.error;
+}
+
+
 /* Implements svn_ra_serf__response_handler_t */
 svn_error_t *
 svn_ra_serf__handle_xml_parser(serf_request_t *request,
@@ -1427,31 +1477,19 @@ svn_ra_serf__handle_xml_parser(serf_requ
   svn_ra_serf__xml_parser_t *ctx = baton;
   svn_error_t *err;
 
+  /* ### get the HANDLER rather than fetching this.  */
   status = serf_bucket_response_status(response, &sl);
   if (SERF_BUCKET_READ_ERROR(status))
     {
       return svn_error_wrap_apr(status, NULL);
     }
 
-  if (ctx->status_code)
-    {
-      *ctx->status_code = sl.code;
-    }
-
-  if (sl.code == 301 || sl.code == 302 || sl.code == 307)
-    {
-      ctx->location = svn_ra_serf__response_get_location(response, ctx->pool);
-    }
-
   /* Woo-hoo.  Nothing here to see.  */
   if (sl.code == 404 && ctx->ignore_errors == FALSE)
     {
-      /* If our caller won't know about the 404, abort() for now. */
-      SVN_ERR_ASSERT(ctx->status_code);
-
       add_done_item(ctx);
 
-      err = svn_ra_serf__handle_server_error(request, response, pool);
+      err = handle_server_error(request, response, pool);
 
       SVN_ERR(svn_error_compose_create(
         svn_ra_serf__handle_discard_body(request, response, NULL, pool),
@@ -1591,19 +1629,6 @@ svn_ra_serf__handle_xml_parser(serf_requ
   /* not reached */
 }
 
-/* Implements svn_ra_serf__response_handler_t */
-svn_error_t *
-svn_ra_serf__handle_server_error(serf_request_t *request,
-                                 serf_bucket_t *response,
-                                 apr_pool_t *pool)
-{
-  svn_ra_serf__server_error_t server_err = { 0 };
-
-  svn_error_clear(svn_ra_serf__handle_discard_body(request, response,
-                                                   &server_err, pool));
-
-  return server_err.error;
-}
 
 apr_status_t
 svn_ra_serf__credentials_callback(char **username, char **password,
@@ -1612,8 +1637,8 @@ svn_ra_serf__credentials_callback(char *
                                   const char *realm,
                                   apr_pool_t *pool)
 {
-  svn_ra_serf__handler_t *ctx = baton;
-  svn_ra_serf__session_t *session = ctx->session;
+  svn_ra_serf__handler_t *handler = baton;
+  svn_ra_serf__session_t *session = handler->session;
   void *creds;
   svn_auth_cred_simple_t *simple_creds;
   svn_error_t *err;
@@ -1642,8 +1667,7 @@ svn_ra_serf__credentials_callback(char *
 
       if (err)
         {
-          session->pending_error
-              = svn_error_compose_create(session->pending_error, err);
+          (void) save_error(session, err);
           return err->apr_err;
         }
 
@@ -1652,13 +1676,11 @@ svn_ra_serf__credentials_callback(char *
       if (!creds || session->auth_attempts > 4)
         {
           /* No more credentials. */
-          session->pending_error
-              = svn_error_compose_create(
-                    session->pending_error,
-                    svn_error_create(
-                          SVN_ERR_AUTHN_FAILED, NULL,
-                          _("No more credentials or we tried too many times.\n"
-                            "Authentication failed")));
+          (void) save_error(session,
+                            svn_error_create(
+                              SVN_ERR_AUTHN_FAILED, NULL,
+                              _("No more credentials or we tried too many"
+                                "times.\nAuthentication failed")));
           return SVN_ERR_AUTHN_FAILED;
         }
 
@@ -1676,21 +1698,20 @@ svn_ra_serf__credentials_callback(char *
       if (!session->proxy_username || session->proxy_auth_attempts > 4)
         {
           /* No more credentials. */
-          session->pending_error
-              = svn_error_compose_create(
-                      ctx->session->pending_error,
-                      svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
-                                       _("Proxy authentication failed")));
+          (void) save_error(session, 
+                            svn_error_create(
+                              SVN_ERR_AUTHN_FAILED, NULL,
+                              _("Proxy authentication failed")));
           return SVN_ERR_AUTHN_FAILED;
         }
     }
 
-  ctx->conn->last_status_code = code;
+  handler->conn->last_status_code = code;
 
   return APR_SUCCESS;
 }
 
-/* Wait for HTTP response status and headers, and invoke CTX->
+/* Wait for HTTP response status and headers, and invoke HANDLER->
    response_handler() to carry out operation-specific processing.
    Afterwards, check for connection close.
 
@@ -1700,47 +1721,79 @@ svn_ra_serf__credentials_callback(char *
 static svn_error_t *
 handle_response(serf_request_t *request,
                 serf_bucket_t *response,
-                svn_ra_serf__handler_t *ctx,
+                svn_ra_serf__handler_t *handler,
                 apr_status_t *serf_status,
-                apr_pool_t *pool)
+                apr_pool_t *scratch_pool)
 {
-  serf_status_line sl;
   apr_status_t status;
+  svn_error_t *err;
+
+  /* ### need to verify whether this already gets init'd on every
+     ### successful exit. for an error-exit, it will (properly) be
+     ### ignored by the caller.  */
+  *serf_status = APR_SUCCESS;
 
   if (!response)
     {
-      /* Uh-oh.  Our connection died.  Requeue. */
-      if (ctx->response_error)
-        SVN_ERR(ctx->response_error(request, response, 0,
-                                    ctx->response_error_baton));
+      /* 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);
 
-      svn_ra_serf__request_create(ctx);
-
-      return APR_SUCCESS;
+      return SVN_NO_ERROR;
     }
 
-  status = serf_bucket_response_status(response, &sl);
-  if (SERF_BUCKET_READ_ERROR(status))
-    {
-      *serf_status = status;
-      return SVN_NO_ERROR; /* Handled by serf */
-    }
-  if (!sl.version && (APR_STATUS_IS_EOF(status) ||
-                      APR_STATUS_IS_EAGAIN(status)))
+  /* If we're reading the body, then skip all this preparation.  */
+  if (handler->reading_body)
+    goto process_body;
+
+  /* Copy the Status-Line info into HANDLER, if we don't yet have it.  */
+  if (handler->sline.version == 0)
     {
-      *serf_status = status;
-      return SVN_NO_ERROR; /* Handled by serf */
+      serf_status_line sl;
+
+      status = serf_bucket_response_status(response, &sl);
+      if (status != APR_SUCCESS)
+        {
+          /* The response line is not (yet) ready, or some other error.  */
+          *serf_status = status;
+          return SVN_NO_ERROR; /* Handled by serf */
+        }
+
+      /* If we got APR_SUCCESS, then we should have Status-Line info.  */
+      SVN_ERR_ASSERT(sl.version != 0);
+
+      handler->sline = sl;
+      handler->sline.reason = apr_pstrdup(handler->handler_pool, sl.reason);
     }
 
+  /* Keep reading from the network until we've read all the headers.  */
   status = serf_bucket_response_wait_for_headers(response);
   if (status)
     {
+      /* The typical "error" will be APR_EAGAIN, meaning that more input
+         from the network is required to complete the reading of the
+         headers.  */
       if (!APR_STATUS_IS_EOF(status))
         {
+          /* Either the headers are not (yet) complete, or there really
+             was an error.  */
           *serf_status = status;
           return SVN_NO_ERROR;
         }
 
+      /* wait_for_headers() will return EOF if there is no body in this
+         response, or if we completely read the body. The latter is not
+         true since we would have set READING_BODY to get the body read,
+         and we would not be back to this code block.
+
+         It can also return EOF if we truly hit EOF while (say) processing
+         the headers. aka Badness.  */
+
       /* Cases where a lack of a response body (via EOF) is okay:
        *  - A HEAD request
        *  - 204/304 response
@@ -1749,102 +1802,170 @@ handle_response(serf_request_t *request,
        * the server closed on us early or we're reading too much.  Either way,
        * scream loudly.
        */
-      if (strcmp(ctx->method, "HEAD") != 0 && sl.code != 204 && sl.code != 304)
-        {
-          svn_error_t *err =
-              svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
-                                svn_error_wrap_apr(status, NULL),
-                                _("Premature EOF seen from server "
-                                  "(http status=%d)"), sl.code);
-          /* This discard may be no-op, but let's preserve the algorithm
-             used elsewhere in this function for clarity's sake. */
-          svn_ra_serf__response_discard_handler(request, response, NULL, pool);
+      if (strcmp(handler->method, "HEAD") != 0
+          && handler->sline.code != 204
+          && handler->sline.code != 304)
+        {
+          err = svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
+                                  svn_error_wrap_apr(status, NULL),
+                                  _("Premature EOF seen from server"
+                                    " (http status=%d)"),
+                                  handler->sline.code);
+
+          /* In case anything else arrives... discard it.  */
+          handler->discard_body = TRUE;
+
           return err;
         }
     }
 
-  if (ctx->conn->last_status_code == 401 && sl.code < 400)
-    {
-      SVN_ERR(svn_auth_save_credentials(ctx->session->auth_state,
-                                        ctx->session->pool));
-      ctx->session->auth_attempts = 0;
-      ctx->session->auth_state = NULL;
-    }
-
-  ctx->conn->last_status_code = sl.code;
-
-  if (sl.code == 405 || sl.code == 409 || sl.code >= 500)
+  /* ... and set up the header fields in HANDLER.  */
+  handler->location = svn_ra_serf__response_get_location(
+                          response, handler->handler_pool);
+
+  /* On the last request, we failed authentication. We succeeded this time,
+     so let's save away these credentials.  */
+  if (handler->conn->last_status_code == 401 && handler->sline.code < 400)
+    {
+      SVN_ERR(svn_auth_save_credentials(handler->session->auth_state,
+                                        handler->session->pool));
+      handler->session->auth_attempts = 0;
+      handler->session->auth_state = NULL;
+    }
+  handler->conn->last_status_code = handler->sline.code;
+
+  if (handler->sline.code == 405
+      || handler->sline.code == 409
+      || handler->sline.code >= 500)
     {
       /* 405 Method Not allowed.
          409 Conflict: can indicate a hook error.
          5xx (Internal) Server error. */
-      SVN_ERR(svn_ra_serf__handle_server_error(request, response, pool));
+      /* ### this is completely wrong. it only catches the current network
+         ### packet. we need ongoing parsing. see SERVER_ERROR down below
+         ### in the process_body: area. we'll eventually move to that.  */
+      SVN_ERR(handle_server_error(request, response, scratch_pool));
 
-      if (!ctx->session->pending_error)
+      if (!handler->session->pending_error)
         {
           apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
 
           /* 405 == Method Not Allowed (Occurs when trying to lock a working
              copy path which no longer exists at HEAD in the repository. */
-
-          if (sl.code == 405 && !strcmp(ctx->method, "LOCK"))
+          if (handler->sline.code == 405
+              && strcmp(handler->method, "LOCK") == 0)
             apr_err = SVN_ERR_FS_OUT_OF_DATE;
 
-          return
-              svn_error_createf(apr_err, NULL,
-                                _("%s request on '%s' failed: %d %s"),
-                                ctx->method, ctx->path, sl.code, sl.reason);
+          return svn_error_createf(apr_err, NULL,
+                                   _("%s request on '%s' failed: %d %s"),
+                                   handler->method, handler->path,
+                                   handler->sline.code, handler->sline.reason);
         }
 
       return SVN_NO_ERROR; /* Error is set in caller */
     }
-  else
+
+  /* Stop processing the above, on every packet arrival.  */
+  handler->reading_body = TRUE;
+
+ process_body:
+
+  /* We've been instructed to ignore the body. Drain whatever is present.  */
+  if (handler->discard_body)
     {
-      svn_error_t *err;
+      *serf_status = drain_bucket(response);
+      return SVN_NO_ERROR;
+    }
 
-      err = ctx->response_handler(request,response, ctx->response_baton, pool);
+  /* If we are supposed to parse the body as a server_error, then do
+     that now.  */
+  if (handler->server_error != NULL)
+    {
+      err = svn_ra_serf__handle_xml_parser(request, response,
+                                           &handler->server_error->parser,
+                                           scratch_pool);
 
-      if (err
-          && (!SERF_BUCKET_READ_ERROR(err->apr_err)
-               || APR_STATUS_IS_ECONNRESET(err->apr_err)))
-        {
-          /* These errors are special cased in serf
-             ### We hope no handler returns these by accident. */
-          *serf_status = err->apr_err;
-          svn_error_clear(err);
-          return SVN_NO_ERROR;
+      /* APR_EOF will be returned when parsing is complete.  If we see
+         any other error, return it immediately.
+
+         In practice the only other error we expect to see is APR_EAGAIN,
+         which indicates the network has no more data right now. This
+         svn_error_t will get unwrapped, and that APR_EAGAIN will be
+         returned to serf. We'll get called later, when more network data
+         is available.  */
+      if (!err || !APR_STATUS_IS_EOF(err->apr_err))
+        return svn_error_trace(err);
+
+      /* Clear the EOF. We don't need it.  */
+      svn_error_clear(err);
+
+      /* If the parsing is done, and we did not extract an error, then
+         simply toss everything, and anything else that might arrive.
+         The higher-level code will need to investigate HANDLER->SLINE,
+         as we have no further information for them.  */
+      if (handler->done
+          && handler->server_error->error->apr_err == APR_SUCCESS)
+        {
+          svn_error_clear(handler->server_error->error);
+
+          /* Stop parsing for a server error.  */
+          handler->server_error = NULL;
+
+          /* If anything arrives after this, then just discard it.  */
+          handler->discard_body = TRUE;
         }
 
-      return svn_error_trace(err);
+      *serf_status = APR_EOF;
+      return SVN_NO_ERROR;
+    }
+
+  /* Pass the body along to the registered response handler.  */
+  err = handler->response_handler(request, response,
+                                  handler->response_baton,
+                                  scratch_pool);
+
+  if (err
+      && (!SERF_BUCKET_READ_ERROR(err->apr_err)
+          || APR_STATUS_IS_ECONNRESET(err->apr_err)))
+    {
+      /* These errors are special cased in serf
+         ### We hope no handler returns these by accident. */
+      *serf_status = err->apr_err;
+      svn_error_clear(err);
+      return SVN_NO_ERROR;
     }
+
+  return svn_error_trace(err);
 }
 
 
 /* Implements serf_response_handler_t for handle_response. Storing
-   errors in ctx->session->pending_error if appropriate. */
+   errors in handler->session->pending_error if appropriate. */
 static apr_status_t
 handle_response_cb(serf_request_t *request,
                    serf_bucket_t *response,
                    void *baton,
-                   apr_pool_t *pool)
+                   apr_pool_t *scratch_pool)
 {
-  svn_ra_serf__handler_t *ctx = baton;
-  svn_ra_serf__session_t *session = ctx->session;
+  svn_ra_serf__handler_t *handler = baton;
   svn_error_t *err;
-  apr_status_t serf_status = APR_SUCCESS;
-
-  err = svn_error_trace(
-          handle_response(request, response, ctx, &serf_status, pool));
+  apr_status_t inner_status;
+  apr_status_t outer_status;
 
-  if (err || session->pending_error)
-    {
-      session->pending_error = svn_error_compose_create(session->pending_error,
-                                                        err);
+  err = svn_error_trace(handle_response(request, response,
+                                        handler, &inner_status,
+                                        scratch_pool));
+
+  /* Select the right status value to return.  */
+  outer_status = save_error(handler->session, err);
+  if (!outer_status)
+    outer_status = inner_status;
+
+  /* Make sure the DONE flag is set properly.  */
+  if (APR_STATUS_IS_EOF(outer_status) || APR_STATUS_IS_EOF(inner_status))
+    handler->done = TRUE;
 
-      serf_status = session->pending_error->apr_err;
-    }
-
-  return serf_status;
+  return outer_status;
 }
 
 /* Perform basic request setup, with special handling for HEAD requests,
@@ -1852,34 +1973,21 @@ handle_response_cb(serf_request_t *reque
    headers and body. */
 static svn_error_t *
 setup_request(serf_request_t *request,
-              svn_ra_serf__handler_t *ctx,
+              svn_ra_serf__handler_t *handler,
               serf_bucket_t **req_bkt,
-              serf_response_acceptor_t *acceptor,
-              void **acceptor_baton,
-              serf_response_handler_t *handler,
-              void **handler_baton,
               apr_pool_t *request_pool,
               apr_pool_t *scratch_pool)
 {
   serf_bucket_t *body_bkt;
   serf_bucket_t *headers_bkt;
 
-  /* Default response acceptor.  */
-  *acceptor = accept_response;
-  *acceptor_baton = ctx->session;
-
-  if (strcmp(ctx->method, "HEAD") == 0)
-    {
-      *acceptor = accept_head;
-    }
-
-  if (ctx->body_delegate)
+  if (handler->body_delegate)
     {
       serf_bucket_alloc_t *bkt_alloc = serf_request_get_alloc(request);
 
       /* ### should pass the scratch_pool  */
-      SVN_ERR(ctx->body_delegate(&body_bkt, ctx->body_delegate_baton,
-                                 bkt_alloc, request_pool));
+      SVN_ERR(handler->body_delegate(&body_bkt, handler->body_delegate_baton,
+                                     bkt_alloc, request_pool));
     }
   else
     {
@@ -1887,20 +1995,18 @@ setup_request(serf_request_t *request,
     }
 
   SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt,
-                         ctx->conn, ctx->method, ctx->path,
-                         body_bkt, ctx->body_type,
+                         handler->conn, handler->method, handler->path,
+                         body_bkt, handler->body_type,
                          request_pool, scratch_pool));
 
-  if (ctx->header_delegate)
+  if (handler->header_delegate)
     {
       /* ### should pass the scratch_pool  */
-      SVN_ERR(ctx->header_delegate(headers_bkt, ctx->header_delegate_baton,
-                                   request_pool));
+      SVN_ERR(handler->header_delegate(headers_bkt,
+                                       handler->header_delegate_baton,
+                                       request_pool));
     }
 
-  *handler = handle_response_cb;
-  *handler_baton = ctx;
-
   return APR_SUCCESS;
 }
 
@@ -1913,44 +2019,58 @@ setup_request_cb(serf_request_t *request
               serf_bucket_t **req_bkt,
               serf_response_acceptor_t *acceptor,
               void **acceptor_baton,
-              serf_response_handler_t *handler,
-              void **handler_baton,
+              serf_response_handler_t *s_handler,
+              void **s_handler_baton,
               apr_pool_t *pool)
 {
-  svn_ra_serf__handler_t *ctx = setup_baton;
+  svn_ra_serf__handler_t *handler = setup_baton;
   svn_error_t *err;
 
   /* ### construct a scratch_pool? serf gives us a pool that will live for
      ### the duration of the request.  */
   apr_pool_t *scratch_pool = pool;
 
-  err = setup_request(request, ctx,
-                      req_bkt,
-                      acceptor, acceptor_baton,
-                      handler, handler_baton,
-                      pool /* request_pool */, scratch_pool);
+  if (strcmp(handler->method, "HEAD") == 0)
+    *acceptor = accept_head;
+  else
+    *acceptor = accept_response;
+  *acceptor_baton = handler->session;
 
-  if (err)
-    {
-      ctx->session->pending_error
-                = svn_error_compose_create(ctx->session->pending_error,
-                                           err);
+  *s_handler = handle_response_cb;
+  *s_handler_baton = handler;
 
-      return err->apr_err;
-    }
+  err = svn_error_trace(setup_request(request, handler, req_bkt,
+                                      pool /* request_pool */, scratch_pool));
 
-  return APR_SUCCESS;
+  return save_error(handler->session, err);
 }
 
 void
 svn_ra_serf__request_create(svn_ra_serf__handler_t *handler)
 {
+  SVN_ERR_ASSERT_NO_RETURN(handler->handler_pool != NULL);
+
+  /* In case HANDLER is re-queued, reset the various transient fields.
+
+     ### prior to recent changes, HANDLER was constant. maybe we should
+     ### break out these processing fields, apart from the request
+     ### definition.  */
+  handler->done = FALSE;
+  handler->server_error = NULL;
+  handler->sline.version = 0;
+  handler->location = NULL;
+  handler->reading_body = FALSE;
+  handler->discard_body = FALSE;
+
+  /* ### do we ever alter the >response_handler?  */
+
   /* ### do we need to hold onto the returned request object, or just
      ### not worry about it (the serf ctx will manage it).  */
   (void) serf_connection_request_create(handler->conn->conn,
                                         setup_request_cb, handler);
 }
 
+
 svn_error_t *
 svn_ra_serf__discover_vcc(const char **vcc_url,
                           svn_ra_serf__session_t *session,
@@ -1983,26 +2103,22 @@ svn_ra_serf__discover_vcc(const char **v
       apr_hash_t *props;
       svn_error_t *err;
 
-      err = svn_ra_serf__retrieve_props(&props, session, conn,
-                                        path, SVN_INVALID_REVNUM,
-                                        "0", base_props, pool, pool);
+      err = svn_ra_serf__fetch_node_props(&props, conn,
+                                          path, SVN_INVALID_REVNUM,
+                                          base_props, pool, pool);
       if (! err)
         {
-          *vcc_url =
-              svn_ra_serf__get_ver_prop(props, path,
-                                        SVN_INVALID_REVNUM,
-                                        "DAV:",
+          apr_hash_t *ns_props;
+
+          ns_props = apr_hash_get(props, "DAV:", 4);
+          *vcc_url = svn_prop_get_value(ns_props,
                                         "version-controlled-configuration");
 
-          relative_path = svn_ra_serf__get_ver_prop(props, path,
-                                                    SVN_INVALID_REVNUM,
-                                                    SVN_DAV_PROP_NS_DAV,
-                                                    "baseline-relative-path");
-
-          uuid = svn_ra_serf__get_ver_prop(props, path,
-                                           SVN_INVALID_REVNUM,
-                                           SVN_DAV_PROP_NS_DAV,
-                                           "repository-uuid");
+          ns_props = apr_hash_get(props,
+                                  SVN_DAV_PROP_NS_DAV, APR_HASH_KEY_STRING);
+          relative_path = svn_prop_get_value(ns_props,
+                                             "baseline-relative-path");
+          uuid = svn_prop_get_value(ns_props, "repository-uuid");
           break;
         }
       else
@@ -2162,3 +2278,176 @@ svn_ra_serf__register_editor_shim_callba
   session->shim_callbacks = callbacks;
   return SVN_NO_ERROR;
 }
+
+
+/* Conforms to Expat's XML_StartElementHandler  */
+static void
+expat_start(void *userData, const char *raw_name, const char **attrs)
+{
+  struct expat_ctx_t *ectx = userData;
+
+  if (ectx->inner_error != NULL)
+    return;
+
+  ectx->inner_error = svn_error_trace(
+                        svn_ra_serf__xml_cb_start(ectx->xmlctx,
+                                                  raw_name, attrs));
+
+#ifdef EXPAT_HAS_STOPPARSER
+  if (ectx->inner_error)
+    (void) XML_StopParser(ectx->parser, 0 /* resumable */);
+#endif
+}
+
+
+/* Conforms to Expat's XML_EndElementHandler  */
+static void
+expat_end(void *userData, const char *raw_name)
+{
+  struct expat_ctx_t *ectx = userData;
+
+  if (ectx->inner_error != NULL)
+    return;
+
+  ectx->inner_error = svn_error_trace(
+                        svn_ra_serf__xml_cb_end(ectx->xmlctx, raw_name));
+
+#ifdef EXPAT_HAS_STOPPARSER
+  if (ectx->inner_error)
+    (void) XML_StopParser(ectx->parser, 0 /* resumable */);
+#endif
+}
+
+
+/* Conforms to Expat's XML_CharacterDataHandler  */
+static void
+expat_cdata(void *userData, const char *data, int len)
+{
+  struct expat_ctx_t *ectx = userData;
+
+  if (ectx->inner_error != NULL)
+    return;
+
+  ectx->inner_error = svn_error_trace(
+                        svn_ra_serf__xml_cb_cdata(ectx->xmlctx, data, len));
+
+#ifdef EXPAT_HAS_STOPPARSER
+  if (ectx->inner_error)
+    (void) XML_StopParser(ectx->parser, 0 /* resumable */);
+#endif
+}
+
+
+/* Implements svn_ra_serf__response_handler_t */
+static svn_error_t *
+expat_response_handler(serf_request_t *request,
+                       serf_bucket_t *response,
+                       void *baton,
+                       apr_pool_t *scratch_pool)
+{
+  struct expat_ctx_t *ectx = baton;
+
+  SVN_ERR_ASSERT(ectx->parser != NULL);
+
+  /* ### should we bail on anything < 200 or >= 300 ??
+     ### actually: < 200 should really be handled by the core.  */
+  if (ectx->handler->sline.code == 404)
+    {
+      /* 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.  */
+      return svn_error_trace(svn_ra_serf__expect_empty_body(
+                               request, response, ectx->handler,
+                               scratch_pool));
+    }
+
+  while (1)
+    {
+      apr_status_t status;
+      const char *data;
+      apr_size_t len;
+      int expat_status;
+
+      status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
+      if (SERF_BUCKET_READ_ERROR(status))
+        return svn_error_wrap_apr(status, NULL);
+
+#if 0
+      /* ### move restart/skip into the core handler  */
+      ectx->handler->read_size += len;
+#endif
+
+      /* ### move PAUSED behavior to a new response handler that can feed
+         ### an inner handler, or can pause for a while.  */
+
+      /* ### should we have an IGNORE_ERRORS flag like the v1 parser?  */
+
+      expat_status = XML_Parse(ectx->parser, data, (int)len, 0 /* isFinal */);
+      if (expat_status == XML_STATUS_ERROR)
+        return svn_error_createf(SVN_ERR_XML_MALFORMED,
+                                 ectx->inner_error,
+                                 _("The %s response contains invalid XML"
+                                   " (%d %s)"),
+                                 ectx->handler->method,
+                                 ectx->handler->sline.code,
+                                 ectx->handler->sline.reason);
+
+      /* Was an error dropped off for us?  */
+      if (ectx->inner_error)
+        {
+          apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
+                               xml_parser_cleanup);
+          return svn_error_trace(ectx->inner_error);
+        }
+
+      /* The parsing went fine. What has the bucket told us?  */
+
+      if (APR_STATUS_IS_EAGAIN(status))
+        return svn_error_wrap_apr(status, NULL);
+
+      if (APR_STATUS_IS_EOF(status))
+        {
+          /* 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 */);
+
+          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...  */
+
+          return svn_error_wrap_apr(status, NULL);
+        }
+    }
+
+  /* NOTREACHED */
+}
+
+
+svn_ra_serf__handler_t *
+svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx,
+                                  apr_pool_t *result_pool)
+{
+  svn_ra_serf__handler_t *handler;
+  struct expat_ctx_t *ectx;
+
+  ectx = apr_pcalloc(result_pool, sizeof(*ectx));
+  ectx->xmlctx = xmlctx;
+  ectx->parser = XML_ParserCreate(NULL);
+  apr_pool_cleanup_register(result_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);
+
+
+  handler = apr_pcalloc(result_pool, sizeof(*handler));
+  handler->handler_pool = result_pool;
+  handler->response_handler = expat_response_handler;
+  handler->response_baton = ectx;
+
+  ectx->handler = handler;
+
+  return handler;
+}

Modified: subversion/branches/javahl-ra/subversion/libsvn_ra_serf/xml.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_ra_serf/xml.c?rev=1343447&r1=1343446&r2=1343447&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_ra_serf/xml.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_ra_serf/xml.c Tue May 29 01:39:41 2012
@@ -24,9 +24,6 @@
 
 
 #include <apr_uri.h>
-
-#include <expat.h>
-
 #include <serf.h>
 
 #include "svn_pools.h"
@@ -37,91 +34,181 @@
 #include "svn_config.h"
 #include "svn_delta.h"
 #include "svn_path.h"
+
 #include "svn_private_config.h"
+#include "private/svn_string_private.h"
 
 #include "ra_serf.h"
 
 
-void
-svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list,
-                       const char **attrs,
-                       apr_pool_t *pool)
+struct svn_ra_serf__xml_context_t {
+  /* Current state information.  */
+  svn_ra_serf__xml_estate_t *current;
+
+  /* If WAITING.NAMESPACE != NULL, wait for NAMESPACE:NAME element to be
+     closed before looking for transitions from CURRENT->STATE.  */
+  svn_ra_serf__dav_props_t waiting;
+
+  /* The transition table.  */
+  const svn_ra_serf__xml_transition_t *ttable;
+
+  /* The callback information.  */
+  svn_ra_serf__xml_opened_t opened_cb;
+  svn_ra_serf__xml_closed_t closed_cb;
+  void *baton;
+
+  /* Linked list of free states.  */
+  svn_ra_serf__xml_estate_t *free_states;
+
+#ifdef SVN_DEBUG
+  /* Used to verify we are not re-entering a callback, specifically to
+     ensure SCRATCH_POOL is not cleared while an outer callback is
+     trying to use it.  */
+  svn_boolean_t within_callback;
+#define START_CALLBACK(xmlctx) \
+  do {                                                    \
+    svn_ra_serf__xml_context_t *xmlctx__tmp = (xmlctx);   \
+    SVN_ERR_ASSERT(!xmlctx__tmp->within_callback);        \
+    xmlctx__tmp->within_callback = TRUE;                  \
+  } while (0)
+#define END_CALLBACK(xmlctx) ((xmlctx)->within_callback = FALSE)
+#else
+#define START_CALLBACK(xmlctx)  /* empty */
+#define END_CALLBACK(xmlctx)  /* empty */
+#endif /* SVN_DEBUG  */
+
+  apr_pool_t *scratch_pool;
+
+};
+
+struct svn_ra_serf__xml_estate_t {
+  /* The current state value.  */
+  int state;
+
+  /* The xml tag that opened this state. Waiting for the tag to close.  */
+  svn_ra_serf__dav_props_t tag;
+
+  /* Should the CLOSED_CB function be called for custom processing when
+     this tag is closed?  */
+  svn_boolean_t custom_close;
+
+  /* A pool may be constructed for this state.  */
+  apr_pool_t *state_pool;
+
+  /* The namespaces extent for this state/element. This will start with
+     the parent's NS_LIST, and we will push new namespaces into our
+     local list. The parent will be unaffected by our locally-scoped data. */
+  svn_ra_serf__ns_t *ns_list;
+
+  /* Any collected attribute values. char * -> svn_string_t *. May be NULL
+     if no attributes have been collected.  */
+  apr_hash_t *attrs;
+
+  /* Any collected cdata. May be NULL if no cdata is being collected.  */
+  svn_stringbuf_t *cdata;
+
+  /* Previous/outer state.  */
+  svn_ra_serf__xml_estate_t *prev;
+
+};
+
+
+static void
+define_namespaces(svn_ra_serf__ns_t **ns_list,
+                  const char *const *attrs,
+                  apr_pool_t *(*get_pool)(void *baton),
+                  void *baton)
 {
-  const char **tmp_attrs = attrs;
+  const char *const *tmp_attrs = attrs;
 
-  while (*tmp_attrs)
+  for (tmp_attrs = attrs; *tmp_attrs != NULL; tmp_attrs += 2)
     {
       if (strncmp(*tmp_attrs, "xmlns", 5) == 0)
         {
-          svn_ra_serf__ns_t *new_ns, *cur_ns;
-          int found = 0;
+          const svn_ra_serf__ns_t *cur_ns;
+          svn_boolean_t found = FALSE;
+          const char *prefix;
+
+          /* The empty prefix, or a named-prefix.  */
+          if (tmp_attrs[0][5] == ':')
+            prefix = &tmp_attrs[0][6];
+          else
+            prefix = "";
 
           /* Have we already defined this ns previously? */
           for (cur_ns = *ns_list; cur_ns; cur_ns = cur_ns->next)
             {
-              if (strcmp(cur_ns->namespace, tmp_attrs[0] + 6) == 0)
+              if (strcmp(cur_ns->namespace, prefix) == 0)
                 {
-                  found = 1;
+                  found = TRUE;
                   break;
                 }
             }
 
           if (!found)
             {
+              apr_pool_t *pool;
+              svn_ra_serf__ns_t *new_ns;
+
+              if (get_pool)
+                pool = get_pool(baton);
+              else
+                pool = baton;
               new_ns = apr_palloc(pool, sizeof(*new_ns));
-              new_ns->namespace = apr_pstrdup(pool, tmp_attrs[0] + 6);
+              new_ns->namespace = apr_pstrdup(pool, prefix);
               new_ns->url = apr_pstrdup(pool, tmp_attrs[1]);
 
+              /* Push into the front of NS_LIST. Parent states will point
+                 to later in the chain, so will be unaffected by
+                 shadowing/other namespaces pushed onto NS_LIST.  */
               new_ns->next = *ns_list;
-
               *ns_list = new_ns;
             }
         }
-      tmp_attrs += 2;
     }
 }
 
+
+void
+svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list,
+                       const char *const *attrs,
+                       apr_pool_t *result_pool)
+{
+  define_namespaces(ns_list, attrs, NULL /* get_pool */, result_pool);
+}
+
+
 /*
  * Look up NAME in the NS_LIST list for previously declared namespace
  * definitions and return a DAV_PROPS_T-tuple that has values.
  */
 void
 svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
-                       svn_ra_serf__ns_t *ns_list,
+                       const svn_ra_serf__ns_t *ns_list,
                        const char *name)
 {
   const char *colon;
-  svn_ra_serf__dav_props_t prop_name;
 
   colon = strchr(name, ':');
   if (colon)
     {
-      svn_ra_serf__ns_t *ns;
-
-      prop_name.namespace = NULL;
+      const svn_ra_serf__ns_t *ns;
 
       for (ns = ns_list; ns; ns = ns->next)
         {
           if (strncmp(ns->namespace, name, colon - name) == 0)
             {
-              prop_name.namespace = ns->url;
-              break;
+              returned_prop_name->namespace = ns->url;
+              returned_prop_name->name = colon + 1;
+              return;
             }
         }
-
-      SVN_ERR_ASSERT_NO_RETURN(prop_name.namespace);
-
-      prop_name.name = colon + 1;
-    }
-  else
-    {
-      /* use default namespace for now */
-      prop_name.namespace = "";
-      prop_name.name = name;
     }
 
-  *returned_prop_name = prop_name;
-  return;
+  /* If there is no prefix, or if the prefix is not found, then the
+     name is NOT within a namespace.  */
+  returned_prop_name->namespace = "";
+  returned_prop_name->name = name;
 }
 
 
@@ -322,3 +409,345 @@ void svn_ra_serf__xml_pop_state(svn_ra_s
   cur_state->prev = parser->free_state;
   parser->free_state = cur_state;
 }
+
+
+/* Return a pool for XES to use for self-alloc (and other specifics).  */
+static apr_pool_t *
+xes_pool(const svn_ra_serf__xml_estate_t *xes)
+{
+  /* Move up through parent states looking for one with a pool. This
+     will always terminate since the initial state has a pool.  */
+  while (xes->state_pool == NULL)
+    xes = xes->prev;
+  return xes->state_pool;
+}
+
+
+static void
+ensure_pool(svn_ra_serf__xml_estate_t *xes)
+{
+  if (xes->state_pool == NULL)
+    xes->state_pool = svn_pool_create(xes_pool(xes));
+}
+
+
+/* This callback is used by define_namespaces() to wait until a pool is
+   required before constructing it.  */
+static apr_pool_t *
+lazy_create_pool(void *baton)
+{
+  svn_ra_serf__xml_estate_t *xes = baton;
+
+  ensure_pool(xes);
+  return xes->state_pool;
+}
+
+
+svn_ra_serf__xml_context_t *
+svn_ra_serf__xml_context_create(
+  const svn_ra_serf__xml_transition_t *ttable,
+  svn_ra_serf__xml_opened_t opened_cb,
+  svn_ra_serf__xml_closed_t closed_cb,
+  void *baton,
+  apr_pool_t *result_pool)
+{
+  svn_ra_serf__xml_context_t *xmlctx;
+  svn_ra_serf__xml_estate_t *xes;
+
+  xmlctx = apr_pcalloc(result_pool, sizeof(*xmlctx));
+  xmlctx->ttable = ttable;
+  xmlctx->opened_cb = opened_cb;
+  xmlctx->closed_cb = closed_cb;
+  xmlctx->baton = baton;
+  xmlctx->scratch_pool = svn_pool_create(result_pool);
+
+  xes = apr_pcalloc(result_pool, sizeof(*xes));
+  /* XES->STATE == 0  */
+
+  /* Child states may use this pool to allocate themselves. If a child
+     needs to collect information, then it will construct a subpool and
+     will use that to allocate itself and its collected data.  */
+  xes->state_pool = result_pool;
+
+  xmlctx->current = xes;
+
+  return xmlctx;
+}
+
+
+apr_hash_t *
+svn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t *xes,
+                              int stop_state)
+{
+  apr_hash_t *data;
+
+  ensure_pool(xes);
+
+  data = apr_hash_make(xes->state_pool);
+
+  /* ### gather data  */
+
+  return data;
+}
+
+
+void
+svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes,
+                      int state,
+                      const char *name,
+                      const char *value)
+{
+  svn_ra_serf__xml_estate_t *scan;
+
+  for (scan = xes; scan != NULL && scan->state != state; scan = scan->prev)
+    /* pass */ ;
+
+  SVN_ERR_ASSERT_NO_RETURN(scan != NULL);
+
+  /* Make sure the target state has a pool.  */
+  ensure_pool(scan);
+
+  /* ... and attribute storage.  */
+  if (scan->attrs == NULL)
+    scan->attrs = apr_hash_make(scan->state_pool);
+
+  /* In all likelihood, NAME is a string constant. But we can't really
+     be sure. And it isn't like we're storing a billion of these into
+     the state pool.  */
+  apr_hash_set(scan->attrs,
+               apr_pstrdup(scan->state_pool, name), APR_HASH_KEY_STRING,
+               apr_pstrdup(scan->state_pool, value));
+}
+
+
+apr_pool_t *
+svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes)
+{
+  /* If they asked for a pool, then ensure that we have one to provide.  */
+  ensure_pool(xes);
+
+  return xes->state_pool;
+}
+
+
+svn_error_t *
+svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
+                          const char *raw_name,
+                          const char *const *attrs)
+{
+  svn_ra_serf__xml_estate_t *current = xmlctx->current;
+  svn_ra_serf__dav_props_t elemname;
+  const svn_ra_serf__xml_transition_t *scan;
+  apr_pool_t *new_pool;
+  svn_ra_serf__xml_estate_t *new_xes;
+
+  /* If we're waiting for an element to close, then just ignore all
+     other element-opens.  */
+  if (xmlctx->waiting.namespace != NULL)
+    return SVN_NO_ERROR;
+
+  /* Look for xmlns: attributes. Lazily create the state pool if any
+     were found.  */
+  define_namespaces(&current->ns_list, attrs, lazy_create_pool, current);
+
+  svn_ra_serf__expand_ns(&elemname, current->ns_list, raw_name);
+
+  for (scan = xmlctx->ttable; scan->ns != NULL; ++scan)
+    {
+      if (scan->from_state != current->state)
+        continue;
+
+      if (strcmp(elemname.name, scan->name) == 0
+          && strcmp(elemname.namespace, scan->ns) == 0)
+        break;
+    }
+  if (scan->ns == NULL)
+    {
+      xmlctx->waiting = elemname;
+      /* ### return?  */
+      return SVN_NO_ERROR;
+    }
+
+  /* We should not be told to collect cdata if the closed_cb will not
+     be called.  */
+  SVN_ERR_ASSERT(!scan->collect_cdata || scan->custom_close);
+
+  /* Found a transition. Make it happen.  */
+
+  /* ### todo. push state  */
+
+  /* ### how to use free states?  */
+  /* This state should be allocated in the extent pool. If we will be
+     collecting information for this state, then construct a subpool.
+
+     ### potentially optimize away the subpool if none of the
+     ### attributes are present. subpools are cheap, tho...  */
+  new_pool = xes_pool(current);
+  if (scan->collect_cdata || scan->collect_attrs[0])
+    {
+      new_pool = svn_pool_create(new_pool);
+
+      /* Prep the new state.  */
+      new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
+      new_xes->state_pool = new_pool;
+
+      /* If we're supposed to collect cdata, then set up a buffer for
+         this. The existence of this buffer will instruct our cdata
+         callback to collect the cdata.  */
+      if (scan->collect_cdata)
+        new_xes->cdata = svn_stringbuf_create_empty(new_pool);
+
+      if (scan->collect_attrs[0] != NULL)
+        {
+          const char *const *saveattr = &scan->collect_attrs[0];
+
+          new_xes->attrs = apr_hash_make(new_pool);
+          for (; *saveattr != NULL; ++saveattr)
+            {
+              const char *name;
+              const char *value;
+
+              if (**saveattr == '?')
+                {
+                  name = *saveattr + 1;
+                  value = svn_xml_get_attr_value(name, attrs);
+                }
+              else
+                {
+                  name = *saveattr;
+                  value = svn_xml_get_attr_value(name, attrs);
+                  if (value == NULL)
+                    return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND,
+                                             NULL,
+                                             _("Missing XML attribute: '%s'"),
+                                             name);
+                }
+
+              if (value)
+                apr_hash_set(new_xes->attrs, name, APR_HASH_KEY_STRING, value);
+            }
+        }
+    }
+  else
+    {
+      /* Prep the new state.  */
+      new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
+      /* STATE_POOL remains NULL.  */
+    }
+
+  /* Some basic copies to set up the new estate.  */
+  new_xes->state = scan->to_state;
+  new_xes->tag.name = apr_pstrdup(new_pool, elemname.name);
+  new_xes->tag.namespace = apr_pstrdup(new_pool, elemname.namespace);
+  new_xes->custom_close = scan->custom_close;
+
+  /* Start with the parent's namespace set.  */
+  new_xes->ns_list = current->ns_list;
+
+  /* The new state is prepared. Make it current.  */
+  new_xes->prev = current;
+  xmlctx->current = new_xes;
+
+  if (scan->custom_open)
+    {
+      START_CALLBACK(xmlctx);
+      SVN_ERR(xmlctx->opened_cb(new_xes, xmlctx->baton,
+                                new_xes->state, xmlctx->scratch_pool));
+      END_CALLBACK(xmlctx);
+      svn_pool_clear(xmlctx->scratch_pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
+                        const char *raw_name)
+{
+  svn_ra_serf__xml_estate_t *xes = xmlctx->current;
+  svn_ra_serf__dav_props_t elemname;
+
+  svn_ra_serf__expand_ns(&elemname, xes->ns_list, raw_name);
+
+  if (xmlctx->waiting.namespace != NULL)
+    {
+      /* If this element is not the closer, then keep waiting... */
+      if (strcmp(elemname.name, xmlctx->waiting.name) != 0
+          || strcmp(elemname.namespace, xmlctx->waiting.namespace) != 0)
+        return SVN_NO_ERROR;
+
+      /* Found it. Stop waiting, and go back for more.  */
+      xmlctx->waiting.namespace = NULL;
+      return SVN_NO_ERROR;
+    }
+
+  /* We should be looking at the same tag that opened the current state.
+
+     Unknown elements are simply skipped, so we wouldn't reach this check.
+
+     Known elements push a new state for a given tag. Some other elemname
+     would imply closing an ancestor tag (where did ours go?) or a spurious
+     tag closure.  */
+  if (strcmp(elemname.name, xes->tag.name) != 0
+      || strcmp(elemname.namespace, xes->tag.namespace) != 0)
+    return svn_error_create(SVN_ERR_XML_MALFORMED, NULL,
+                            _("The response contains invalid XML"));
+
+  if (xes->custom_close)
+    {
+      const svn_string_t *cdata;
+
+      if (xes->cdata)
+        {
+          cdata = svn_stringbuf__morph_into_string(xes->cdata);
+#ifdef SVN_DEBUG
+          /* We might toss the pool holding this structure, but it could also
+             be within a parent pool. In any case, for safety's sake, disable
+             the stringbuf against future Badness.  */
+          xes->cdata->pool = NULL;
+#endif
+        }
+      else
+        cdata = NULL;
+
+      START_CALLBACK(xmlctx);
+      SVN_ERR(xmlctx->closed_cb(xes, xmlctx->baton, xes->state,
+                                cdata, xes->attrs,
+                                xmlctx->scratch_pool));
+      END_CALLBACK(xmlctx);
+      svn_pool_clear(xmlctx->scratch_pool);
+    }
+
+  /* Pop the state.  */
+  xmlctx->current = xes->prev;
+
+  /* ### not everything should go on the free state list. XES may go
+     ### away with the state pool.  */
+  xes->prev = xmlctx->free_states;
+  xmlctx->free_states = xes;
+
+  /* If there is a STATE_POOL, then toss it. This will get rid of as much
+     memory as possible. Potentially the XES (if we didn't create a pool
+     right away, then XES may be in a parent pool).  */
+  if (xes->state_pool)
+    svn_pool_destroy(xes->state_pool);
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
+                          const char *data,
+                          apr_size_t len)
+{
+  /* If we're collecting cdata, but NOT waiting for a closing tag
+     (ie. not within an unknown tag), then copy the cdata.  */
+  if (xmlctx->current->cdata != NULL
+      && xmlctx->waiting.namespace == NULL)
+    svn_stringbuf_appendbytes(xmlctx->current->cdata, data, len);
+
+  return SVN_NO_ERROR;
+}
+

Modified: subversion/branches/javahl-ra/subversion/libsvn_ra_svn/marshal.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_ra_svn/marshal.c?rev=1343447&r1=1343446&r2=1343447&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_ra_svn/marshal.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_ra_svn/marshal.c Tue May 29 01:39:41 2012
@@ -439,6 +439,8 @@ static svn_error_t *write_number(svn_ra_
 {
   apr_size_t written;
 
+  /* SVN_INT64_BUFFER_SIZE includes space for a terminating NUL that
+   * svn__ui64toa will always append. */
   if (conn->write_pos + SVN_INT64_BUFFER_SIZE >= sizeof(conn->write_buf))
     SVN_ERR(writebuf_flush(conn, pool));
 
@@ -464,7 +466,7 @@ svn_error_t *svn_ra_svn_write_string(svn
       SVN_ERR(writebuf_writechar(conn, pool, ':'));
     }
   else
-    write_number(conn, pool, str->len, ':');
+    SVN_ERR(write_number(conn, pool, str->len, ':'));
 
   SVN_ERR(writebuf_write(conn, pool, str->data, str->len));
   SVN_ERR(writebuf_writechar(conn, pool, ' '));
@@ -482,7 +484,7 @@ svn_error_t *svn_ra_svn_write_cstring(sv
       SVN_ERR(writebuf_writechar(conn, pool, ':'));
     }
   else
-    write_number(conn, pool, len, ':');
+    SVN_ERR(write_number(conn, pool, len, ':'));
 
   SVN_ERR(writebuf_write(conn, pool, s, len));
   SVN_ERR(writebuf_writechar(conn, pool, ' '));

Modified: subversion/branches/javahl-ra/subversion/libsvn_ra_svn/ra_svn.h
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_ra_svn/ra_svn.h?rev=1343447&r1=1343446&r2=1343447&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_ra_svn/ra_svn.h (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_ra_svn/ra_svn.h Tue May 29 01:39:41 2012
@@ -57,8 +57,8 @@ typedef svn_error_t *(*ra_svn_block_hand
                                                void *baton);
 
 /* The size of our per-connection read and write buffers. */
-#define SVN_RA_SVN__READBUF_SIZE 4*4096
-#define SVN_RA_SVN__WRITEBUF_SIZE 4*4096
+#define SVN_RA_SVN__READBUF_SIZE (4*4096)
+#define SVN_RA_SVN__WRITEBUF_SIZE (4*4096)
 
 /* Create forward reference */
 typedef struct svn_ra_svn__session_baton_t svn_ra_svn__session_baton_t;

Modified: subversion/branches/javahl-ra/subversion/libsvn_repos/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_repos/commit.c?rev=1343447&r1=1343446&r2=1343447&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_repos/commit.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_repos/commit.c Tue May 29 01:39:41 2012
@@ -136,6 +136,17 @@ struct ev2_baton
   /* The repository we are editing.  */
   svn_repos_t *repos;
 
+  /* The authz baton for checks; NULL to skip authz.  */
+  svn_authz_t *authz;
+
+  /* The repository name and user for performing authz checks.  */
+  const char *authz_repos_name;
+  const char *authz_user;
+
+  /* Callback to provide info about the committed revision.  */
+  svn_commit_callback2_t commit_cb;
+  void *commit_baton;
+
   /* The FS txn editor  */
   svn_editor_t *inner;
 
@@ -158,6 +169,40 @@ out_of_date(const char *path, svn_node_k
 }
 
 
+static svn_error_t *
+invoke_commit_cb(svn_commit_callback2_t commit_cb,
+                 void *commit_baton,
+                 svn_fs_t *fs,
+                 svn_revnum_t revision,
+                 const char *post_commit_errstr,
+                 apr_pool_t *scratch_pool)
+{
+  /* FS interface returns non-const values.  */
+  /* const */ svn_string_t *date;
+  /* const */ svn_string_t *author;
+  svn_commit_info_t *commit_info;
+
+  if (commit_cb == NULL)
+    return SVN_NO_ERROR;
+
+  SVN_ERR(svn_fs_revision_prop(&date, fs, revision, SVN_PROP_REVISION_DATE,
+                               scratch_pool));
+  SVN_ERR(svn_fs_revision_prop(&author, fs, revision,
+                               SVN_PROP_REVISION_AUTHOR,
+                               scratch_pool));
+
+  commit_info = svn_create_commit_info(scratch_pool);
+
+  /* fill up the svn_commit_info structure */
+  commit_info->revision = revision;
+  commit_info->date = date ? date->data : NULL;
+  commit_info->author = author ? author->data : NULL;
+  commit_info->post_commit_err = post_commit_errstr;
+
+  return svn_error_trace(commit_cb(commit_info, commit_baton, scratch_pool));
+}
+
+
 
 /* If EDITOR_BATON contains a valid authz callback, verify that the
    REQUIRED access to PATH in ROOT is authorized.  Return an error
@@ -646,7 +691,8 @@ svn_repos__post_commit_error_str(svn_err
   else
     hook_err2 = hook_err1;
 
-  /* This implementation counts on svn_repos_fs_commit_txn() returning
+  /* This implementation counts on svn_repos_fs_commit_txn() and
+     libsvn_repos/commit.c:complete_cb() returning
      svn_fs_commit_txn() as the parent error with a child
      SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED error.  If the parent error
      is SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED then there was no error
@@ -718,7 +764,6 @@ close_edit(void *edit_baton,
              display it as a warning) and clear the error. */
           post_commit_err = svn_repos__post_commit_error_str(err, pool);
           svn_error_clear(err);
-          err = SVN_NO_ERROR;
         }
     }
   else
@@ -745,41 +790,19 @@ close_edit(void *edit_baton,
                                          svn_fs_abort_txn(eb->txn, pool)));
     }
 
-  /* Pass new revision information to the caller's callback. */
-  {
-    svn_string_t *date, *author;
-    svn_commit_info_t *commit_info;
-
-    /* Even if there was a post-commit hook failure, it's more serious
-       if one of the calls here fails, so we explicitly check for errors
-       here, while saving the possible post-commit error for later. */
-
-    err = svn_fs_revision_prop(&date, svn_repos_fs(eb->repos),
-                                new_revision, SVN_PROP_REVISION_DATE,
-                                pool);
-    if (! err)
-      {
-        err = svn_fs_revision_prop(&author, svn_repos_fs(eb->repos),
-                                   new_revision, SVN_PROP_REVISION_AUTHOR,
-                                   pool);
-      }
-
-    if ((! err) && eb->commit_callback)
-      {
-        commit_info = svn_create_commit_info(pool);
-
-        /* fill up the svn_commit_info structure */
-        commit_info->revision = new_revision;
-        commit_info->date = date ? date->data : NULL;
-        commit_info->author = author ? author->data : NULL;
-        commit_info->post_commit_err = post_commit_err;
-        err = (*eb->commit_callback)(commit_info,
-                                     eb->commit_callback_baton,
-                                     pool);
-      }
-  }
+  /* At this point, the post-commit error has been converted to a string.
+     That information will be passed to a callback, if provided. If the
+     callback invocation fails in some way, that failure is returned here.
+     IOW, the post-commit error information is low priority compared to
+     other gunk here.  */
 
-  return svn_error_trace(err);
+  /* Pass new revision information to the caller's callback. */
+  return svn_error_trace(invoke_commit_cb(eb->commit_callback,
+                                          eb->commit_callback_baton,
+                                          eb->repos->fs,
+                                          new_revision,
+                                          post_commit_err,
+                                          pool));
 }
 
 
@@ -973,41 +996,368 @@ svn_repos_get_commit_editor5(const svn_d
 }
 
 
+#if 0
+static svn_error_t *
+ev2_check_authz(const struct ev2_baton *eb,
+                const char *relpath,
+                svn_repos_authz_access_t required,
+                apr_pool_t *scratch_pool)
+{
+  const char *fspath;
+  svn_boolean_t allowed;
+
+  if (eb->authz == NULL)
+    return SVN_NO_ERROR;
+
+  if (relpath)
+    fspath = apr_pstrcat(scratch_pool, "/", relpath, NULL);
+  else
+    fspath = NULL;
+
+  SVN_ERR(svn_repos_authz_check_access(eb->authz, eb->authz_repos_name, fspath,
+                                       eb->authz_user, required,
+                                       &allowed, scratch_pool));
+  if (!allowed)
+    return svn_error_create(required & svn_authz_write
+                            ? SVN_ERR_AUTHZ_UNWRITABLE
+                            : SVN_ERR_AUTHZ_UNREADABLE,
+                            NULL, "Access denied");
+
+  return SVN_NO_ERROR;
+}
+#endif
+
+
+/* This implements svn_editor_cb_add_directory_t */
+static svn_error_t *
+add_directory_cb(void *baton,
+                 const char *relpath,
+                 const apr_array_header_t *children,
+                 apr_hash_t *props,
+                 svn_revnum_t replaces_rev,
+                 apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_add_directory(eb->inner, relpath, children, props,
+                                   replaces_rev));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements svn_editor_cb_add_file_t */
+static svn_error_t *
+add_file_cb(void *baton,
+            const char *relpath,
+            const svn_checksum_t *checksum,
+            svn_stream_t *contents,
+            apr_hash_t *props,
+            svn_revnum_t replaces_rev,
+            apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_add_file(eb->inner, relpath, checksum, contents, props,
+                              replaces_rev));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements svn_editor_cb_add_symlink_t */
+static svn_error_t *
+add_symlink_cb(void *baton,
+               const char *relpath,
+               const char *target,
+               apr_hash_t *props,
+               svn_revnum_t replaces_rev,
+               apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_add_symlink(eb->inner, relpath, target, props,
+                                 replaces_rev));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements svn_editor_cb_add_absent_t */
+static svn_error_t *
+add_absent_cb(void *baton,
+              const char *relpath,
+              svn_kind_t kind,
+              svn_revnum_t replaces_rev,
+              apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_add_absent(eb->inner, relpath, kind, replaces_rev));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements svn_editor_cb_alter_directory_t */
+static svn_error_t *
+alter_directory_cb(void *baton,
+                   const char *relpath,
+                   svn_revnum_t revision,
+                   const apr_array_header_t *children,
+                   apr_hash_t *props,
+                   apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_alter_directory(eb->inner, relpath, revision,
+                                     children, props));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements svn_editor_cb_alter_file_t */
+static svn_error_t *
+alter_file_cb(void *baton,
+              const char *relpath,
+              svn_revnum_t revision,
+              apr_hash_t *props,
+              const svn_checksum_t *checksum,
+              svn_stream_t *contents,
+              apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_alter_file(eb->inner, relpath, revision, props,
+                                checksum, contents));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements svn_editor_cb_alter_symlink_t */
+static svn_error_t *
+alter_symlink_cb(void *baton,
+                 const char *relpath,
+                 svn_revnum_t revision,
+                 apr_hash_t *props,
+                 const char *target,
+                 apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_alter_symlink(eb->inner, relpath, revision, props,
+                                   target));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements svn_editor_cb_delete_t */
+static svn_error_t *
+delete_cb(void *baton,
+          const char *relpath,
+          svn_revnum_t revision,
+          apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_delete(eb->inner, relpath, revision));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements svn_editor_cb_copy_t */
+static svn_error_t *
+copy_cb(void *baton,
+        const char *src_relpath,
+        svn_revnum_t src_revision,
+        const char *dst_relpath,
+        svn_revnum_t replaces_rev,
+        apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_copy(eb->inner, src_relpath, src_revision, dst_relpath,
+                          replaces_rev));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements svn_editor_cb_move_t */
+static svn_error_t *
+move_cb(void *baton,
+        const char *src_relpath,
+        svn_revnum_t src_revision,
+        const char *dst_relpath,
+        svn_revnum_t replaces_rev,
+        apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_move(eb->inner, src_relpath, src_revision, dst_relpath,
+                          replaces_rev));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements svn_editor_cb_rotate_t */
+static svn_error_t *
+rotate_cb(void *baton,
+          const apr_array_header_t *relpaths,
+          const apr_array_header_t *revisions,
+          apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_rotate(eb->inner, relpaths, revisions));
+  return SVN_NO_ERROR;
+}
+
+
+/* This implements svn_editor_cb_complete_t */
+static svn_error_t *
+complete_cb(void *baton,
+            apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+  svn_revnum_t revision;
+  svn_error_t *post_commit_err;
+  const char *conflict_path;
+  svn_error_t *err;
+  const char *post_commit_errstr;
+
+  /* The transaction has been fully edited. Let the pre-commit hook
+     have a look at the thing.  */
+  SVN_ERR(svn_repos__hooks_pre_commit(eb->repos, eb->txn_name, scratch_pool));
+
+  /* Hook is done. Let's do the actual commit.  */
+  SVN_ERR(svn_fs_editor_commit(&revision, &post_commit_err, &conflict_path,
+                               eb->inner, scratch_pool, scratch_pool));
+
+  /* Did a conflict occur during the commit process?  */
+  if (conflict_path != NULL)
+    return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
+                             _("Conflict at '%s'"), conflict_path);
+
+  /* Since did not receive an error during the commit process, and no
+     conflict was specified... we committed a revision. Run the hooks.
+     Other errors may have occurred within the FS (specified by the
+     POST_COMMIT_ERR localvar), but we need to run the hooks.  */
+  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
+  err = svn_repos__hooks_post_commit(eb->repos, revision, eb->txn_name,
+                                     scratch_pool);
+  if (err)
+    err = svn_error_create(SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err,
+                           _("Commit succeeded, but post-commit hook failed"));
+
+  /* Combine the FS errors with the hook errors, and stringify.  */
+  err = svn_error_compose_create(post_commit_err, err);
+  if (err)
+    {
+      post_commit_errstr = svn_repos__post_commit_error_str(err, scratch_pool);
+      svn_error_clear(err);
+    }
+  else
+    {
+      post_commit_errstr = NULL;
+    }
+
+  return svn_error_trace(invoke_commit_cb(eb->commit_cb, eb->commit_baton,
+                                          eb->repos->fs, revision,
+                                          post_commit_errstr,
+                                          scratch_pool));
+}
+
+
+/* This implements svn_editor_cb_abort_t */
+static svn_error_t *
+abort_cb(void *baton,
+         apr_pool_t *scratch_pool)
+{
+  struct ev2_baton *eb = baton;
+
+  SVN_ERR(svn_editor_abort(eb->inner));
+  return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+apply_revprops(svn_fs_t *fs,
+               const char *txn_name,
+               apr_hash_t *revprops,
+               apr_pool_t *scratch_pool)
+{
+  svn_fs_txn_t *txn;
+  const apr_array_header_t *revprops_array;
+
+  /* The FS editor has a TXN inside it, but we can't access it. Open another
+     based on the TXN_NAME.  */
+  SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, scratch_pool));
+
+  /* Validate and apply the revision properties.  */
+  revprops_array = svn_prop_hash_to_array(revprops, scratch_pool);
+  SVN_ERR(svn_repos_fs_change_txn_props(txn, revprops_array, scratch_pool));
+
+  /* ### do we need to force the txn to close, or is it enough to wait
+     ### for the pool to be cleared?  */
+  return SVN_NO_ERROR;
+}
+
+
 svn_error_t *
 svn_repos__get_commit_ev2(svn_editor_t **editor,
                           svn_repos_t *repos,
-                          svn_revnum_t revision,
-                          apr_hash_t *revprop_table,
+                          svn_authz_t *authz,
+                          const char *authz_repos_name,
+                          const char *authz_user,
+                          apr_hash_t *revprops,
+                          svn_commit_callback2_t commit_cb,
+                          void *commit_baton,
                           svn_cancel_func_t cancel_func,
                           void *cancel_baton,
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool)
 {
   static const svn_editor_cb_many_t editor_cbs = {
-    NULL /* add_directory_cb */,
-    NULL /* add_file_cb */,
-    NULL /* add_symlink_cb */,
-    NULL /* add_absent_cb */,
-    NULL /* alter_directory_cb */,
-    NULL /* alter_file_cb */,
-    NULL /* alter_symlink_cb */,
-    NULL /* delete_cb */,
-    NULL /* copy_cb */,
-    NULL /* move_cb */,
-    NULL /* rotate_cb */,
-    NULL /* complete_cb */,
-    NULL /* abort_cb */
+    add_directory_cb,
+    add_file_cb,
+    add_symlink_cb,
+    add_absent_cb,
+    alter_directory_cb,
+    alter_file_cb,
+    alter_symlink_cb,
+    delete_cb,
+    copy_cb,
+    move_cb,
+    rotate_cb,
+    complete_cb,
+    abort_cb
   };
-  struct ev2_baton *eb = apr_palloc(result_pool, sizeof(*eb));
+  struct ev2_baton *eb;
+  const svn_string_t *author;
+
+  /* Can the user modify the repository at all?  */
+  /* ### check against AUTHZ.  */
 
+  /* Okay... some access is allowed. Let's run the start-commit hook.  */
+  author = apr_hash_get(revprops, SVN_PROP_REVISION_AUTHOR,
+                        APR_HASH_KEY_STRING);
+  SVN_ERR(svn_repos__hooks_start_commit(repos, author ? author->data : NULL,
+                                        repos->client_capabilities,
+                                        scratch_pool));
+
+  eb = apr_palloc(result_pool, sizeof(*eb));
   eb->repos = repos;
+  eb->authz = authz;
+  eb->authz_repos_name = authz_repos_name;
+  eb->authz_user = authz_user;
+  eb->commit_cb = commit_cb;
+  eb->commit_baton = commit_baton;
 
   SVN_ERR(svn_fs_editor_create(&eb->inner, &eb->txn_name,
-                               repos->fs, revision,
-                               SVN_FS_TXN_CHECK_LOCKS,
+                               repos->fs, SVN_FS_TXN_CHECK_LOCKS,
                                cancel_func, cancel_baton,
                                result_pool, scratch_pool));
 
+  /* The TXN has been created. Go ahead and apply all revision properties.  */
+  SVN_ERR(apply_revprops(repos->fs, eb->txn_name, revprops, scratch_pool));
+
+  /* Wrap the FS editor within our editor.  */
   SVN_ERR(svn_editor_create(editor, eb, cancel_func, cancel_baton,
                             result_pool, scratch_pool));
   SVN_ERR(svn_editor_setcb_many(*editor, &editor_cbs, scratch_pool));

Modified: subversion/branches/javahl-ra/subversion/libsvn_repos/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_repos/deprecated.c?rev=1343447&r1=1343446&r2=1343447&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_repos/deprecated.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_repos/deprecated.c Tue May 29 01:39:41 2012
@@ -572,8 +572,7 @@ repos_notify_handler(void *baton,
   switch (notify->action)
   {
     case svn_repos_notify_warning:
-      len = strlen(notify->warning_str);
-      svn_error_clear(svn_stream_write(feedback_stream, notify->warning_str, &len));
+      svn_error_clear(svn_stream_puts(feedback_stream, notify->warning_str));
       return;
 
     case svn_repos_notify_dump_rev_end: