[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 02:29:28 +0200

Jon Foster wrote on Tue, Sep 21, 2010 at 00:43:13 +0100:
> Hi,
>
> Updated patch attached.
>
> [[[
> 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
> (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.
> (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>
> ]]]
>
> > > 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->contains_precondition_error = FALSE;
> >
> > (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().)
>
> After reviewing this code again, I've dropped that chunk. I've
> convinced
> myself that the variable is never used in that code path.
>

True.

Though I'm tempted to leave it in anyway; the next person to add code to
initialize server_err might copy this initalizer...

But I speculate that the thing is allocated with apr_pCalloc() anyway,
so this whole discussion doesn't matter :-)

> > > + 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.
> >
> > <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).
>
> Even with the old code, if there are any elements with CDATA nested
> inside <D:responsedescription>, Serf is going to go wrong. If we

Ah, right.

> collect CDATA into different variables, then it'll just go wrong in
> a different way (e.g. the HTTP status line might be shown as part of
> the message).
>
> I think there's a whole package of work that could be done to make
> Serf resilient against weird XML, but I think that's separate from
> this work. (It's also quite hard to test).
>

Agreed.

> Thanks for the thourough review!

You're welcome.

Content-Description: patch_atomic_revprops_dav2.txt
> Index: subversion/libsvn_ra_serf/util.c
> ===================================================================
> --- subversion/libsvn_ra_serf/util.c (revision 999156)
> +++ subversion/libsvn_ra_serf/util.c (working copy)
> @@ -945,6 +945,34 @@ 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. */

Good docstring, but it will be slightly clearer (and extensible) if you
called out the parameter names:

  +/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric status
  + code into *STATUS_CODE_OUT. Ignores leading whitespace. */

(but yeah, I'm being lazy and dropping the "Use POOL for temporary
allocations" boilerplate)

> +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_create(buf->data, scratch_pool);

*cough* svn_stringbuf_dup()

> +
> + 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.
> */
> @@ -968,6 +996,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);
> + ctx->collect_cdata = TRUE;
> + }
>
> return SVN_NO_ERROR;
> }
> @@ -993,9 +1029,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(&status_code, ctx->cdata, parser->pool));
> + if (status_code == 412)
> + ctx->contains_precondition_error = TRUE;
> + }
> +
> return SVN_NO_ERROR;
> }
>
> @@ -1044,6 +1095,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 999156)
> +++ 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 02:30:49 CEST

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