You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2014/01/10 20:02:05 UTC

svn commit: r1557224 - in /subversion/trunk/subversion/libsvn_ra_serf: ra_serf.h update.c util.c xml.c

Author: rhuijben
Date: Fri Jan 10 19:02:04 2014
New Revision: 1557224

URL: http://svn.apache.org/r1557224
Log:
Following up on r1557216 and other recent changes move some parts of the
public api of thetransition based xml parser in ra_serf from util.c to xml.c.

* subversion/libsvn_ra_serf/ra_serf.h
  (svn_ra_serf__xml_cb_start,
   svn_ra_serf__xml_cb_end,
   svn_ra_serf__xml_cb_cdata): Remove prototypes.

* subversion/libsvn_ra_serf/update.c
  (PARSE_CHUNK_SIZE): Update comment.

* subversion/libsvn_ra_serf/util.c
  (includes): Remove a few unused includes.

  (XML_STATUS_*,
   XML_VERSION_AT_LEAST,
   EXPAT_HAS_STOPPARSER,
   PARSE_CHUNK_SIZE,
   expat_ctx_t): Move to xml.c

  (parse_xml,
   xml_parser_cleanup): Move to xml.c

  (expat_start,
   expat_end,
   expat_cdata,
   expat_response_handler,
   svn_ra_serf__create_expat_handler): Move to xml.c

* subversion/libsvn_ra_serf/xml.c
  (includes): Add expat.h.

  (XML_STATUS_*,
   XML_VERSION_AT_LEAST,
   EXPAT_HAS_STOPPARSER,
   PARSE_CHUNK_SIZE,
   expat_ctx_t): Move here.

  (svn_ra_serf__xml_cb_start,
   svn_ra_serf__xml_cb_end,
   svn_ra_serf__xml_cb_cdata): Make static and rename to...
  (xml_cb_start,
   xml_cb_end,
   xml_cb_cdata): This.

  (parse_xml,
   xml_parser_cleanup): Move here.

  (expat_start,
   expat_end,
   expat_cdata,
   expat_response_handler): Move here. Update caller.

  (svn_ra_serf__create_expat_handler): Move here.

Modified:
    subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h
    subversion/trunk/subversion/libsvn_ra_serf/update.c
    subversion/trunk/subversion/libsvn_ra_serf/util.c
    subversion/trunk/subversion/libsvn_ra_serf/xml.c

Modified: subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h?rev=1557224&r1=1557223&r2=1557224&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h Fri Jan 10 19:02:04 2014
@@ -728,29 +728,6 @@ svn_ra_serf__xml_note(svn_ra_serf__xml_e
 apr_pool_t *
 svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes);
 
-
-/* Any XML parser may be used. When an opening tag is seen, call this
-   function to feed the information into XMLCTX.  */
-svn_error_t *
-svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
-                          const char *raw_name,
-                          const char *const *attrs);
-
-
-/* When a close tag is seen, call this function to feed the information
-   into XMLCTX.  */
-svn_error_t *
-svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
-                        const char *raw_name);
-
-
-/* When cdata is parsed by the wrapping XML parser, call this function to
-   feed the cdata into the XMLCTX.  */
-svn_error_t *
-svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
-                          const char *data,
-                          apr_size_t len);
-
 /*
  * Parses a server-side error message into a local Subversion error.
  */

Modified: subversion/trunk/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/update.c?rev=1557224&r1=1557223&r2=1557224&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/update.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/update.c Fri Jan 10 19:02:04 2014
@@ -237,7 +237,7 @@ static const svn_ra_serf__xml_transition
 #define SPILLBUF_BLOCKSIZE 4096
 #define SPILLBUF_MAXBUFFSIZE 131072
 
-#define PARSE_CHUNK_SIZE 8000 /* Copied from utils.c ### Needs tuning */
+#define PARSE_CHUNK_SIZE 8000 /* Copied from xml.c ### Needs tuning */
 
 /* Forward-declare our report context. */
 typedef struct report_context_t report_context_t;

Modified: subversion/trunk/subversion/libsvn_ra_serf/util.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/util.c?rev=1557224&r1=1557223&r2=1557224&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/util.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/util.c Fri Jan 10 19:02:04 2014
@@ -33,64 +33,21 @@
 #include <serf.h>
 #include <serf_bucket_types.h>
 
-#include <expat.h>
-
 #include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_dirent_uri.h"
 #include "svn_path.h"
 #include "svn_string.h"
