Alien-SVN
view release on metacpan or search on metacpan
src/subversion/subversion/libsvn_ra_serf/util.c view on Meta::CPAN
is still in the error state and will just return SVN_ERR_CEASE_INVOCATION
(=the last error for the connection) again */
if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
{
apr_status_t status = serf_connection_reset(handler->conn->conn);
if (status)
err = svn_error_compose_create(err,
svn_ra_serf__wrap_err(status, NULL));
}
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.
*/
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
drain_bucket(serf_bucket_t *bucket)
{
/* Read whatever is in the bucket, and just drop it. */
while (1)
{
apr_status_t status;
const char *data;
apr_size_t len;
status = serf_bucket_read(bucket, SERF_READ_ALL_AVAIL, &data, &len);
if (status)
return status;
}
}
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 *
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_ra_serf__wrap_err(status, NULL);
return SVN_NO_ERROR;
}
apr_status_t
svn_ra_serf__response_discard_handler(serf_request_t *request,
serf_bucket_t *response,
void *baton,
apr_pool_t *pool)
{
return drain_bucket(response);
}
/* Return the value of the RESPONSE's Location header if any, or NULL
otherwise. */
static const char *
response_get_location(serf_bucket_t *response,
const char *base_url,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
serf_bucket_t *headers;
const char *location;
headers = serf_bucket_response_get_headers(response);
location = serf_bucket_headers_get(headers, "Location");
if (location == NULL)
return NULL;
/* The RFCs say we should have received a full url in LOCATION, but
older apache versions and many custom web handlers just return a
relative path here...
And we can't trust anything because it is network data.
*/
if (*location == '/')
{
apr_uri_t uri;
apr_status_t status;
status = apr_uri_parse(scratch_pool, base_url, &uri);
if (status != APR_SUCCESS)
return NULL;
/* Replace the path path with what we got */
uri.path = (char*)svn_urlpath__canonicalize(location, scratch_pool);
/* And make APR produce a proper full url for us */
location = apr_uri_unparse(scratch_pool, &uri, 0);
/* Fall through to ensure our canonicalization rules */
}
else if (!svn_path_is_url(location))
{
return NULL; /* Any other formats we should support? */
}
return svn_uri_canonicalize(location, result_pool);
}
/* Implements svn_ra_serf__response_handler_t */
svn_error_t *
svn_ra_serf__expect_empty_body(serf_request_t *request,
serf_bucket_t *response,
void *baton,
apr_pool_t *scratch_pool)
{
svn_ra_serf__handler_t *handler = baton;
serf_bucket_t *hdrs;
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. */
/* 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);
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_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
{
/* 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;
}
/* 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)
{
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;
if (!parser->state)
svn_ra_serf__xml_push_state(parser, 0);
/* ### get a real scratch_pool */
scratch_pool = parser->state->pool;
svn_ra_serf__define_ns(&parser->state->ns_list, attrs, parser->state->pool);
svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name);
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;
}
/* Conforms to Expat's XML_EndElementHandler */
static void
end_xml(void *userData, const char *raw_name)
{
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)
return;
/* ### get a real scratch_pool */
scratch_pool = parser->state->pool;
svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name);
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;
}
/* Conforms to Expat's XML_CharacterDataHandler */
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)
return;
if (!parser->state)
svn_ra_serf__xml_push_state(parser, 0);
/* ### get a real scratch_pool */
scratch_pool = parser->state->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
response is complete, adding the current "done item" to the list of
completed items. */
static void
add_done_item(svn_ra_serf__xml_parser_t *ctx)
{
/* Make sure we don't add to DONE_LIST twice. */
if (!*ctx->done)
{
*ctx->done = TRUE;
if (ctx->done_list)
{
ctx->done_item->data = ctx->user_data;
ctx->done_item->next = *ctx->done_list;
*ctx->done_list = ctx->done_item;
}
}
}
static svn_error_t *
write_to_pending(svn_ra_serf__xml_parser_t *ctx,
const char *data,
apr_size_t len,
apr_pool_t *scratch_pool)
{
if (ctx->pending == NULL)
{
ctx->pending = apr_pcalloc(ctx->pool, sizeof(*ctx->pending));
ctx->pending->buf = svn_spillbuf__create(PARSE_CHUNK_SIZE,
SPILL_SIZE,
ctx->pool);
}
/* Copy the data into one or more chunks in the spill buffer. */
return svn_error_trace(svn_spillbuf__write(ctx->pending->buf,
data, len,
scratch_pool));
}
static svn_error_t *
inject_to_parser(svn_ra_serf__xml_parser_t *ctx,
const char *data,
apr_size_t len,
const serf_status_line *sl)
{
int xml_status;
xml_status = XML_Parse(ctx->xmlp, data, (int) len, 0);
if (! ctx->ignore_errors)
{
SVN_ERR(ctx->error);
src/subversion/subversion/libsvn_ra_serf/util.c view on Meta::CPAN
/* The buffer is empty. */
pending_empty = TRUE;
break;
}
}
/* If the PENDING structures are empty *and* we consumed all content from
the network, then we're completely done with the parsing. */
if (pending_empty &&
parser->pending->network_eof)
{
int xml_status;
SVN_ERR_ASSERT(parser->xmlp != NULL);
/* Tell the parser that no more content will be parsed. */
xml_status = XML_Parse(parser->xmlp, NULL, 0, 1);
apr_pool_cleanup_run(parser->pool, &parser->xmlp, xml_parser_cleanup);
parser->xmlp = NULL;
if (! parser->ignore_errors)
{
SVN_ERR(parser->error);
if (xml_status != XML_STATUS_OK)
{
return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
_("XML parsing failed"));
}
}
add_done_item(parser);
}
*network_eof = parser->pending ? parser->pending->network_eof : FALSE;
return SVN_NO_ERROR;
}
#undef PENDING_TO_PARSE
/* ### 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_ra_serf__wrap_err(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,
serf_bucket_t *response,
void *baton,
apr_pool_t *pool)
{
serf_status_line sl;
apr_status_t status;
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_ra_serf__wrap_err(status, NULL);
}
/* Woo-hoo. Nothing here to see. */
if (sl.code == 404 && !ctx->ignore_errors)
{
err = handle_server_error(request, response, pool);
if (err && APR_STATUS_IS_EOF(err->apr_err))
add_done_item(ctx);
return svn_error_trace(err);
}
if (!ctx->xmlp)
{
ctx->xmlp = XML_ParserCreate(NULL);
apr_pool_cleanup_register(ctx->pool, &ctx->xmlp, xml_parser_cleanup,
apr_pool_cleanup_null);
XML_SetUserData(ctx->xmlp, ctx);
XML_SetElementHandler(ctx->xmlp, start_xml, end_xml);
if (ctx->cdata)
{
XML_SetCharacterDataHandler(ctx->xmlp, cdata_xml);
}
}
while (1)
{
const char *data;
apr_size_t len;
status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
if (SERF_BUCKET_READ_ERROR(status))
{
return svn_ra_serf__wrap_err(status, NULL);
}
/* Note: once the callbacks invoked by inject_to_parser() sets the
PAUSED flag, then it will not be cleared. write_to_pending() will
only save the content. Logic outside of serf_context_run() will
clear that flag, as appropriate, along with processing the
content that we have placed into the PENDING buffer.
We want to save arriving content into the PENDING structures if
the parser has been paused, or we already have data in there (so
the arriving data is appended, rather than injected out of order) */
if (ctx->paused || HAS_PENDING_DATA(ctx->pending))
{
err = write_to_pending(ctx, data, len, pool);
}
else
{
err = inject_to_parser(ctx, data, len, &sl);
if (err)
{
/* Should have no errors if IGNORE_ERRORS is set. */
SVN_ERR_ASSERT(!ctx->ignore_errors);
}
}
if (err)
{
SVN_ERR_ASSERT(ctx->xmlp != NULL);
apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup);
add_done_item(ctx);
return svn_error_trace(err);
}
if (APR_STATUS_IS_EAGAIN(status))
{
return svn_ra_serf__wrap_err(status, NULL);
}
if (APR_STATUS_IS_EOF(status))
{
if (ctx->pending != NULL)
ctx->pending->network_eof = TRUE;
/* We just hit the end of the network content. If we have nothing
in the PENDING structures, then we're completely done. */
if (!HAS_PENDING_DATA(ctx->pending))
{
int xml_status;
src/subversion/subversion/libsvn_ra_serf/util.c view on Meta::CPAN
/* Cases where a lack of a response body (via EOF) is okay:
* - A HEAD request
* - 204/304 response
*
* Otherwise, if we get an EOF here, something went really wrong: either
* the server closed on us early or we're reading too much. Either way,
* scream loudly.
*/
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_ra_serf__wrap_err(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;
}
}
/* ... and set up the header fields in HANDLER. */
handler->location = response_get_location(response,
handler->session->session_url_str,
handler->handler_pool,
scratch_pool);
/* 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 == 408
|| handler->sline.code == 409
|| handler->sline.code >= 500)
{
/* 405 Method Not allowed.
408 Request Timeout
409 Conflict: can indicate a hook error.
5xx (Internal) Server error. */
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_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 (!handler->session->pending_error)
{
apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
/* 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:
/* 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;
}
/* If we are supposed to parse the body as a server_error, then do
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.
src/subversion/subversion/libsvn_ra_serf/util.c view on Meta::CPAN
if (sline.code >= 300)
return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
_("Unexpected HTTP status %d '%s' on '%s'\n"),
sline.code, sline.reason, path);
return SVN_NO_ERROR;
}
svn_error_t *
svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *ra_session,
svn_delta_shim_callbacks_t *callbacks)
{
svn_ra_serf__session_t *session = ra_session->priv;
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);
}
/* ### TODO: sline.code < 200 should really be handled by the core */
if ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300))
{
/* 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_ra_serf__wrap_err(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? */
( run in 1.487 second using v1.01-cache-2.11-cpan-13bb782fe5a )