You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2012/06/27 17:13:42 UTC

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

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c Wed Jun 27 15:12:37 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,
@@ -213,7 +257,7 @@ ssl_server_cert(void *baton, int failure
       int i;
       for (i = 0; i < san->nelts; i++) {
           char *s = APR_ARRAY_IDX(san, i, char*);
-          if (apr_fnmatch(s, conn->hostname,
+          if (apr_fnmatch(s, conn->session->session_url.hostname,
                           APR_FNM_PERIOD) == APR_SUCCESS) {
               found_matching_hostname = 1;
               cert_info.hostname = s;
@@ -225,7 +269,7 @@ ssl_server_cert(void *baton, int failure
   /* Match server certificate CN with the hostname of the server */
   if (!found_matching_hostname && cert_info.hostname)
     {
-      if (apr_fnmatch(cert_info.hostname, conn->hostname,
+      if (apr_fnmatch(cert_info.hostname, conn->session->session_url.hostname,
                       APR_FNM_PERIOD) == APR_FNM_NOMATCH)
         {
           svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
@@ -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 *
@@ -331,7 +364,7 @@ conn_setup(apr_socket_t *sock,
   *read_bkt = serf_context_bucket_socket_create(conn->session->context,
                                                sock, conn->bkt_alloc);
 
-  if (conn->using_ssl)
+  if (conn->session->using_ssl)
     {
       /* input stream */
       *read_bkt = serf_bucket_ssl_decrypt_create(*read_bkt, conn->ssl_context,
@@ -341,7 +374,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->hostname);
+          serf_ssl_set_hostname(conn->ssl_context,
+                                conn->session->session_url.hostname);
 #endif
 
           serf_ssl_client_cert_provider_set(conn->ssl_context,
@@ -391,24 +425,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 +471,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 +480,8 @@ connection_closed(serf_connection_t *con
       SVN_ERR_MALFUNCTION();
     }
 
-  if (sc->using_ssl)
-      sc->ssl_context = NULL;
+  if (conn->session->using_ssl)
+    conn->ssl_context = NULL;
 
   return SVN_NO_ERROR;
 }
@@ -469,15 +492,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 +548,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 +601,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);
-
-  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_pw(data,
+                                              cert_path,
+                                              password,
+                                              session->pool));
 
-  return APR_SUCCESS;
+  return save_error(session, err);
 }
 
 
@@ -629,7 +633,7 @@ static svn_error_t *
 setup_serf_req(serf_request_t *request,
                serf_bucket_t **req_bkt,
                serf_bucket_t **hdrs_bkt,
-               svn_ra_serf__connection_t *conn,
+               svn_ra_serf__session_t *session,
                const char *method, const char *url,
                serf_bucket_t *body_bkt, const char *content_type,
                apr_pool_t *request_pool,
@@ -640,7 +644,7 @@ setup_serf_req(serf_request_t *request,
 #if SERF_VERSION_AT_LEAST(1, 1, 0)
   svn_spillbuf_t *buf;
 
-  if (conn->http10 && body_bkt != NULL)
+  if (session->http10 && 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
@@ -666,7 +670,7 @@ 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 (conn->http10)
+  if (session->http10)
     {
       if (body_bkt == NULL)
         serf_bucket_request_set_CL(*req_bkt, 0);
@@ -677,10 +681,10 @@ setup_serf_req(serf_request_t *request,
 
   *hdrs_bkt = serf_bucket_request_get_headers(*req_bkt);
 
-  /* We use serf_bucket_headers_setn() because the string below have a
+  /* We use serf_bucket_headers_setn() because the USERAGENT has a
      lifetime longer than this bucket. Thus, there is no need to copy
      the header values.  */
-  serf_bucket_headers_setn(*hdrs_bkt, "User-Agent", conn->useragent);
+  serf_bucket_headers_setn(*hdrs_bkt, "User-Agent", session->useragent);
 
   if (content_type)
     {
@@ -756,6 +760,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 +845,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,69 +876,57 @@ cdata_error(svn_ra_serf__xml_parser_t *p
   return SVN_NO_ERROR;
 }
 
-/* Implements svn_ra_serf__response_handler_t */
-svn_error_t *
-svn_ra_serf__handle_discard_body(serf_request_t *request,
-                                 serf_bucket_t *response,
-                                 void *baton,
-                                 apr_pool_t *pool)
-{
-  apr_status_t status;
-  svn_ra_serf__server_error_t *server_err = baton;
 
-  if (server_err)
+static apr_status_t
+drain_bucket(serf_bucket_t *bucket)
+{
+  /* Read whatever is in the bucket, and just drop it.  */
+  while (1)
     {
-      if (!server_err->init)
-        {
-          serf_bucket_t *hdrs;
-          const char *val;
+      apr_status_t status;
+      const char *data;
+      apr_size_t len;
 
-          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(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_error;
-              server_err->parser.end = end_error;
-              server_err->parser.cdata = cdata_error;
-              server_err->parser.done = &server_err->done;
-              server_err->parser.ignore_errors = TRUE;
-            }
-          else
-            {
-              server_err->error = SVN_NO_ERROR;
-            }
-        }
+      status = serf_bucket_read(bucket, SERF_READ_ALL_AVAIL, &data, &len);
+      if (status)
+        return status;
+    }
+}
 
-      if (server_err->has_xml_response)
-        {
-          svn_error_t *err = svn_ra_serf__handle_xml_parser(
-                                                        request,
-                                                        response,
-                                                        &server_err->parser,
-                                                        pool);
 
-          if (server_err->done && server_err->error->apr_err == APR_SUCCESS)
-            {
-              svn_error_clear(server_err->error);
-              server_err->error = SVN_NO_ERROR;
-            }
+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;
 
-          return svn_error_trace(err);
-        }
+  server_err = apr_pcalloc(result_pool, sizeof(*server_err));
+  server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL);
+  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;
+}
 
-  status = svn_ra_serf__response_discard_handler(request, response,
-                                                 NULL, pool);
+/* Implements svn_ra_serf__response_handler_t */
+svn_error_t *
+svn_ra_serf__handle_discard_body(serf_request_t *request,
+                                 serf_bucket_t *response,
+                                 void *baton,
+                                 apr_pool_t *pool)
+{
+  apr_status_t status;
 
+  status = drain_bucket(response);
   if (status)
     return svn_error_wrap_apr(status, NULL);
 
@@ -930,71 +939,83 @@ 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 *
-svn_ra_serf__response_get_location(serf_bucket_t *response,
-                                   apr_pool_t *pool)
+
+/* Return the value of the RESPONSE's Location header if any, or NULL
+   otherwise.  All allocations will be made in POOL.  */
+static const char *
+response_get_location(serf_bucket_t *response,
+                      apr_pool_t *pool)
 {
   serf_bucket_t *headers;
-  const char *val;
+  const char *location;
+  apr_status_t status;
+  apr_uri_t uri;
 
   headers = serf_bucket_response_get_headers(response);
-  val = serf_bucket_headers_get(headers, "Location");
-  return val ? svn_urlpath__canonicalize(val, pool) : NULL;
+  location = serf_bucket_headers_get(headers, "Location");
+  if (location == NULL)
+    return NULL;
+
+  /* Ignore the scheme/host/port. Or just return as-is if we can't parse.  */
+  status = apr_uri_parse(pool, location, &uri);
+  if (!status)
+    location = uri.path;
+
+  return svn_urlpath__canonicalize(location, pool);
 }
 
+
 /* 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);
+
+      /* Get the parser to set our DONE flag.  */
+      server_err->parser.done = &handler->done;
 
-      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;
+      handler->server_error = server_err;
+    }
+  else
+    {
+      /* 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 +1097,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 +1150,50 @@ 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;
-        }
-    }
-
-  /* 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);
-
-      /* 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);
-        }
-      else if (ctx->done && server_err->error->apr_err == APR_SUCCESS)
-        {
-          svn_error_clear(server_err->error);
-          server_err->error = SVN_NO_ERROR;
-        }
-
-      svn_error_clear(err);
-    }
+          svn_ra_serf__server_error_t *server_err;
 
-  err = svn_ra_serf__handle_discard_body(request, response, NULL, pool);
+          server_err = begin_error_parsing(start_207, end_207, cdata_207,
+                                           handler->handler_pool);
 
-  if (err && APR_STATUS_IS_EOF(err->apr_err))
-    {
-      serf_status_line sl;
-      apr_status_t status;
+          /* Get the parser to set our DONE flag.  */
+          server_err->parser.done = &handler->done;
 
-      status = serf_bucket_response_status(response, &sl);
-      if (SERF_BUCKET_READ_ERROR(status))
+          handler->server_error = server_err;
+        }
+      else
         {
-          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;
 }
 
 
@@ -1220,6 +1204,7 @@ start_xml(void *userData, const char *ra
   svn_ra_serf__xml_parser_t *parser = userData;
   svn_ra_serf__dav_props_t name;
   apr_pool_t *scratch_pool;
+  svn_error_t *err;
 
   if (parser->error)
     return;
@@ -1234,7 +1219,11 @@ start_xml(void *userData, const char *ra
 
   svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name);
 
-  parser->error = parser->start(parser, name, attrs, scratch_pool);
+  err = parser->start(parser, name, attrs, scratch_pool);
+  if (err && !SERF_BUCKET_READ_ERROR(err->apr_err))
+    err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
+
+  parser->error = err;
 }
 
 
@@ -1244,6 +1233,7 @@ end_xml(void *userData, const char *raw_
 {
   svn_ra_serf__xml_parser_t *parser = userData;
   svn_ra_serf__dav_props_t name;
+  svn_error_t *err;
   apr_pool_t *scratch_pool;
 
   if (parser->error)
@@ -1254,7 +1244,11 @@ end_xml(void *userData, const char *raw_
 
   svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name);
 
-  parser->error = parser->end(parser, name, scratch_pool);
+  err = parser->end(parser, name, scratch_pool);
+  if (err && !SERF_BUCKET_READ_ERROR(err->apr_err))
+    err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
+
+  parser->error = err;
 }
 
 
@@ -1263,6 +1257,7 @@ static void
 cdata_xml(void *userData, const char *data, int len)
 {
   svn_ra_serf__xml_parser_t *parser = userData;
+  svn_error_t *err;
   apr_pool_t *scratch_pool;
 
   if (parser->error)
@@ -1274,7 +1269,11 @@ cdata_xml(void *userData, const char *da
   /* ### get a real scratch_pool  */
   scratch_pool = parser->state->pool;
 
-  parser->error = parser->cdata(parser, data, len, scratch_pool);
+  err = parser->cdata(parser, data, len, scratch_pool);
+  if (err && !SERF_BUCKET_READ_ERROR(err->apr_err))
+    err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
+
+  parser->error = err;
 }
 
 /* Flip the requisite bits in CTX to indicate that processing of the
@@ -1326,7 +1325,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 +1414,65 @@ 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 };
+  serf_bucket_t *hdrs;
+  const char *val;
+  apr_status_t 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)
+    {
+      /* ### we should figure out how to reuse begin_error_parsing  */
+
+      server_err.error = svn_error_create(APR_SUCCESS, NULL, NULL);
+      server_err.contains_precondition_error = FALSE;
+      server_err.cdata = svn_stringbuf_create_empty(scratch_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_error;
+      server_err.parser.end = end_error;
+      server_err.parser.cdata = cdata_error;
+      server_err.parser.done = &server_err.done;
+      server_err.parser.ignore_errors = TRUE;
+
+      /* We don't care about any errors except for SERVER_ERR.ERROR  */
+      svn_error_clear(svn_ra_serf__handle_xml_parser(request,
+                                                     response,
+                                                     &server_err.parser,
+                                                     scratch_pool));
+
+      /* ### checking DONE is silly. the above only parses whatever has
+         ### been received at the network interface. totally wrong. but
+         ### it is what we have for now (maintaining historical code),
+         ### until we fully migrate.  */
+      if (server_err.done && server_err.error->apr_err == APR_SUCCESS)
+        {
+          svn_error_clear(server_err.error);
+          server_err.error = SVN_NO_ERROR;
+        }
+
+      return svn_error_trace(server_err.error);
+    }
+
+  /* The only error that we will return is from the XML response body.
+     Otherwise, ignore the entire body but allow SUCCESS/EOF/EAGAIN to
+     surface. */
+  err = drain_bucket(response);
+  if (err && !SERF_BUCKET_READ_ERROR(err))
+    return svn_error_wrap_apr(err, NULL);
+
+  return SVN_NO_ERROR;
+}
+
+
 /* Implements svn_ra_serf__response_handler_t */
 svn_error_t *
 svn_ra_serf__handle_xml_parser(serf_request_t *request,
@@ -1427,36 +1485,22 @@ 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);
+      err = handle_server_error(request, response, pool);
 
-      add_done_item(ctx);
+      if (err && APR_STATUS_IS_EOF(err->apr_err))
+        add_done_item(ctx);
 
-      err = svn_ra_serf__handle_server_error(request, response, pool);
-
-      SVN_ERR(svn_error_compose_create(
-        svn_ra_serf__handle_discard_body(request, response, NULL, pool),
-        err));
-      return SVN_NO_ERROR;
+      return svn_error_trace(err);
     }
 
   if (ctx->headers_baton == NULL)
@@ -1591,19 +1635,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 +1643,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 +1673,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 +1682,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 +1704,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 +1727,83 @@ 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));
-
-      svn_ra_serf__request_create(ctx);
+      /* 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);
 
-      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);
+
+      /* HTTP/1.1? (or later)  */
+      if (sl.version != SERF_HTTP_10)
+        handler->session->http10 = FALSE;
     }
 
+  /* 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 +1812,200 @@ 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;
+  /* ... and set up the header fields in HANDLER.  */
+  handler->location = response_get_location(response, handler->handler_pool);
 
-  if (sl.code == 405 || sl.code == 409 || sl.code >= 500)
+  /* 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));
+      serf_bucket_t *hdrs;
+      const char *val;
 
-      if (!ctx->session->pending_error)
+      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)
         {
-          apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
+          svn_ra_serf__server_error_t *server_err;
 
-          /* 405 == Method Not Allowed (Occurs when trying to lock a working
-             copy path which no longer exists at HEAD in the repository. */
+          server_err = begin_error_parsing(start_error, end_error, cdata_error,
+                                           handler->handler_pool);
+          /* Get the parser to set our DONE flag.  */
+          server_err->parser.done = &handler->done;
+
+          handler->server_error = server_err;
+        }
+      else
+        {
+          handler->discard_body = TRUE;
 
-          if (sl.code == 405 && !strcmp(ctx->method, "LOCK"))
-            apr_err = SVN_ERR_FS_OUT_OF_DATE;
+          if (!handler->session->pending_error)
+            {
+              apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
 
-          return
-              svn_error_createf(apr_err, NULL,
-                                _("%s request on '%s' failed: %d %s"),
-                                ctx->method, ctx->path, sl.code, sl.reason);
+              /* 405 == Method Not Allowed (Occurs when trying to lock a working
+                copy path which no longer exists at HEAD in the repository. */
+              if (handler->sline.code == 405
+                  && strcmp(handler->method, "LOCK") == 0)
+                apr_err = SVN_ERR_FS_OUT_OF_DATE;
+
+              handler->session->pending_error =
+                  svn_error_createf(apr_err, NULL,
+                                    _("%s request on '%s' failed: %d %s"),
+                                   handler->method, handler->path,
+                                   handler->sline.code, handler->sline.reason);
+            }
         }
+    }
+
+  /* Stop processing the above, on every packet arrival.  */
+  handler->reading_body = TRUE;
+
+ process_body:
 
-      return SVN_NO_ERROR; /* Error is set in caller */
+  /* We've been instructed to ignore the body. Drain whatever is present.  */
+  if (handler->discard_body)
+    {
+      *serf_status = drain_bucket(response);
+
+      /* If the handler hasn't set done (which it shouldn't have) and
+         we now have the EOF, go ahead and set it so that we can stop
+         our context loops.
+       */
+      if (!handler->done && APR_STATUS_IS_EOF(*serf_status))
+          handler->done = TRUE;
+
+      return SVN_NO_ERROR;
     }
-  else
+
+  /* If we are supposed to parse the body as a server_error, then do
+     that now.  */
+  if (handler->server_error != NULL)
     {
-      svn_error_t *err;
+      err = svn_ra_serf__handle_xml_parser(request, response,
+                                           &handler->server_error->parser,
+                                           scratch_pool);
 
-      err = ctx->response_handler(request,response, ctx->response_baton, pool);
+      /* If we do not receive an error or it is a non-transient error, return
+         immediately.
 
-      if (err
-          && (!SERF_BUCKET_READ_ERROR(err->apr_err)
-               || APR_STATUS_IS_ECONNRESET(err->apr_err)))
+         APR_EOF will be returned when parsing is complete.
+
+         APR_EAGAIN & WAIT_CONN may be intermittently returned as we proceed through
+         parsing and the network has no more data right now.  If we receive that,
+         clear the error and return - allowing serf to wait for more data.
+         */
+      if (!err || SERF_BUCKET_READ_ERROR(err->apr_err))
+        return svn_error_trace(err);
+
+      if (!APR_STATUS_IS_EOF(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);
+      /* 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;
+        }
+
+      *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;
+  apr_status_t inner_status;
+  apr_status_t outer_status;
 
-  err = svn_error_trace(
-          handle_response(request, response, ctx, &serf_status, pool));
-
-  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 +2013,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 +2035,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->session, 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 +2059,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 +2143,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 +2318,187 @@ 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;
+
+  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);
+    }
+
+  /* ### 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 */);
+
+      /* We need to check INNER_ERROR first. This is an error from the
+         callbacks that has been "dropped off" for us to retrieve. On
+         current Expat parsers, we stop the parser when an error occurs,
+         so we want to ignore EXPAT_STATUS (which reports the stoppage).
+
+         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,
+                                 _("The %s response contains invalid XML"
+                                   " (%d %s)"),
+                                 ectx->handler->method,
+                                 ectx->handler->sline.code,
+                                 ectx->handler->sline.reason);
+
+      /* The parsing went fine. What has the bucket told us?  */
+
+      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 */);
+
+          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...  */
+        }
+
+      if (status && !SERF_BUCKET_READ_ERROR(status))
+        {
+          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 = NULL;
+  ectx->cleanup_pool = result_pool;
+
+
+  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/master-passphrase/subversion/libsvn_ra_serf/xml.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/xml.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/xml.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/xml.c Wed Jun 27 15:12:37 2012
@@ -24,9 +24,6 @@
 
 
 #include <apr_uri.h>
-
-#include <expat.h>
-
 #include <serf.h>
 
 #include "svn_pools.h"
@@ -37,91 +34,182 @@
 #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;
+  svn_ra_serf__xml_cdata_t cdata_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 +410,401 @@ 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;
+}
+
+void
+svn_ra_serf__xml_context_destroy(
+  svn_ra_serf__xml_context_t *xmlctx)
+{
+  svn_pool_destroy(xmlctx->scratch_pool);
+}
+
+svn_ra_serf__xml_context_t *
+svn_ra_serf__xml_context_create(
+  const svn_ra_serf__xml_transition_t *ttable,
+  svn_ra_serf__xml_opened_t opened_cb,
+  svn_ra_serf__xml_closed_t closed_cb,
+  svn_ra_serf__xml_cdata_t cdata_cb,
+  void *baton,
+  apr_pool_t *result_pool)
+{
+  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->cdata_cb = cdata_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;
+  apr_pool_t *pool;
+
+  ensure_pool(xes);
+  pool = xes->state_pool;
+
+  data = apr_hash_make(pool);
+
+  for (; xes != NULL; xes = xes->prev)
+    {
+      if (xes->attrs != NULL)
+        {
+          apr_hash_index_t *hi;
+
+          for (hi = apr_hash_first(pool, xes->attrs); hi;
+               hi = apr_hash_next(hi))
+            {
+              const void *key;
+              apr_ssize_t klen;
+              void *val;
+
+              /* Parent name/value lifetimes are at least as long as POOL.  */
+              apr_hash_this(hi, &key, &klen, &val);
+              apr_hash_set(data, key, klen, val);
+            }
+        }
+
+      if (xes->state == stop_state)
+        break;
+    }
+
+  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;
+
+      /* Wildcard tag match.  */
+      if (*scan->name == '*')
+        break;
+
+      /* Found a specific transition.  */
+      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,
+                             apr_pstrdup(new_pool, 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 (xmlctx->opened_cb)
+    {
+      START_CALLBACK(xmlctx);
+      SVN_ERR(xmlctx->opened_cb(new_xes, xmlctx->baton,
+                                new_xes->state, &new_xes->tag,
+                                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 are waiting for a closing tag, then we are uninterested in
+     the cdata. Just return.  */
+  if (xmlctx->waiting.namespace != NULL)
+    return SVN_NO_ERROR;
+
+  /* If the current state is collecting cdata, then copy the cdata.  */
+  if (xmlctx->current->cdata != NULL)
+    {
+      svn_stringbuf_appendbytes(xmlctx->current->cdata, data, len);
+    }
+  /* ... else if a CDATA_CB has been supplied, then invoke it for
+     all states.  */
+  else if (xmlctx->cdata_cb != NULL)
+    {
+      START_CALLBACK(xmlctx);
+      SVN_ERR(xmlctx->cdata_cb(xmlctx->current,
+                               xmlctx->baton,
+                               xmlctx->current->state,
+                               data, len,
+                               xmlctx->scratch_pool));
+      END_CALLBACK(xmlctx);
+      svn_pool_clear(xmlctx->scratch_pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_svn/client.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_svn/client.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_svn/client.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_svn/client.c Wed Jun 27 15:12:37 2012
@@ -1582,7 +1582,7 @@ static svn_error_t *handle_unsupported_c
 {
   if (err && err->apr_err == SVN_ERR_RA_SVN_UNKNOWN_CMD)
     return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err,
-                            msg);
+                            _(msg));
   return err;
 }
 
@@ -1599,8 +1599,7 @@ static svn_error_t *ra_svn_stat(svn_ra_s
   SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "stat", "c(?r)", path, rev));
 
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
-                                 _("'stat' not implemented")));
-
+                                 N_("'stat' not implemented")));
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "(?l)", &list));
 
   if (! list)
@@ -1659,7 +1658,7 @@ static svn_error_t *ra_svn_get_locations
 
   /* Servers before 1.1 don't support this command. Check for this here. */
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
-                                 _("'get-locations' not implemented")));
+                                 N_("'get-locations' not implemented")));
 
   /* Read the hash items. */
   is_done = FALSE;
@@ -1713,7 +1712,8 @@ ra_svn_get_location_segments(svn_ra_sess
 
   /* Servers before 1.5 don't support this command. Check for this here. */
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
-                                 _("'get-location-segments' not implemented")));
+                                 N_("'get-location-segments'"
+                                    " not implemented")));
 
   /* Parse the response. */
   is_done = FALSE;