-#include "svn_xml.h"
 #include "svn_props.h"
 #include "svn_dirent_uri.h"
 
 #include "../libsvn_ra/ra_loader.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_fspath.h"
-#include "private/svn_subr_private.h"
 #include "private/svn_auth_private.h"
 
 #include "ra_serf.h"
 
-
-/* Fix for older expat 1.95.x's that do not define
- * XML_STATUS_OK/XML_STATUS_ERROR
- */
-#ifndef XML_STATUS_OK
-#define XML_STATUS_OK    1
-#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
-
-
-struct expat_ctx_t {
-  svn_ra_serf__xml_context_t *xmlctx;
-  XML_Parser parser;
-  svn_ra_serf__handler_t *handler;
-  const int *expected_status;
-
-  svn_error_t *inner_error;
-
-  /* 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 },
@@ -1136,40 +1093,6 @@ svn_ra_serf__expect_empty_body(serf_requ
   return SVN_NO_ERROR;
 }
 
-/* svn_error_t * wrapper around XML_Parse */
-static APR_INLINE svn_error_t *
-parse_xml(XML_Parser parser, const char *data, apr_size_t len, svn_boolean_t is_final)
-{
-  int xml_status = XML_Parse(parser, data, (int)len, is_final);
-  const char *msg;
-
-  if (xml_status == XML_STATUS_OK)
-    return SVN_NO_ERROR;
-
-  msg = XML_ErrorString(XML_GetErrorCode(parser));
-
-  return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA,
-                          svn_error_createf(SVN_ERR_XML_MALFORMED, NULL,
-                                            _("Malformed XML: %s"),
-                                            msg),
-                          _("The XML response contains invalid XML"));
-}
-
-/* Apr pool cleanup handler to release an XML_Parser in success and error
-   conditions */
-static apr_status_t
-xml_parser_cleanup(void *baton)
-{
-  XML_Parser *xmlp = baton;
-
-  if (*xmlp)
-    {
-      (void) XML_ParserFree(*xmlp);
-      *xmlp = NULL;
-    }
-
-  return APR_SUCCESS;
-}
 
 apr_status_t
 svn_ra_serf__credentials_callback(char **username, char **password,
@@ -1900,181 +1823,6 @@ svn_ra_serf__register_editor_shim_callba
   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_boolean_t got_expected_status;
-
-  if (ectx->expected_status)
-    {
-      const int *status = ectx->expected_status;
-      got_expected_status = FALSE;
-
-      while (*status && ectx->handler->sline.code != *status)
-        status++;
-
-      got_expected_status = (*status) != 0;
-    }
-  else
-    got_expected_status = (ectx->handler->sline.code == 200);
-
-  if (!ectx->handler->server_error
-      && ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300)
-          || ! got_expected_status))
-    {
-      /* By deferring to expect_empty_body(), it will make a choice on
-         how to handle the body. Whatever the decision, the core handler
-         will take over, and we will not be called again.  */
-
-      /* ### This handles xml bodies as svn-errors (returned via serf context
-         ### loop), but ignores non-xml errors.
-
-         Current code depends on this behavior and checks itself while other
-         continues, and then verifies if work has been performed.
-
-         ### TODO: Make error checking consistent */
-
-      /* ### If !GOT_EXPECTED_STATUS, this should always produce an error */
-      return svn_error_trace(svn_ra_serf__expect_empty_body(
-                               request, response, ectx->handler,
-                               scratch_pool));
-    }
-
-  if (!ectx->parser)
-    {
-      ectx->parser = XML_ParserCreate(NULL);
-      apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser,
-                                xml_parser_cleanup, apr_pool_cleanup_null);
-      XML_SetUserData(ectx->parser, ectx);
-      XML_SetElementHandler(ectx->parser, expat_start, expat_end);
-      XML_SetCharacterDataHandler(ectx->parser, expat_cdata);
-    }
-
-  while (1)
-    {
-      apr_status_t status;
-      const char *data;
-      apr_size_t len;
-      svn_error_t *err;
-      svn_boolean_t at_eof = FALSE;
-
-      status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
-      if (SERF_BUCKET_READ_ERROR(status))
-        return svn_ra_serf__wrap_err(status, NULL);
-      else if (APR_STATUS_IS_EOF(status))
-        at_eof = TRUE;
-
-#if 0
-      /* ### move restart/skip into the core handler  */
-      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?  */
-
-      err = parse_xml(ectx->parser, data, len, at_eof /* isFinal */);
-
-      err = svn_error_compose_create(ectx->inner_error, err);
-
-      if (at_eof || err)
-        {
-          /* Release xml parser state/tables. */
-          apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
-                               xml_parser_cleanup);
-        }
-
-      /* We need to check INNER_ERROR first. This is an error from the
-         callbacks that has been "dropped off" for us to retrieve. On
-         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.  */
-      SVN_ERR(err);
-
-      /* The parsing went fine. What has the bucket told us?  */
-
-      if (at_eof)
-        {
-          /* Make sure we actually got xml and clean up after parsing */
-          SVN_ERR(svn_ra_serf__xml_context_done(ectx->xmlctx));
-        }
-
-      if (status && !SERF_BUCKET_READ_ERROR(status))
-        {
-          return svn_ra_serf__wrap_err(status, NULL);
-        }
-    }
-
-  /* NOTREACHED */
-}
-
 /* Shandard done_delegate handler */
 static svn_error_t *
 response_done(serf_request_t *request,
@@ -2116,27 +1864,3 @@ svn_ra_serf__create_handler(apr_pool_t *
   return handler;
 }
 
-
-svn_ra_serf__handler_t *
-svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx,
-                                  const int *expected_status,
-                                  apr_pool_t *result_pool)
-{
-  svn_ra_serf__handler_t *handler;
-  struct expat_ctx_t *ectx;
-
-  ectx = apr_pcalloc(result_pool, sizeof(*ectx));
-  ectx->xmlctx = xmlctx;
-  ectx->parser = NULL;
-  ectx->expected_status = expected_status;
-  ectx->cleanup_pool = result_pool;
-
-
-  handler = svn_ra_serf__create_handler(result_pool);
-  handler->response_handler = expat_response_handler;
-  handler->response_baton = ectx;
-
-  ectx->handler = handler;
-
-  return handler;
-}

