subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhuij...@apache.org
Subject svn commit: r1553443 - in /subversion/trunk/subversion: libsvn_ra_serf/multistatus.c libsvn_ra_serf/ra_serf.h libsvn_ra_serf/util.c tests/cmdline/prop_tests.py
Date Thu, 26 Dec 2013 03:50:01 GMT
Author: rhuijben
Date: Thu Dec 26 03:50:01 2013
New Revision: 1553443

URL: http://svn.apache.org/r1553443
Log:
Switch libsvn_ra_serf's http error parser to the transition based xml parser
and at the same time stop ignoring most multi response status reports, such
as used by property and revision property changes.

* subversion/libsvn_ra_serf/multistatus.c
  New file.

* subversion/libsvn_ra_serf/ra_serf.h
  (svn_ra_serf__server_error_t): Replace some variables by a list of internal
    per path/property items that is later parsed into an error.
  (svn_ra_serf__setup_error_parsing,
   svn_ra_serf__handle_server_error): New function.

* subversion/libsvn_ra_serf/util.c
  (start_error,
   end_error,
   cdata_error,
   begin_error_parsing): Remove functions.

  (svn_ra_serf__expect_empty_body): Update caller.
  (parse_dav_status): Move to multistatus.c
  (start_207,
   end_207,
   cdata_207): Remove functions.
  (svn_ra_serf__handle_multistatus_only): Move to multistatus.c
  (handle_response): Update caller. Extract some code to
    svn_ra_serf__handle_server_error in multistatus.c
  (svn_ra_serf__error_on_status): Provide handling for a few more status values.
  (expat_response_handler): Make sure we don't convert an existing error parser.

* subversion/tests/cmdline/prop_tests.py
  (invalid_propvalues): Remove XFail marker. We just see the error after this
    patch (and the requested detailed message after r1553441).

Added:
    subversion/trunk/subversion/libsvn_ra_serf/multistatus.c   (with props)
Modified:
    subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h
    subversion/trunk/subversion/libsvn_ra_serf/util.c
    subversion/trunk/subversion/tests/cmdline/prop_tests.py

