[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: [PATCH 3/3] atomic-revprop: Signal the error as a HTTP status code

From: Daniel Shahaf <d.s_at_daniel.shahaf.name>
Date: Tue, 21 Sep 2010 00:57:07 +0200

Jon Foster wrote on Mon, Sep 20, 2010 at 22:59:02 +0100:
> Daniel Shahaf wrote:
> > Jon Foster wrote on Mon, Sep 20, 2010 at 11:01:02 +0100:
> > > However, there is a simpler way! The <D:status> element contains
> > > the HTTP error code, usually "500 Internal Server Error". So we
> > > could pick a special HTTP status code to mean SVN_ERR_BAD_OLD_VALUE.
> > > I think of old_value_p as a precondition for the operation, a bit
> > > like "If-Modified-Since:", so I'd suggest "412 Precondition Failed".
> > > Note that generic DAV clients won't ever see this message, because
> > > they won't be sending old_value_p.
> >
> > I'll commit this once I have an ra_serf version too. Would you
> > like to write the ra_serf part of the patch, too?
>
> Sure. See attached.

> This updated patch is against the atomic-revprop branch.
>
> [[[
> Signal SVN_ERR_BAD_OLD_VALUE over DAV using a HTTP 412 status
> code inside a 207 multi-status response.
>
> * subversion/mod_dav_svn/util.c:

No trailing colon here ----------^

> (dav_svn__convert_err): Map SVN_ERR_BAD_OLD_VALUE to 412.
>
> * subversion/libsvn_ra_neon/util.c:
> (multistatus_baton_t): New member contains_precondition_error.
> (end_207_element): Check for HTTP 412 status code and create
> a SVN_ERR_BAD_OLD_VALUE error if found.
>
> * subversion/libsvn_ra_serf/ra_serf.h:
> (svn_ra_serf__server_error_t): New member
> contains_precondition_error.
>
> * subversion/libsvn_ra_serf/util.c:
> (parse_dav_status): New method.
> (svn_ra_serf__handle_discard_body): Initialise new member.
> (start_207): Parse DAV:status XML element.
> (end_207): Parse DAV:status XML element. Use SVN_ERR_BAD_OLD_VALUE
> error code if applicable.
> (svn_ra_serf__handle_multistatus_only): Initialise new member.
>
> Patch by: Jon Foster <jon.foster_at_cabot.co.uk>
> ]]]
>
> Kind regards,
>
> Jon
>

> Index: subversion/libsvn_ra_serf/util.c
> ===================================================================
> --- subversion/libsvn_ra_serf/util.c (revision 999102)
> +++ subversion/libsvn_ra_serf/util.c (working copy)
> @@ -836,6 +836,7 @@ svn_ra_serf__handle_discard_body(serf_request_t *r
> {
> 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("", pool);
> server_err->collect_cdata = FALSE;
> server_err->parser.pool = server_err->error->pool;

(So, this is the error handler for a function that normally discards its
response, and the meat of the patch is in svn_ra_serf__handle_multistatus_only().)

> @@ -945,6 +946,31 @@ svn_ra_serf__handle_status_only(serf_request_t *re
> return svn_error_return(err);
> }
>
> +/* Given a string like "HTTP/1.1 500 (status)", parse out the numeric status
> + code. Ignores leading whitespace. This function will overwrite and then
> + clear the passed buf. */

Sounds like a strange semantics. Couldn't you just take a scratch_pool
argument and duplicate the buffer, leaving the caller's copy unchanged?
Changing it in-place seems like a shoot-oneself-in-the-foot move...

> +static svn_error_t *
> +parse_dav_status(svn_stringbuf_t *buf, int *status_code_out)
> +{
> + svn_error_t * err;
> + const char * token;
> + char * tok_status = 0;

Style nit: no space after the * in the last three lines.

Also, no need to initialize TOK_STATUS (says svn_cstring_split_append()).

> +
> + svn_stringbuf_strip_whitespace(buf);
> + token = apr_strtok(buf->data, " \t\r\n", &tok_status);
> + if (token)
> + token = apr_strtok(NULL, " \t\r\n", &tok_status);
> + if (!token)
> + return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
> + "Malformed DAV:status CDATA");

Should the cdata be included in the error text? (using svn_error_createf())

> + err = svn_cstring_atoi(status_code_out, token);
> + if (err)
> + return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
> + "Malformed DAV:status CDATA");
> + svn_stringbuf_setempty(buf);
> + return SVN_NO_ERROR;
> +}
> +
> /*
> * Expat callback invoked on a start element tag for a 207 response.
> */
> @@ -968,6 +994,14 @@ start_207(svn_ra_serf__xml_parser_t *parser,
> 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);

Okay; D:responsedescription and D:status are siblings [1], so it's okay to
reuse CTX->cdata.

[1] http://paste.lisp.org/display/113346

<paranoia on>
Are you sure they will always be siblings? If we aren't sure that yes,
we could just use two stringbufs (instead of reusing ctx->cdata).

> + ctx->collect_cdata = TRUE;
> + }
>
> return SVN_NO_ERROR;
> }
> @@ -993,9 +1027,24 @@ end_207(svn_ra_serf__xml_parser_t *parser,
> ctx->collect_cdata = FALSE;
> ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data,
> ctx->cdata->len);
> - ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
> + if (ctx->contains_precondition_error)
> + ctx->error->apr_err = SVN_ERR_BAD_OLD_VALUE;
> + 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(ctx->cdata, &status_code));

Our convention is to have the output parameters first.

> + if (status_code == 412)
> + ctx->contains_precondition_error = TRUE;
> + }
> +
> return SVN_NO_ERROR;
> }
>
> @@ -1044,6 +1093,7 @@ svn_ra_serf__handle_multistatus_only(serf_request_
> {
> 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("", pool);
> server_err->collect_cdata = FALSE;
> server_err->parser.pool = server_err->error->pool;
> Index: subversion/libsvn_ra_serf/ra_serf.h
> ===================================================================
> --- subversion/libsvn_ra_serf/ra_serf.h (revision 999102)
> +++ subversion/libsvn_ra_serf/ra_serf.h (working copy)
> @@ -673,6 +673,9 @@ typedef struct svn_ra_serf__server_error_t {
> /* 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;
>
Received on 2010-09-21 00:58:06 CEST

This is an archived mail posted to the Subversion Dev mailing list.