Modified: subversion/trunk/subversion/libsvn_ra_serf/xml.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/xml.c?rev=1557224&r1=1557223&r2=1557224&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/xml.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/xml.c Fri Jan 10 19:02:04 2014
@@ -24,6 +24,7 @@
 
 
 #include <apr_uri.h>
+#include <expat.h>
 #include <serf.h>
 
 #include "svn_private_config.h"
@@ -42,6 +43,30 @@
 #include "ra_serf.h"
 
 
+/* Fix for older expat 1.95.x's that do not define
+ * XML_STATUS_OK/XML_STATUS_ERROR
+ */
+#ifndef XML_STATUS_OK
+#define XML_STATUS_OK    1
+#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
+
+
 struct svn_ra_serf__xml_context_t {
   /* Current state information.  */
   svn_ra_serf__xml_estate_t *current;
@@ -126,6 +151,19 @@ struct svn_ra_serf__xml_estate_t {
 
 };
 
+struct expat_ctx_t {
+  svn_ra_serf__xml_context_t *xmlctx;
+  XML_Parser parser;
+  svn_ra_serf__handler_t *handler;
+  const int *expected_status;
+
+  svn_error_t *inner_error;
+
+  /* Do not use this pool for allocation. It is merely recorded for running
+     the cleanup handler.  */
+  apr_pool_t *cleanup_pool;
+};
+
 
 static void
 define_namespaces(svn_ra_serf__ns_t **ns_list,
@@ -573,10 +611,10 @@ svn_ra_serf__xml_state_pool(svn_ra_serf_
 }
 
 
-svn_error_t *
-svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
-                          const char *raw_name,
-                          const char *const *attrs)
+static svn_error_t *
+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;
@@ -722,9 +760,9 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__x
 }
 
 
-svn_error_t *
-svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
-                        const char *raw_name)
+static svn_error_t *
+xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
+           const char *raw_name)
 {
   svn_ra_serf__xml_estate_t *xes = xmlctx->current;
 
@@ -777,10 +815,10 @@ svn_ra_serf__xml_cb_end(svn_ra_serf__xml
 }
 
 
-svn_error_t *
-svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
-                          const char *data,
-                          apr_size_t len)
+static svn_error_t *
+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.  */
@@ -809,3 +847,216 @@ svn_ra_serf__xml_cb_cdata(svn_ra_serf__x
   return SVN_NO_ERROR;
 }
 