Added: subversion/trunk/subversion/libsvn_ra_serf/multistatus.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/multistatus.c?rev=1553443&view=auto
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/multistatus.c (added)
+++ subversion/trunk/subversion/libsvn_ra_serf/multistatus.c Thu Dec 26 03:50:01 2013
@@ -0,0 +1,684 @@
+/*
+ * multistatus.c : parse multistatus (error) responses.
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+
+
+#include <assert.h>
+
+#include <apr.h>
+
+#include <serf.h>
+#include <serf_bucket_types.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 "private/svn_dep_compat.h"
+#include "private/svn_fspath.h"
+
+#include "ra_serf.h"
+
+/* The current state of our XML parsing. */
+typedef enum iprops_state_e {
+  INITIAL = XML_STATE_INITIAL,
+  MS_MULTISTATUS,
+
+  MS_RESPONSE,
+  MS_RESPONSE_HREF,
+
+  MS_PROPSTAT,
+  MS_PROPSTAT_PROP,
+  MS_PROPSTAT_PROP_NAME,
+  MS_PROPSTAT_STATUS,
+  MS_PROPSTAT_RESPONSEDESCRIPTION,
+  MS_PROPSTAT_ERROR,
+  MS_PROPSTAT_ERROR_HUMANREADABLE,
+
+  MS_RESPONSE_STATUS,
+  MS_RESPONSE_RESPONSEDESCRIPTION,
+  MS_RESPONSE_ERROR,
+  MS_RESPONSE_ERROR_HUMANREADABLE,
+
+  MS_MULTISTATUS_RESPONSEDESCRIPTION,
+
+  D_ERROR,
+  S_ERROR,
+  M_ERROR_HUMANREADABLE
+} iprops_state_e;
+
+/*
+  <D:multistatus xmlns:D="DAV:">
+    <D:response>
+      <D:href>http://something</D:href>
+      <!-- Possibly multiple D:href elements -->
+      <D:status>HTTP/1.1 500 failed</D:status>
+      <D:error>
+        <S:human-readable code="12345">
+          Some Subversion error
+        </S:human-readable>
+      </D:error>
+      <D:responsedescription>
+        Human readable description
+      </D:responsedescription>
+      <D:location>http://redirected</D:location>
+    </D:response>
+    ...
+  </D:multistatus>
+
+  Or for property operations:
+
+  <D:multistatus xmlns:D="DAV:">
+    <D:response>
+      <D:href>http://somewhere-else</D:href>
+      <D:propstat>
+        <D:propname><C:myprop /></D:propname>
+        <D:status>HTTP/1.1 499 failed</D:status>
+        <D:error>
+          <S:human-readable code="12345">
+            Some Subversion error
+          </S:human-readable>
+        </D:error>
+        <D:responsedescription>
+          Human readable description
+        </D:responsedescription>
+      </D:propstat>
+      <D:status>HTTP/1.1 499 failed</D:status>
+      <D:error>
+        <S:human-readable code="12345">
+          Some Subversion error
+        </S:human-readable>
+      </D:error>
+      <D:responsedescription>
+        Human readable description
+      </D:responsedescription>
+      <D:location>http://redirected</D:location>
+    <D:responsedescription>
+      Global description
+    <D:responsedescription>
+  </D:multistatus>
+
+  Or on request failures
+  <D:error>
+    <S:error>
+  </D:error>
+ */
+
+#define D_ "DAV:"
+#define S_ SVN_XML_NAMESPACE
+#define M_ "http://apache.org/dav/xmlns"
+static const svn_ra_serf__xml_transition_t multistatus_ttable[] = {
+  { INITIAL, D_, "multistatus", MS_MULTISTATUS,
+    FALSE, { NULL }, FALSE },
+
+  { MS_MULTISTATUS, D_, "responsedescription", MS_MULTISTATUS_RESPONSEDESCRIPTION,
+    TRUE, { NULL }, TRUE },
+
+  /* <response> */
+  { MS_MULTISTATUS, D_, "response", MS_RESPONSE,
+    FALSE, { NULL }, TRUE },
+
+  { MS_RESPONSE, D_, "href", MS_RESPONSE_HREF,
+    TRUE, { NULL }, TRUE },
+
+  /* <propstat> */
+  { MS_RESPONSE, D_, "propstat", MS_PROPSTAT,
+    FALSE, { NULL }, TRUE },
+
+  { MS_PROPSTAT, D_, "prop", MS_PROPSTAT_PROP,
+    FALSE, { NULL }, FALSE },
+
+  { MS_PROPSTAT_PROP, "", "*", MS_PROPSTAT_PROP_NAME,
+    FALSE, { NULL }, FALSE },
+
+  { MS_PROPSTAT, D_, "status", MS_PROPSTAT_STATUS,
+    TRUE, { NULL }, TRUE },
+
+  { MS_PROPSTAT, D_, "responsedescription", MS_PROPSTAT_RESPONSEDESCRIPTION,
+    TRUE, { NULL }, TRUE },
+
+  { MS_PROPSTAT, D_, "error", MS_PROPSTAT_ERROR,
+    FALSE, { NULL }, FALSE },
+
+  { MS_PROPSTAT_ERROR, M_, "human-readable", MS_PROPSTAT_ERROR_HUMANREADABLE,
+    TRUE, { "?errcode", NULL }, TRUE },
+  /* </propstat> */
+
+
+  { MS_RESPONSE, D_, "status", MS_RESPONSE_STATUS,
+    TRUE, { NULL }, TRUE },
+
+  { MS_RESPONSE, D_, "responsedescription", MS_RESPONSE_RESPONSEDESCRIPTION,
+    TRUE, { NULL }, TRUE },
+
+  { MS_RESPONSE, D_, "error", MS_RESPONSE_ERROR,
+    FALSE, { NULL }, TRUE },
+
+  { MS_RESPONSE_ERROR, M_, "human-readable", MS_RESPONSE_ERROR_HUMANREADABLE,
+    TRUE, { "?errcode", NULL }, TRUE },
+
+  /* </response> */
+
+  { MS_MULTISTATUS, D_, "responsedescription", MS_MULTISTATUS_RESPONSEDESCRIPTION,
+    TRUE, { NULL }, TRUE },
+
+
+  { INITIAL, D_, "error", D_ERROR,
+    FALSE, { NULL }, TRUE },
+
+  { D_ERROR, S_, "error", S_ERROR,
+    FALSE, { NULL }, FALSE },
+
+  { D_ERROR, M_, "human-readable", M_ERROR_HUMANREADABLE,
+    TRUE, { "?errcode", NULL }, TRUE },
+
+  { 0 }
+};
+
+/* 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 *
+parse_status_line(int *status_code_out,
+                  const char **reason,
+                  const char *status_line,
+                  apr_pool_t *result_pool,
+                  apr_pool_t *scratch_pool)
+{
+  svn_error_t *err;
+  const char *token;
+  char *tok_status;
+  svn_stringbuf_t *temp_buf = svn_stringbuf_create(status_line, scratch_pool);
+
+  svn_stringbuf_strip_whitespace(temp_buf);
+  token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status);
+  if (token)
+    token = apr_strtok(NULL, " \t\r\n", &tok_status);
+  if (!token)
+    return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+                             _("Malformed DAV:status '%s'"),
+                             status_line);
+  err = svn_cstring_atoi(status_code_out, token);
+  if (err)
+    return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
+                             _("Malformed DAV:status '%s'"),
+                             status_line);
+
+  token = apr_strtok(NULL, " \t\r\n", &tok_status);
+
+  *reason = apr_pstrdup(result_pool, token);
+
+  return SVN_NO_ERROR;
+}
+
+
+typedef struct error_item_t
+{
+  const char *path;
+  const char *propname;
+
+  int http_status;
+  const char *http_reason;
+  apr_status_t apr_err;
+
+  const char *message;
+} error_item_t;
+
+static svn_error_t *
+multistatus_opened(svn_ra_serf__xml_estate_t *xes,
+                   void *baton,
+                   int entered_state,
+                   const svn_ra_serf__dav_props_t *tag,
+                   apr_pool_t *scratch_pool)
+{
+  const char *propname;
+
+  switch (entered_state)
+    {
+      case MS_PROPSTAT_PROP_NAME:
+        if (strcmp(tag->namespace, SVN_DAV_PROP_NS_SVN) == 0)
+          propname = apr_pstrcat(scratch_pool, SVN_PROP_PREFIX, tag->name,
+                                 SVN_VA_NULL);
+        else
+          propname = tag->name;
+        svn_ra_serf__xml_note(xes, MS_PROPSTAT, "propname", propname);
+        break;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+multistatus_closed(svn_ra_serf__xml_estate_t *xes,
+                   void *baton,
+                   int leaving_state,
+                   const svn_string_t *cdata,
+                   apr_hash_t *attrs,
+                   apr_pool_t *scratch_pool)
+{
+  struct svn_ra_serf__server_error_t *server_error = baton;
+  const char *errcode;
+  const char *status;
+
+  switch (leaving_state)
+    {
+      case MS_RESPONSE_HREF:
+        {
+          apr_status_t status;
+          apr_uri_t uri;
+
+          status = apr_uri_parse(scratch_pool, cdata->data, &uri);
+          if (status)
+            return svn_ra_serf__wrap_err(status, NULL);
+          svn_ra_serf__xml_note(xes, MS_RESPONSE, "path",
+                                svn_urlpath__canonicalize(uri.path, scratch_pool));
+        }
+        break;
+      case MS_RESPONSE_STATUS:
+        svn_ra_serf__xml_note(xes, MS_RESPONSE, "status", cdata->data);
+        break;
+      case MS_RESPONSE_ERROR_HUMANREADABLE:
+        svn_ra_serf__xml_note(xes, MS_RESPONSE, "human-readable", cdata->data);
+        errcode = svn_hash_gets(attrs, "errcode");
+        if (errcode)
+          svn_ra_serf__xml_note(xes, MS_RESPONSE, "errcode", errcode);
+        break;
+      case MS_RESPONSE:
+        if (status = svn_hash_gets(attrs, "status"))
+          {
+            error_item_t *item;
+
+            item = apr_pcalloc(server_error->pool, sizeof(*item));
+
+            item->path = apr_pstrdup(server_error->pool,
+                                     svn_hash_gets(attrs, "path"));
+
+            SVN_ERR(parse_status_line(&item->http_status,
+                                      &item->http_reason,
+                                      status,
+                                      server_error->pool,
+                                      scratch_pool));
+
+            /* Do we have a mod_dav specific message? */
+            item->message = svn_hash_gets(attrs, "human-readable");
+
+            if (item->message)
+              {
+                const char *errcode = svn_hash_gets(attrs, "errcode");
+
+                if (errcode)
+                  {
+                    apr_int64_t val;
+
+                    SVN_ERR(svn_cstring_atoi64(&val, errcode));
+                    item->apr_err = (apr_status_t)val;
+                  }
+
+                item->message = apr_pstrdup(server_error->pool, item->message);
+              }
+            else
+              item->message = apr_pstrdup(server_error->pool,
+                                          svn_hash_gets(attrs, "description"));
+
+            APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
+          }
+        break;
+
+
+      case MS_PROPSTAT_STATUS:
+        svn_ra_serf__xml_note(xes, MS_PROPSTAT, "status", cdata->data);
+        break;
+      case MS_PROPSTAT_ERROR_HUMANREADABLE:
+        svn_ra_serf__xml_note(xes, MS_PROPSTAT, "human-readable", cdata->data);
+        errcode = svn_hash_gets(attrs, "errcode");
+        if (errcode)
+          svn_ra_serf__xml_note(xes, MS_PROPSTAT, "errcode", errcode);
+        break;
+      case MS_PROPSTAT_RESPONSEDESCRIPTION:
+        svn_ra_serf__xml_note(xes, MS_PROPSTAT, "description",
+                              cdata->data);
+        break;
+
+      case MS_PROPSTAT:
+        if (status = svn_hash_gets(attrs, "status"))
+          {
+            apr_hash_t *response_attrs;
+            error_item_t *item;
+
+            response_attrs = svn_ra_serf__xml_gather_since(xes, MS_RESPONSE);
+            item = apr_pcalloc(server_error->pool, sizeof(*item));
+
+            item->path = apr_pstrdup(server_error->pool,
+                                     svn_hash_gets(response_attrs, "path"));
+            item->propname = apr_pstrdup(server_error->pool,
+                                         svn_hash_gets(attrs, "propname"));
+
+            SVN_ERR(parse_status_line(&item->http_status,
+                                      &item->http_reason,
+                                      status,
+                                      server_error->pool,
+                                      scratch_pool));
+
+            /* Do we have a mod_dav specific message? */
+            item->message = svn_hash_gets(attrs, "human-readable");
+
+            if (item->message)
+              {
+                const char *errcode = svn_hash_gets(attrs, "errcode");
+
+                if (errcode)
+                  {
+                    apr_int64_t val;
+
+                    SVN_ERR(svn_cstring_atoi64(&val, errcode));
+                    item->apr_err = (apr_status_t)val;
+                  }
+
+                item->message = apr_pstrdup(server_error->pool, item->message);
+              }
+            else
+              item->message = apr_pstrdup(server_error->pool,
+                                          svn_hash_gets(attrs, "description"));
+
+
+            APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
+          }
+        break;
+
+      case M_ERROR_HUMANREADABLE:
+        svn_ra_serf__xml_note(xes, D_ERROR, "human-readable", cdata->data);
+        errcode = svn_hash_gets(attrs, "errcode");
+        if (errcode)
+          svn_ra_serf__xml_note(xes, D_ERROR, "errcode", errcode);
+        break;
+
+      case D_ERROR:
+        {
+          error_item_t *item;
+
+          item = apr_pcalloc(server_error->pool, sizeof(*item));
+
+          item->http_status = 500;
+
+          /* Do we have a mod_dav specific message? */
+          item->message = svn_hash_gets(attrs, "human-readable");
+
+          if (item->message)
+            {
+              const char *errcode = svn_hash_gets(attrs, "errcode");
+
+              if (errcode)
+                {
+                  apr_int64_t val;
+
+                  SVN_ERR(svn_cstring_atoi64(&val, errcode));
+                  item->apr_err = (apr_status_t)val;
+                }
+
+              item->message = apr_pstrdup(server_error->pool, item->message);
+            }
+          else
+            item->message = apr_pstrdup(server_error->pool,
+                                        svn_hash_gets(attrs, "description"));
+
+
+          APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
+          }
+    }
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+multistatus_done(void *baton,
+                 apr_pool_t *scratch_pool)
+{
+  struct svn_ra_serf__server_error_t *server_error = baton;
+  svn_error_t *err = NULL;
+  int i;
+
+  for (i = 0; i < server_error->items->nelts; i++)
+    {
+      const error_item_t *item;
+      apr_status_t status;
+      const char *message;
+
+      item = APR_ARRAY_IDX(server_error->items, i, error_item_t *);
+
+      if (!item->apr_err && item->http_status == 200)
+        {
+          continue; /* Success code */
+        }
+      else if (!item->apr_err && item->http_status == 424
+               && strcmp(server_error->method, "PROPPATCH") == 0)
+        {
+          continue; /* Failed because other PROPPATCH operations failed */
+        }
+
+      if (item->apr_err)
+        status = item->apr_err;
+      else if (err)
+        status = err->apr_err;
+      else
+        switch (item->http_status)
+          {
+            case 301:
+            case 302:
+            case 303:
+            case 307:
+            case 308:
+              status = SVN_ERR_RA_DAV_RELOCATED;
+              break;
+            case 403:
+              status = SVN_ERR_RA_DAV_FORBIDDEN;
+              break;
+            case 404:
+              status = SVN_ERR_FS_NOT_FOUND;
+              break;
+            case 423:
+              status = SVN_ERR_FS_NO_LOCK_TOKEN;
+              break;
+            case 500:
+              status = SVN_ERR_RA_DAV_REQUEST_FAILED;
+              break;
+            case 501:
+              status = SVN_ERR_UNSUPPORTED_FEATURE;
+              break;
+            case 412:
+              /* Handle magic error value for atomic revprops */
+              if (strcmp(server_error->method, "PROPPATCH") == 0)
+                status = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH;
+              else
+                status = SVN_ERR_RA_DAV_REQUEST_FAILED;
+              break;
+
+            default:
+              status = SVN_ERR_RA_DAV_REQUEST_FAILED;
+              break;
+        }
+
+      if (item->message)
+        {
+          svn_stringbuf_t *sb = svn_stringbuf_create(item->message,
+                                                     scratch_pool);
+
+          svn_stringbuf_strip_whitespace(sb);
+          message = sb->data;
+        }
+      else if (item->propname
+               && strcmp(server_error->method, "PROPPATCH") == 0)
+        {
+          message = apr_psprintf(scratch_pool,
+                                 _("Property operation on '%s' failed"),
+                                 item->propname);
+        }
+      else
+        message = NULL;
+
+      SVN_ERR_ASSERT(status > 0);
+      err = svn_error_compose_create(
+                    err,
+                    svn_error_create(status, NULL, message));
+    }
+
+  server_error->error = err;
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra_serf__setup_error_parsing(svn_ra_serf__server_error_t **server_err,
+                                 svn_ra_serf__handler_t *handler,
+                                 svn_boolean_t expect_207_only,
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool)
+{
+  svn_ra_serf__server_error_t *ms_baton;
+  svn_ra_serf__handler_t *tmp_handler;
+
+  int *expected_status = apr_pcalloc(result_pool,
+                                     2 * sizeof(expected_status[0]));
+
+  expected_status[0] = handler->sline.code;
+
+  ms_baton = apr_pcalloc(result_pool, sizeof(*ms_baton));
+  ms_baton->pool = result_pool;
+  /*ms_baton->error = svn_error_create(APR_SUCCESS, NULL, NULL);*/
+
+  ms_baton->items = apr_array_make(result_pool, 4, sizeof(error_item_t *));
+  ms_baton->method = apr_pstrdup(result_pool, handler->method);
+
+  ms_baton->xmlctx = svn_ra_serf__xml_context_create(multistatus_ttable,
+                                                     multistatus_opened,
+                                                     multistatus_closed,
+                                                     NULL, multistatus_done,
+                                                     ms_baton,
+                                                     ms_baton->pool);
+
+  tmp_handler = svn_ra_serf__create_expat_handler(ms_baton->xmlctx,
+                                                  expected_status,
+                                                  result_pool);
+
+  /* Ugly way to obtain expat_handler() */
+  tmp_handler->sline = handler->sline;
+  ms_baton->response_handler = tmp_handler->response_handler;
+  ms_baton->response_baton = tmp_handler->response_baton;
+
+  *server_err = ms_baton;
+  return SVN_NO_ERROR;
+}
+
+
+
+/* Implements svn_ra_serf__response_handler_t */
+svn_error_t *
+svn_ra_serf__handle_multistatus_only(serf_request_t *request,
+                                     serf_bucket_t *response,
+                                     void *baton,
+                                     apr_pool_t *scratch_pool)
+{
+  svn_ra_serf__handler_t *handler = baton;
+
+  /* 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);
+
+    {
+      serf_bucket_t *hdrs;
+      const char *val;
+
+      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)
+        {
+          svn_ra_serf__server_error_t *server_err;
+
+          SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err,
+                                                   handler,
+                                                   TRUE,
+                                                   handler->handler_pool,
+                                                   handler->handler_pool));
+
+          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;
+        }
+    }
+
+  /* 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;
+}
+
+svn_error_t *
+svn_ra_serf__handle_server_error(svn_ra_serf__server_error_t *server_error,
+                                 svn_ra_serf__handler_t *handler,
+                                 serf_request_t *request,
+                                 serf_bucket_t *response,
+                                 apr_status_t *serf_status,
+                                 apr_pool_t *scratch_pool)
+{
+  svn_error_t *err;
+
+  err = server_error->response_handler(request, response,
+                                       server_error->response_baton,
+                                       scratch_pool);
+ /* If we do not receive an error or it is a non-transient error, return
+     immediately.
+
+     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))
+    {
+      *serf_status = err->apr_err;
+      svn_error_clear(err);
+      return SVN_NO_ERROR;
+    }
+
+  /* Clear the EOF. We don't need it.  */
+  svn_error_clear(err);
+
+  *serf_status = APR_EOF;
+  return SVN_NO_ERROR;
+}
\ No newline at end of file

Propchange: subversion/trunk/subversion/libsvn_ra_serf/multistatus.c
------------------------------------------------------------------------------
    svn:eol-style = native

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=1553443&r1=1553442&r2=1553443&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h Thu Dec 26 03:50:01 2013
@@ -890,7 +890,6 @@ svn_ra_serf__xml_cb_cdata(svn_ra_serf__x
                           const char *data,
                           apr_size_t len);
 
-
 /*
  * Parses a server-side error message into a local Subversion error.
  */
@@ -898,23 +897,17 @@ struct svn_ra_serf__server_error_t {
   /* Our local representation of the error. */
   svn_error_t *error;
 
-  /* Are we done with the response? */
-  svn_boolean_t done;
-
-  /* Have we seen an error tag? */
-  svn_boolean_t in_error;
-
-  /* Have we seen a HTTP "412 Precondition Failed" error? */
-  svn_boolean_t contains_precondition_error;
-
-  /* Should we be collecting the XML cdata? */
-  svn_boolean_t collect_cdata;
+  apr_pool_t *pool;
 
-  /* Collected cdata. NULL if cdata not needed. */
-  svn_stringbuf_t *cdata;
+  /* The partial errors to construct the final error from */
+  apr_array_header_t *items;
+  const char *method;
 
   /* XML parser and namespace used to parse the remote response */
-  svn_ra_serf__xml_parser_t parser;
+  svn_ra_serf__xml_context_t *xmlctx;
+
+  svn_ra_serf__response_handler_t response_handler;
+  void *response_baton;
 };
 
 
@@ -974,6 +967,27 @@ svn_ra_serf__expect_empty_body(serf_requ
 
 
 /*
+ * This function sets up error parsing for an existing request
+ */
+svn_error_t *
+svn_ra_serf__setup_error_parsing(svn_ra_serf__server_error_t **server_err,
+                                 svn_ra_serf__handler_t *handler,
+                                 svn_boolean_t expect_207_only,
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool);
+
+/*
+ * Forwards response data to the server error parser
+ */
+svn_error_t *
+svn_ra_serf__handle_server_error(svn_ra_serf__server_error_t *server_error,
+                                 svn_ra_serf__handler_t *handler,
+                                 serf_request_t *request,
+                                 serf_bucket_t *response,
+                                 apr_status_t *serf_status,
+                                 apr_pool_t *scratch_pool);
+
+/*
  * This function will feed the RESPONSE body into XMLP.  When parsing is
  * completed (i.e. an EOF is received), *DONE is set to TRUE.
  * Implements svn_ra_serf__response_handler_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=1553443&r1=1553442&r2=1553443&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/util.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/util.c Thu Dec 26 03:50:01 2013
@@ -1007,104 +1007,6 @@ svn_ra_serf__context_run_one(svn_ra_serf
 }
 
 
-/*
- * Expat callback invoked on a start element tag for an error response.
- */
-static svn_error_t *
-start_error(svn_ra_serf__xml_parser_t *parser,
-            svn_ra_serf__dav_props_t name,
-            const char **attrs,
-            apr_pool_t *scratch_pool)
-{
-  svn_ra_serf__server_error_t *ctx = parser->user_data;
-
-  if (!ctx->in_error &&
-      strcmp(name.namespace, "DAV:") == 0 &&
-      strcmp(name.name, "error") == 0)
-    {
-      ctx->in_error = TRUE;
-    }
-  else if (ctx->in_error && strcmp(name.name, "human-readable") == 0)
-    {
-      const char *err_code;
-
-      err_code = svn_xml_get_attr_value("errcode", attrs);
-      if (err_code)
-        {
-          apr_int64_t val;
-
-          SVN_ERR(svn_cstring_atoi64(&val, err_code));
-          ctx->error->apr_err = (apr_status_t)val;
-        }
-
-      /* If there's no error code provided, or if the provided code is
-         0 (which can happen sometimes depending on how the error is
-         constructed on the server-side), just pick a generic error
-         code to run with. */
-      if (! ctx->error->apr_err)
-        {
-          ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
-        }
-
-      /* Start collecting cdata. */
-      svn_stringbuf_setempty(ctx->cdata);
-      ctx->collect_cdata = TRUE;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on an end element tag for a PROPFIND response.
- */
-static svn_error_t *
-end_error(svn_ra_serf__xml_parser_t *parser,
-          svn_ra_serf__dav_props_t name,
-          apr_pool_t *scratch_pool)
-{
-  svn_ra_serf__server_error_t *ctx = parser->user_data;
-
-  if (ctx->in_error &&
-      strcmp(name.namespace, "DAV:") == 0 &&
-      strcmp(name.name, "error") == 0)
-    {
-      ctx->in_error = FALSE;
-    }
-  if (ctx->in_error && strcmp(name.name, "human-readable") == 0)
-    {
-      /* 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. */
-      svn_stringbuf_strip_whitespace(ctx->cdata);
-
-      ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data,
-                                           ctx->cdata->len);
-      ctx->collect_cdata = FALSE;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on CDATA elements in an error response.
- *
- * This callback can be called multiple times.
- */
-static svn_error_t *
-cdata_error(svn_ra_serf__xml_parser_t *parser,
-            const char *data,
-            apr_size_t len,
-            apr_pool_t *scratch_pool)
-{
-  svn_ra_serf__server_error_t *ctx = parser->user_data;
-
-  if (ctx->collect_cdata)
-    {
-      svn_stringbuf_appendbytes(ctx->cdata, data, len);
-    }
-
-  return SVN_NO_ERROR;
-}
 
 
 static apr_status_t
@@ -1124,28 +1026,7 @@ drain_bucket(serf_bucket_t *bucket)
 }
 
 
-static svn_ra_serf__server_error_t *
-begin_error_parsing(svn_ra_serf__xml_start_element_t start,
-                    svn_ra_serf__xml_end_element_t end,
-                    svn_ra_serf__xml_cdata_chunk_handler_t cdata,
-                    apr_pool_t *result_pool)
-{
-  svn_ra_serf__server_error_t *server_err;
-
-  server_err = apr_pcalloc(result_pool, sizeof(*server_err));
-  server_err->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;
-}
 
 /* Implements svn_ra_serf__response_handler_t */
 svn_error_t *
@@ -1234,7 +1115,7 @@ svn_ra_serf__expect_empty_body(serf_requ
   const char *val;
 
   /* This function is just like handle_multistatus_only() except for the
-     XML parsing callbacks. We want to look for the human-readable element.  */
+     XML parsing callbacks. We want to look for the -readable element.  */
 
   /* 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
@@ -1248,11 +1129,10 @@ svn_ra_serf__expect_empty_body(serf_requ
     {
       svn_ra_serf__server_error_t *server_err;
 
-      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;
+      SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler,
+                                               FALSE,
+                                               handler->handler_pool,
+                                               handler->handler_pool));
 
       handler->server_error = server_err;
     }
@@ -1269,188 +1149,6 @@ svn_ra_serf__expect_empty_body(serf_requ
   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 *
-parse_dav_status(int *status_code_out, svn_stringbuf_t *buf,
-                 apr_pool_t *scratch_pool)
-{
-  svn_error_t *err;
-  const char *token;
-  char *tok_status;
-  svn_stringbuf_t *temp_buf = svn_stringbuf_dup(buf, scratch_pool);
-
-  svn_stringbuf_strip_whitespace(temp_buf);
-  token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status);
-  if (token)
-    token = apr_strtok(NULL, " \t\r\n", &tok_status);
-  if (!token)
-    return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                             _("Malformed DAV:status CDATA '%s'"),
-                             buf->data);
-  err = svn_cstring_atoi(status_code_out, token);
-  if (err)
-    return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
-                             _("Malformed DAV:status CDATA '%s'"),
-                             buf->data);
-
-  return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on a start element tag for a 207 response.
- */
-static svn_error_t *
-start_207(svn_ra_serf__xml_parser_t *parser,
-          svn_ra_serf__dav_props_t name,
-          const char **attrs,
-          apr_pool_t *scratch_pool)
-{
-  svn_ra_serf__server_error_t *ctx = parser->user_data;
-
-  if (!ctx->in_error &&
-      strcmp(name.namespace, "DAV:") == 0 &&
-      strcmp(name.name, "multistatus") == 0)
-    {
-      ctx->in_error = TRUE;
-    }
-  else if (ctx->in_error && strcmp(name.name, "responsedescription") == 0)
-    {
-      /* Start collecting cdata. */
-      svn_stringbuf_setempty(ctx->cdata);
-      ctx->collect_cdata = TRUE;
-    }
-  else if (ctx->in_error &&
-           strcmp(name.namespace, "DAV:") == 0 &&
-           strcmp(name.name, "status") == 0)
-    {
-      /* Start collecting cdata. */
-      svn_stringbuf_setempty(ctx->cdata);
-      ctx->collect_cdata = TRUE;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on an end element tag for a 207 response.
- */
-static svn_error_t *
-end_207(svn_ra_serf__xml_parser_t *parser,
-        svn_ra_serf__dav_props_t name,
-        apr_pool_t *scratch_pool)
-{
-  svn_ra_serf__server_error_t *ctx = parser->user_data;
-
-  if (ctx->in_error &&
-      strcmp(name.namespace, "DAV:") == 0 &&
-      strcmp(name.name, "multistatus") == 0)
-    {
-      ctx->in_error = FALSE;
-    }
-  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);
-      if (ctx->contains_precondition_error)
-        ctx->error->apr_err = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH;
-      else
-        ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
-    }
-  else if (ctx->in_error &&
-           strcmp(name.namespace, "DAV:") == 0 &&
-           strcmp(name.name, "status") == 0)
-    {
-      int status_code;
-
-      ctx->collect_cdata = FALSE;
-
-      SVN_ERR(parse_dav_status(&status_code, ctx->cdata, parser->pool));
-      if (status_code == 412)
-        ctx->contains_precondition_error = TRUE;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on CDATA elements in a 207 response.
- *
- * This callback can be called multiple times.
- */
-static svn_error_t *
-cdata_207(svn_ra_serf__xml_parser_t *parser,
-          const char *data,
-          apr_size_t len,
-          apr_pool_t *scratch_pool)
-{
-  svn_ra_serf__server_error_t *ctx = parser->user_data;
-
-  if (ctx->collect_cdata)
-    {
-      svn_stringbuf_appendbytes(ctx->cdata, data, len);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Implements svn_ra_serf__response_handler_t */
-svn_error_t *
-svn_ra_serf__handle_multistatus_only(serf_request_t *request,
-                                     serf_bucket_t *response,
-                                     void *baton,
-                                     apr_pool_t *scratch_pool)
-{
-  svn_ra_serf__handler_t *handler = baton;
-
-  /* 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);
-
-    {
-      serf_bucket_t *hdrs;
-      const char *val;
-
-      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)
-        {
-          svn_ra_serf__server_error_t *server_err;
-
-          server_err = begin_error_parsing(start_207, end_207, cdata_207,
-                                           handler->handler_pool);
-
-          /* Get the parser to set our DONE flag.  */
-          server_err->parser.done = &handler->done;
-
-          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;
-        }
-    }
-
-  /* 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;
-}
-
-
 /* Conforms to Expat's XML_StartElementHandler  */
 static void
 start_xml(void *userData, const char *raw_name, const char **attrs)
@@ -2063,10 +1761,10 @@ handle_response(serf_request_t *request,
         {
           svn_ra_serf__server_error_t *server_err;
 
-          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;
+          SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler,
+                                                   FALSE,
+                                                   handler->handler_pool,
+                                                   handler->handler_pool));
 
           handler->server_error = server_err;
         }
@@ -2116,50 +1814,12 @@ handle_response(serf_request_t *request,
      that now.  */
   if (handler->server_error != NULL)
     {
-      err = svn_ra_serf__handle_xml_parser(request, response,
-                                           &handler->server_error->parser,
-                                           scratch_pool);
-
-      /* If we do not receive an error or it is a non-transient error, return
-         immediately.
-
-         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))
-        {
-          *serf_status = err->apr_err;
-          svn_error_clear(err);
-          return SVN_NO_ERROR;
-        }
-
-      /* 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;
+      return svn_error_trace(
+                svn_ra_serf__handle_server_error(handler->server_error,
+                                                 handler,
+                                                 request, response, 
+                                                 serf_status,
+                                                 scratch_pool));
     }
 
   /* Pass the body along to the registered response handler.  */
@@ -2528,6 +2188,13 @@ svn_ra_serf__error_on_status(serf_status
       case 404:
         return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
                                  _("'%s' path not found"), path);
+      case 405:
+      case 409:
+        return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL,
+                                 _("'%s' is out of date"), path);
+      case 412:
+        return svn_error_createf(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL,
+                                 _("Precondition on '%s' failed"), path);
       case 423:
         return svn_error_createf(SVN_ERR_FS_NO_LOCK_TOKEN, NULL,
                                  _("'%s': no lock token available"), path);
@@ -2648,8 +2315,9 @@ expat_response_handler(serf_request_t *r
   else
     got_expected_status = (ectx->handler->sline.code == 200);
 
-  if ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >=
300)
-      || ! got_expected_status)
+  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

Modified: subversion/trunk/subversion/tests/cmdline/prop_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/prop_tests.py?rev=1553443&r1=1553442&r2=1553443&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/prop_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/prop_tests.py Thu Dec 26 03:50:01 2013
@@ -1633,7 +1633,6 @@ def props_over_time(sbox):
 
 # XFail the same reason revprop_change() is.
 @SkipUnless(svntest.main.server_enforces_date_syntax)
-@XFail(svntest.main.is_ra_type_dav)
 @Issue(3086)
 def invalid_propvalues(sbox):
   "test handling invalid svn:* property values"



Mime
View raw message