@@ -1780,7 +1780,7 @@ static svn_error_t *ra_svn_get_file_revs
 
   /* Servers before 1.1 don't support this command.  Check for this here. */
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
-                                 _("'get-file-revs' not implemented")));
+                                 N_("'get-file-revs' not implemented")));
 
   while (1)
     {
@@ -1911,8 +1911,8 @@ static svn_error_t *ra_svn_lock_compat(s
 
       /* Servers before 1.2 doesn't support locking.  Check this here. */
       SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
-                                     _("Server doesn't support "
-                                       "the lock command")));
+                                     N_("Server doesn't support "
+                                        "the lock command")));
 
       err = svn_ra_svn_read_cmd_response(conn, iterpool, "l", &list);
 
@@ -1975,8 +1975,8 @@ static svn_error_t *ra_svn_unlock_compat
 
       /* Servers before 1.2 don't support locking.  Check this here. */
       SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, iterpool),
-                                     _("Server doesn't support the unlock "
-                                       "command")));
+                                     N_("Server doesn't support the unlock "
+                                        "command")));
 
       err = svn_ra_svn_read_cmd_response(conn, iterpool, "");
 
@@ -2262,8 +2262,8 @@ static svn_error_t *ra_svn_get_lock(svn_
 
   /* Servers before 1.2 doesn't support locking.  Check this here. */
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
-                                 _("Server doesn't support the get-lock "
-                                   "command")));
+                                 N_("Server doesn't support the get-lock "
+                                    "command")));
 
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "(?l)", &list));
   if (list)