+/* svn_error_t * wrapper around XML_Parse */
+static APR_INLINE svn_error_t *
+parse_xml(XML_Parser parser, const char *data, apr_size_t len, svn_boolean_t is_final)
+{
+  int xml_status = XML_Parse(parser, data, (int)len, is_final);
+  const char *msg;
+
+  /* ### Perhaps we should filter some specific error codes on systems
+         that use STOPPARSER to hide addtional errors */
+  if (xml_status == XML_STATUS_OK)
+    return SVN_NO_ERROR;
+
+  msg = XML_ErrorString(XML_GetErrorCode(parser));
+
+  return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA,
+                          svn_error_createf(SVN_ERR_XML_MALFORMED, NULL,
+                                            _("Malformed XML: %s"),
+                                            msg),
+                          _("The XML response contains invalid XML"));
+}
+
+/* Apr pool cleanup handler to release an XML_Parser in success and error
+   conditions */
+static apr_status_t
+xml_parser_cleanup(void *baton)
+{
+  XML_Parser *xmlp = baton;
+
+  if (*xmlp)
+    {
+      (void) XML_ParserFree(*xmlp);
+      *xmlp = NULL;
+    }
+
+  return APR_SUCCESS;
+}
+
+/* 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(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(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(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_boolean_t got_expected_status;
+
+  if (ectx->expected_status)
+    {
+      const int *status = ectx->expected_status;
+      got_expected_status = FALSE;
+
+      while (*status && ectx->handler->sline.code != *status)
+        status++;
+
+      got_expected_status = (*status) != 0;
+    }
+  else
+    got_expected_status = (ectx->handler->sline.code == 200);
+
+  if (!ectx->handler->server_error
+      && ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300)
+          || ! got_expected_status))
+    {
+      /* By deferring to expect_empty_body(), it will make a choice on
+         how to handle the body. Whatever the decision, the core handler
+         will take over, and we will not be called again.  */
+
+      /* ### This handles xml bodies as svn-errors (returned via serf context
+         ### loop), but ignores non-xml errors.
+
+         Current code depends on this behavior and checks itself while other
+         continues, and then verifies if work has been performed.
+
+         ### TODO: Make error checking consistent */
+
+      /* ### If !GOT_EXPECTED_STATUS, this should always produce an error */
+      return svn_error_trace(svn_ra_serf__expect_empty_body(
+                               request, response, ectx->handler,
+                               scratch_pool));
+    }
+
+  if (!ectx->parser)
+    {
+      ectx->parser = XML_ParserCreate(NULL);
+      apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser,
+                                xml_parser_cleanup, apr_pool_cleanup_null);
+      XML_SetUserData(ectx->parser, ectx);
+      XML_SetElementHandler(ectx->parser, expat_start, expat_end);
+      XML_SetCharacterDataHandler(ectx->parser, expat_cdata);
+    }
+
+  while (1)
+    {
+      apr_status_t status;
+      const char *data;
+      apr_size_t len;
+      svn_error_t *err;
+      svn_boolean_t at_eof = FALSE;
+
+      status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
+      if (SERF_BUCKET_READ_ERROR(status))
+        return svn_ra_serf__wrap_err(status, NULL);
+      else if (APR_STATUS_IS_EOF(status))
+        at_eof = TRUE;
+
+      err = parse_xml(ectx->parser, data, len, at_eof /* isFinal */);
+
+      err = svn_error_compose_create(ectx->inner_error, err);
+
+      if (at_eof || err)
+        {
+          /* Release xml parser state/tables. */
+          apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
+                               xml_parser_cleanup);
+        }
+
+      SVN_ERR(err);
+
+      /* The parsing went fine. What has the bucket told us?  */
+      if (at_eof)
+        {
+          /* Make sure we actually got xml and clean up after parsing */
+          SVN_ERR(svn_ra_serf__xml_context_done(ectx->xmlctx));
+        }
+
+      if (status && !SERF_BUCKET_READ_ERROR(status))
+        {
+          return svn_ra_serf__wrap_err(status, NULL);
+        }
+    }
+
+  /* NOTREACHED */
+}
+
+
+svn_ra_serf__handler_t *
+svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx,
+                                  const int *expected_status,
+                                  apr_pool_t *result_pool)
+{
+  svn_ra_serf__handler_t *handler;
+  struct expat_ctx_t *ectx;
+
+  ectx = apr_pcalloc(result_pool, sizeof(*ectx));
+  ectx->xmlctx = xmlctx;
+  ectx->parser = NULL;
+  ectx->expected_status = expected_status;
+  ectx->cleanup_pool = result_pool;
+
+  handler = svn_ra_serf__create_handler(result_pool);
+  handler->response_handler = expat_response_handler;
+  handler->response_baton = ectx;
+
+  ectx->handler = handler;
+
+  return handler;
+}