@@ -2315,8 +2315,8 @@ static svn_error_t *ra_svn_get_locks(svn
 
   /* Servers before 1.2 doesn't support locking.  Check this here. */
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
-                                 _("Server doesn't support the get-lock "
-                                   "command")));
+                                 N_("Server doesn't support the get-lock "
+                                    "command")));
 
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "l", &list));
 
@@ -2372,8 +2372,8 @@ static svn_error_t *ra_svn_replay(svn_ra
                                low_water_mark, send_deltas));
 
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
-                                 _("Server doesn't support the replay "
-                                   "command")));
+                                 N_("Server doesn't support the replay "
+                                    "command")));
 
   SVN_ERR(svn_ra_svn_drive_editor2(sess->conn, pool, editor, edit_baton,
                                    NULL, TRUE));
@@ -2403,8 +2403,8 @@ ra_svn_replay_range(svn_ra_session_t *se
                                low_water_mark, send_deltas));
 
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
-                                 _("Server doesn't support the replay-range "
-                                   "command")));
+                                 N_("Server doesn't support the "
+                                    "replay-range command")));
 
   iterpool = svn_pool_create(pool);
   for (rev = start_revision; rev <= end_revision; rev++)
@@ -2502,7 +2502,7 @@ ra_svn_get_deleted_rev(svn_ra_session_t 
 
   /* Servers before 1.6 don't support this command.  Check for this here. */
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
-                                 _("'get-deleted-rev' not implemented")));
+                                 N_("'get-deleted-rev' not implemented")));
 
   return svn_ra_svn_read_cmd_response(conn, pool, "r", revision_deleted);
 }

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_svn/editorp.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_svn/editorp.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_svn/editorp.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_svn/editorp.c Wed Jun 27 15:12:37 2012
@@ -844,25 +844,25 @@ static const struct {
                           const apr_array_header_t *params,
                           ra_svn_driver_state_t *ds);
 } ra_svn_edit_cmds[] = {
-  { "target-rev",       ra_svn_handle_target_rev },
-  { "open-root",        ra_svn_handle_open_root },
-  { "delete-entry",     ra_svn_handle_delete_entry },
+  { "change-file-prop", ra_svn_handle_change_file_prop },
+  { "open-file",        ra_svn_handle_open_file },
+  { "apply-textdelta",  ra_svn_handle_apply_textdelta },
+  { "textdelta-chunk",  ra_svn_handle_textdelta_chunk },
+  { "close-file",       ra_svn_handle_close_file },
   { "add-dir",          ra_svn_handle_add_dir },
   { "open-dir",         ra_svn_handle_open_dir },
   { "change-dir-prop",  ra_svn_handle_change_dir_prop },
+  { "delete-entry",     ra_svn_handle_delete_entry },
   { "close-dir",        ra_svn_handle_close_dir },
   { "absent-dir",       ra_svn_handle_absent_dir },
   { "add-file",         ra_svn_handle_add_file },
-  { "open-file",        ra_svn_handle_open_file },
-  { "apply-textdelta",  ra_svn_handle_apply_textdelta },
-  { "textdelta-chunk",  ra_svn_handle_textdelta_chunk },
   { "textdelta-end",    ra_svn_handle_textdelta_end },
-  { "change-file-prop", ra_svn_handle_change_file_prop },
-  { "close-file",       ra_svn_handle_close_file },
   { "absent-file",      ra_svn_handle_absent_file },
-  { "close-edit",       ra_svn_handle_close_edit },
   { "abort-edit",       ra_svn_handle_abort_edit },
   { "finish-replay",    ra_svn_handle_finish_replay },
+  { "target-rev",       ra_svn_handle_target_rev },
+  { "open-root",        ra_svn_handle_open_root },
+  { "close-edit",       ra_svn_handle_close_edit },
   { NULL }
 };