You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2012/06/27 17:13:42 UTC
svn commit: r1354571 [18/37] - in /subversion/branches/master-passphrase: ./
build/ build/ac-macros/ build/generator/ build/generator/templates/
build/win32/ contrib/client-side/emacs/ contrib/server-side/ notes/
notes/api-errata/1.8/ notes/directory-i...
Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c Wed Jun 27 15:12:37 2012
@@ -33,11 +33,14 @@
#include <serf.h>
#include <serf_bucket_types.h>
+#include <expat.h>
+
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_private_config.h"
#include "svn_string.h"
#include "svn_xml.h"
+#include "svn_props.h"
#include "../libsvn_ra/ra_loader.h"
#include "private/svn_dep_compat.h"
@@ -55,6 +58,17 @@
#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
@@ -79,6 +93,19 @@ struct svn_ra_serf__pending_t {
&& svn_spillbuf__get_size((p)->buf) != 0)
+struct expat_ctx_t {
+ svn_ra_serf__xml_context_t *xmlctx;
+ XML_Parser parser;
+ svn_ra_serf__handler_t *handler;
+
+ 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 },
@@ -114,6 +141,23 @@ ssl_convert_serf_failures(int failures)
return svn_failures;
}
+
+static apr_status_t
+save_error(svn_ra_serf__session_t *session,
+ svn_error_t *err)
+{
+ if (err || session->pending_error)
+ {
+ session->pending_error = svn_error_compose_create(
+ session->pending_error,
+ err);
+ return session->pending_error->apr_err;
+ }
+
+ return APR_SUCCESS;
+}
+
+
/* Construct the realmstring, e.g. https://svn.collab.net:443. */
static const char *
construct_realm(svn_ra_serf__session_t *session,
@@ -213,7 +257,7 @@ ssl_server_cert(void *baton, int failure
int i;
for (i = 0; i < san->nelts; i++) {
char *s = APR_ARRAY_IDX(san, i, char*);
- if (apr_fnmatch(s, conn->hostname,
+ if (apr_fnmatch(s, conn->session->session_url.hostname,
APR_FNM_PERIOD) == APR_SUCCESS) {
found_matching_hostname = 1;
cert_info.hostname = s;
@@ -225,7 +269,7 @@ ssl_server_cert(void *baton, int failure
/* Match server certificate CN with the hostname of the server */
if (!found_matching_hostname && cert_info.hostname)
{
- if (apr_fnmatch(cert_info.hostname, conn->hostname,
+ if (apr_fnmatch(cert_info.hostname, conn->session->session_url.hostname,
APR_FNM_PERIOD) == APR_FNM_NOMATCH)
{
svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
@@ -273,21 +317,10 @@ ssl_server_cert_cb(void *baton, int fail
svn_error_t *err;
subpool = svn_pool_create(session->pool);
- err = ssl_server_cert(baton, failures, cert, subpool);
-
+ err = svn_error_trace(ssl_server_cert(baton, failures, cert, subpool));
svn_pool_destroy(subpool);
- if (err || session->pending_error)
- {
- session->pending_error = svn_error_compose_create(
- session->pending_error,
- err);
-
- return session->pending_error->apr_err;
- }
-
- return APR_SUCCESS;
-
+ return save_error(session, err);
}
static svn_error_t *
@@ -331,7 +364,7 @@ conn_setup(apr_socket_t *sock,
*read_bkt = serf_context_bucket_socket_create(conn->session->context,
sock, conn->bkt_alloc);
- if (conn->using_ssl)
+ if (conn->session->using_ssl)
{
/* input stream */
*read_bkt = serf_bucket_ssl_decrypt_create(*read_bkt, conn->ssl_context,
@@ -341,7 +374,8 @@ conn_setup(apr_socket_t *sock,
conn->ssl_context = serf_bucket_ssl_encrypt_context_get(*read_bkt);
#if SERF_VERSION_AT_LEAST(1,0,0)
- serf_ssl_set_hostname(conn->ssl_context, conn->hostname);
+ serf_ssl_set_hostname(conn->ssl_context,
+ conn->session->session_url.hostname);
#endif
serf_ssl_client_cert_provider_set(conn->ssl_context,
@@ -391,24 +425,14 @@ svn_ra_serf__conn_setup(apr_socket_t *so
{
svn_ra_serf__connection_t *conn = baton;
svn_ra_serf__session_t *session = conn->session;
- apr_status_t status = APR_SUCCESS;
-
- svn_error_t *err = conn_setup(sock,
- read_bkt,
- write_bkt,
- baton,
- pool);
-
- if (err || session->pending_error)
- {
- session->pending_error = svn_error_compose_create(
- session->pending_error,
- err);
-
- status = session->pending_error->apr_err;
- }
+ svn_error_t *err;
- return status;
+ err = svn_error_trace(conn_setup(sock,
+ read_bkt,
+ write_bkt,
+ baton,
+ pool));
+ return save_error(session, err);
}
@@ -447,8 +471,7 @@ accept_head(serf_request_t *request,
}
static svn_error_t *
-connection_closed(serf_connection_t *conn,
- svn_ra_serf__connection_t *sc,
+connection_closed(svn_ra_serf__connection_t *conn,
apr_status_t why,
apr_pool_t *pool)
{
@@ -457,8 +480,8 @@ connection_closed(serf_connection_t *con
SVN_ERR_MALFUNCTION();
}
- if (sc->using_ssl)
- sc->ssl_context = NULL;
+ if (conn->session->using_ssl)
+ conn->ssl_context = NULL;
return SVN_NO_ERROR;
}
@@ -469,15 +492,12 @@ svn_ra_serf__conn_closed(serf_connection
apr_status_t why,
apr_pool_t *pool)
{
- svn_ra_serf__connection_t *sc = closed_baton;
+ svn_ra_serf__connection_t *ra_conn = closed_baton;
svn_error_t *err;
- err = connection_closed(conn, sc, why, pool);
+ err = svn_error_trace(connection_closed(ra_conn, why, pool));
- if (err)
- sc->session->pending_error = svn_error_compose_create(
- sc->session->pending_error,
- err);
+ (void) save_error(ra_conn->session, err);
}
@@ -528,20 +548,11 @@ apr_status_t svn_ra_serf__handle_client_
{
svn_ra_serf__connection_t *conn = data;
svn_ra_serf__session_t *session = conn->session;
- svn_error_t *err = handle_client_cert(data,
- cert_path,
- session->pool);
-
- if (err || session->pending_error)
- {
- session->pending_error = svn_error_compose_create(
- session->pending_error,
- err);
+ svn_error_t *err;
- return session->pending_error->apr_err;
- }
+ err = svn_error_trace(handle_client_cert(data, cert_path, session->pool));
- return APR_SUCCESS;
+ return save_error(session, err);
}
/* Implementation for svn_ra_serf__handle_client_cert_pw */
@@ -590,21 +601,14 @@ apr_status_t svn_ra_serf__handle_client_
{
svn_ra_serf__connection_t *conn = data;
svn_ra_serf__session_t *session = conn->session;
- svn_error_t *err = handle_client_cert_pw(data,
- cert_path,
- password,
- session->pool);
-
- if (err || session->pending_error)
- {
- session->pending_error = svn_error_compose_create(
- session->pending_error,
- err);
+ svn_error_t *err;
- return session->pending_error->apr_err;
- }
+ err = svn_error_trace(handle_client_cert_pw(data,
+ cert_path,
+ password,
+ session->pool));
- return APR_SUCCESS;
+ return save_error(session, err);
}
@@ -629,7 +633,7 @@ static svn_error_t *
setup_serf_req(serf_request_t *request,
serf_bucket_t **req_bkt,
serf_bucket_t **hdrs_bkt,
- svn_ra_serf__connection_t *conn,
+ svn_ra_serf__session_t *session,
const char *method, const char *url,
serf_bucket_t *body_bkt, const char *content_type,
apr_pool_t *request_pool,
@@ -640,7 +644,7 @@ setup_serf_req(serf_request_t *request,
#if SERF_VERSION_AT_LEAST(1, 1, 0)
svn_spillbuf_t *buf;
- if (conn->http10 && body_bkt != NULL)
+ if (session->http10 && body_bkt != NULL)
{
/* Ugh. Use HTTP/1.0 to talk to the server because we don't know if
it speaks HTTP/1.1 (and thus, chunked requests), or because the
@@ -666,7 +670,7 @@ setup_serf_req(serf_request_t *request,
/* Set the Content-Length value. This will also trigger an HTTP/1.0
request (rather than the default chunked request). */
#if SERF_VERSION_AT_LEAST(1, 1, 0)
- if (conn->http10)
+ if (session->http10)
{
if (body_bkt == NULL)
serf_bucket_request_set_CL(*req_bkt, 0);
@@ -677,10 +681,10 @@ setup_serf_req(serf_request_t *request,
*hdrs_bkt = serf_bucket_request_get_headers(*req_bkt);
- /* We use serf_bucket_headers_setn() because the string below have a
+ /* We use serf_bucket_headers_setn() because the USERAGENT has a
lifetime longer than this bucket. Thus, there is no need to copy
the header values. */
- serf_bucket_headers_setn(*hdrs_bkt, "User-Agent", conn->useragent);
+ serf_bucket_headers_setn(*hdrs_bkt, "User-Agent", session->useragent);
if (content_type)
{
@@ -756,6 +760,28 @@ svn_ra_serf__context_run_wait(svn_boolea
}
+svn_error_t *
+svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+
+ /* Create a serf request based on HANDLER. */
+ svn_ra_serf__request_create(handler);
+
+ /* Wait until the response logic marks its DONE status. */
+ err = svn_ra_serf__context_run_wait(&handler->done, handler->session,
+ scratch_pool);
+ 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.
*/
@@ -819,15 +845,10 @@ end_error(svn_ra_serf__xml_parser_t *par
/* 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. */
- apr_size_t len;
- const char *cd = ctx->cdata->data;
- if (*cd == '\n')
- ++cd;
- len = strlen(cd);
- if (len > 0 && cd[len-1] == '\n')
- --len;
+ svn_stringbuf_strip_whitespace(ctx->cdata);
- ctx->error->message = apr_pstrmemdup(ctx->error->pool, cd, len);
+ ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data,
+ ctx->cdata->len);
ctx->collect_cdata = FALSE;
}
@@ -855,69 +876,57 @@ cdata_error(svn_ra_serf__xml_parser_t *p
return SVN_NO_ERROR;
}
-/* 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;
- svn_ra_serf__server_error_t *server_err = baton;
- if (server_err)
+static apr_status_t
+drain_bucket(serf_bucket_t *bucket)
+{
+ /* Read whatever is in the bucket, and just drop it. */
+ while (1)
{
- if (!server_err->init)
- {
- serf_bucket_t *hdrs;
- const char *val;
+ apr_status_t status;
+ const char *data;
+ apr_size_t len;
- server_err->init = TRUE;
- 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)
- {
- 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_empty(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;
- }
- else
- {
- server_err->error = SVN_NO_ERROR;
- }
- }
+ status = serf_bucket_read(bucket, SERF_READ_ALL_AVAIL, &data, &len);
+ if (status)
+ return status;
+ }
+}
- if (server_err->has_xml_response)
- {
- svn_error_t *err = svn_ra_serf__handle_xml_parser(
- request,
- response,
- &server_err->parser,
- pool);
- if (server_err->done && server_err->error->apr_err == APR_SUCCESS)
- {
- svn_error_clear(server_err->error);
- server_err->error = SVN_NO_ERROR;
- }
+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;
- return svn_error_trace(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;
+}
- status = svn_ra_serf__response_discard_handler(request, response,
- NULL, pool);
+/* 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_error_wrap_apr(status, NULL);
@@ -930,71 +939,83 @@ svn_ra_serf__response_discard_handler(se
void *baton,
apr_pool_t *pool)
{
- /* Just loop through and discard the body. */
- while (1)
- {
- apr_status_t status;
- const char *data;
- apr_size_t len;
-
- status = serf_bucket_read(response, SERF_READ_ALL_AVAIL, &data, &len);
-
- if (status)
- {
- return status;
- }
-
- /* feed me */
- }
+ return drain_bucket(response);
}
-const char *
-svn_ra_serf__response_get_location(serf_bucket_t *response,
- apr_pool_t *pool)
+
+/* Return the value of the RESPONSE's Location header if any, or NULL
+ otherwise. All allocations will be made in POOL. */
+static const char *
+response_get_location(serf_bucket_t *response,
+ apr_pool_t *pool)
{
serf_bucket_t *headers;
- const char *val;
+ const char *location;
+ apr_status_t status;
+ apr_uri_t uri;
headers = serf_bucket_response_get_headers(response);
- val = serf_bucket_headers_get(headers, "Location");
- return val ? svn_urlpath__canonicalize(val, pool) : NULL;
+ location = serf_bucket_headers_get(headers, "Location");
+ if (location == NULL)
+ return NULL;
+
+ /* Ignore the scheme/host/port. Or just return as-is if we can't parse. */
+ status = apr_uri_parse(pool, location, &uri);
+ if (!status)
+ location = uri.path;
+
+ return svn_urlpath__canonicalize(location, pool);
}
+
/* Implements svn_ra_serf__response_handler_t */
svn_error_t *
-svn_ra_serf__handle_status_only(serf_request_t *request,
- serf_bucket_t *response,
- void *baton,
- apr_pool_t *pool)
+svn_ra_serf__expect_empty_body(serf_request_t *request,
+ serf_bucket_t *response,
+ void *baton,
+ apr_pool_t *scratch_pool)
{
- svn_error_t *err;
- svn_ra_serf__simple_request_context_t *ctx = baton;
+ svn_ra_serf__handler_t *handler = baton;
+ serf_bucket_t *hdrs;
+ const char *val;
- SVN_ERR_ASSERT(ctx->pool);
+ /* This function is just like handle_multistatus_only() except for the
+ XML parsing callbacks. We want to look for the human-readable element. */
- err = svn_ra_serf__handle_discard_body(request, response,
- &ctx->server_error, pool);
+ /* 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);
- if (err && APR_STATUS_IS_EOF(err->apr_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)
{
- serf_status_line sl;
- apr_status_t status;
+ svn_ra_serf__server_error_t *server_err;
- status = serf_bucket_response_status(response, &sl);
- if (SERF_BUCKET_READ_ERROR(status))
- {
- return svn_error_wrap_apr(status, NULL);
- }
+ 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;
- ctx->status = sl.code;
- ctx->reason = sl.reason ? apr_pstrdup(ctx->pool, sl.reason) : NULL;
- ctx->location = svn_ra_serf__response_get_location(response, ctx->pool);
- ctx->done = TRUE;
+ 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;
}
- return svn_error_trace(err);
+ /* 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 *
@@ -1076,6 +1097,9 @@ end_207(svn_ra_serf__xml_parser_t *parse
}
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);
@@ -1126,90 +1150,50 @@ svn_error_t *
svn_ra_serf__handle_multistatus_only(serf_request_t *request,
serf_bucket_t *response,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
- svn_error_t *err;
- svn_ra_serf__simple_request_context_t *ctx = baton;
- svn_ra_serf__server_error_t *server_err = &ctx->server_error;
+ svn_ra_serf__handler_t *handler = baton;
- SVN_ERR_ASSERT(ctx->pool);
+ /* 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);
- /* If necessary, initialize our XML parser. */
- if (server_err && !server_err->init)
{
serf_bucket_t *hdrs;
const char *val;
- server_err->init = TRUE;
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)
{
- 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_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_207;
- server_err->parser.end = end_207;
- server_err->parser.cdata = cdata_207;
- server_err->parser.done = &ctx->done;
- server_err->parser.ignore_errors = TRUE;
- }
- else
- {
- ctx->done = TRUE;
- server_err->error = SVN_NO_ERROR;
- }
- }
-
- /* If server_err->error still contains APR_SUCCESS, it means that we
- have not successfully parsed the XML yet. */
- if (server_err && server_err->error
- && server_err->error->apr_err == APR_SUCCESS)
- {
- err = svn_ra_serf__handle_xml_parser(request, response,
- &server_err->parser, pool);
-
- /* APR_EOF will be returned when parsing is complete. If we see
- any other error, return it immediately. In practice the only
- other error we expect to see is APR_EAGAIN, which indicates that
- we could not parse the XML because the contents are not yet
- available to be read. */
- if (!err || !APR_STATUS_IS_EOF(err->apr_err))
- {
- return svn_error_trace(err);
- }
- else if (ctx->done && server_err->error->apr_err == APR_SUCCESS)
- {
- svn_error_clear(server_err->error);
- server_err->error = SVN_NO_ERROR;
- }
-
- svn_error_clear(err);
- }
+ svn_ra_serf__server_error_t *server_err;
- err = svn_ra_serf__handle_discard_body(request, response, NULL, pool);
+ server_err = begin_error_parsing(start_207, end_207, cdata_207,
+ handler->handler_pool);
- if (err && APR_STATUS_IS_EOF(err->apr_err))
- {
- serf_status_line sl;
- apr_status_t status;
+ /* Get the parser to set our DONE flag. */
+ server_err->parser.done = &handler->done;
- status = serf_bucket_response_status(response, &sl);
- if (SERF_BUCKET_READ_ERROR(status))
+ handler->server_error = server_err;
+ }
+ else
{
- return svn_error_wrap_apr(status, NULL);
+ /* The body was not text/xml, so we don't know what to do with it.
+ Toss anything that arrives. */
+ handler->discard_body = TRUE;
}
-
- ctx->status = sl.code;
- ctx->reason = sl.reason ? apr_pstrdup(ctx->pool, sl.reason) : NULL;
- ctx->location = svn_ra_serf__response_get_location(response, ctx->pool);
}
- return svn_error_trace(err);
+ /* 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;
}
@@ -1220,6 +1204,7 @@ start_xml(void *userData, const char *ra
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;
@@ -1234,7 +1219,11 @@ start_xml(void *userData, const char *ra
svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name);
- parser->error = parser->start(parser, name, attrs, scratch_pool);
+ 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;
}
@@ -1244,6 +1233,7 @@ end_xml(void *userData, const char *raw_
{
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)
@@ -1254,7 +1244,11 @@ end_xml(void *userData, const char *raw_
svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name);
- parser->error = parser->end(parser, name, scratch_pool);
+ 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;
}
@@ -1263,6 +1257,7 @@ 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)
@@ -1274,7 +1269,11 @@ cdata_xml(void *userData, const char *da
/* ### get a real scratch_pool */
scratch_pool = parser->state->pool;
- parser->error = parser->cdata(parser, data, len, scratch_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
@@ -1326,7 +1325,7 @@ inject_to_parser(svn_ra_serf__xml_parser
{
int xml_status;
- xml_status = XML_Parse(ctx->xmlp, data, len, 0);
+ xml_status = XML_Parse(ctx->xmlp, data, (int) len, 0);
if (xml_status == XML_STATUS_ERROR && !ctx->ignore_errors)
{
if (sl == NULL)
@@ -1415,6 +1414,65 @@ svn_ra_serf__process_pending(svn_ra_serf
}
+/* ### 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_error_wrap_apr(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,
@@ -1427,36 +1485,22 @@ svn_ra_serf__handle_xml_parser(serf_requ
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_error_wrap_apr(status, NULL);
}
- if (ctx->status_code)
- {
- *ctx->status_code = sl.code;
- }
-
- if (sl.code == 301 || sl.code == 302 || sl.code == 307)
- {
- ctx->location = svn_ra_serf__response_get_location(response, ctx->pool);
- }
-
/* Woo-hoo. Nothing here to see. */
if (sl.code == 404 && ctx->ignore_errors == FALSE)
{
- /* If our caller won't know about the 404, abort() for now. */
- SVN_ERR_ASSERT(ctx->status_code);
+ err = handle_server_error(request, response, pool);
- add_done_item(ctx);
+ if (err && APR_STATUS_IS_EOF(err->apr_err))
+ add_done_item(ctx);
- err = svn_ra_serf__handle_server_error(request, response, pool);
-
- SVN_ERR(svn_error_compose_create(
- svn_ra_serf__handle_discard_body(request, response, NULL, pool),
- err));
- return SVN_NO_ERROR;
+ return svn_error_trace(err);
}
if (ctx->headers_baton == NULL)
@@ -1591,19 +1635,6 @@ svn_ra_serf__handle_xml_parser(serf_requ
/* not reached */
}
-/* Implements svn_ra_serf__response_handler_t */
-svn_error_t *
-svn_ra_serf__handle_server_error(serf_request_t *request,
- serf_bucket_t *response,
- apr_pool_t *pool)
-{
- svn_ra_serf__server_error_t server_err = { 0 };
-
- svn_error_clear(svn_ra_serf__handle_discard_body(request, response,
- &server_err, pool));
-
- return server_err.error;
-}
apr_status_t
svn_ra_serf__credentials_callback(char **username, char **password,
@@ -1612,8 +1643,8 @@ svn_ra_serf__credentials_callback(char *
const char *realm,
apr_pool_t *pool)
{
- svn_ra_serf__handler_t *ctx = baton;
- svn_ra_serf__session_t *session = ctx->session;
+ svn_ra_serf__handler_t *handler = baton;
+ svn_ra_serf__session_t *session = handler->session;
void *creds;
svn_auth_cred_simple_t *simple_creds;
svn_error_t *err;
@@ -1642,8 +1673,7 @@ svn_ra_serf__credentials_callback(char *
if (err)
{
- session->pending_error
- = svn_error_compose_create(session->pending_error, err);
+ (void) save_error(session, err);
return err->apr_err;
}
@@ -1652,13 +1682,11 @@ svn_ra_serf__credentials_callback(char *
if (!creds || session->auth_attempts > 4)
{
/* No more credentials. */
- session->pending_error
- = svn_error_compose_create(
- session->pending_error,
- svn_error_create(
- SVN_ERR_AUTHN_FAILED, NULL,
- _("No more credentials or we tried too many times.\n"
- "Authentication failed")));
+ (void) save_error(session,
+ svn_error_create(
+ SVN_ERR_AUTHN_FAILED, NULL,
+ _("No more credentials or we tried too many"
+ "times.\nAuthentication failed")));
return SVN_ERR_AUTHN_FAILED;
}
@@ -1676,21 +1704,20 @@ svn_ra_serf__credentials_callback(char *
if (!session->proxy_username || session->proxy_auth_attempts > 4)
{
/* No more credentials. */
- session->pending_error
- = svn_error_compose_create(
- ctx->session->pending_error,
- svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
- _("Proxy authentication failed")));
+ (void) save_error(session,
+ svn_error_create(
+ SVN_ERR_AUTHN_FAILED, NULL,
+ _("Proxy authentication failed")));
return SVN_ERR_AUTHN_FAILED;
}
}
- ctx->conn->last_status_code = code;
+ handler->conn->last_status_code = code;
return APR_SUCCESS;
}
-/* Wait for HTTP response status and headers, and invoke CTX->
+/* Wait for HTTP response status and headers, and invoke HANDLER->
response_handler() to carry out operation-specific processing.
Afterwards, check for connection close.
@@ -1700,47 +1727,83 @@ svn_ra_serf__credentials_callback(char *
static svn_error_t *
handle_response(serf_request_t *request,
serf_bucket_t *response,
- svn_ra_serf__handler_t *ctx,
+ svn_ra_serf__handler_t *handler,
apr_status_t *serf_status,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
- serf_status_line sl;
apr_status_t status;
+ svn_error_t *err;
+
+ /* ### need to verify whether this already gets init'd on every
+ ### successful exit. for an error-exit, it will (properly) be
+ ### ignored by the caller. */
+ *serf_status = APR_SUCCESS;
if (!response)
{
- /* Uh-oh. Our connection died. Requeue. */
- if (ctx->response_error)
- SVN_ERR(ctx->response_error(request, response, 0,
- ctx->response_error_baton));
-
- svn_ra_serf__request_create(ctx);
+ /* Uh-oh. Our connection died. */
+ if (handler->response_error)
+ SVN_ERR(handler->response_error(request, response, 0,
+ handler->response_error_baton));
+
+ /* Requeue another request for this handler.
+ ### how do we know if the handler can deal with this?! */
+ svn_ra_serf__request_create(handler);
- return APR_SUCCESS;
+ return SVN_NO_ERROR;
}
- status = serf_bucket_response_status(response, &sl);
- if (SERF_BUCKET_READ_ERROR(status))
- {
- *serf_status = status;
- return SVN_NO_ERROR; /* Handled by serf */
- }
- if (!sl.version && (APR_STATUS_IS_EOF(status) ||
- APR_STATUS_IS_EAGAIN(status)))
+ /* If we're reading the body, then skip all this preparation. */
+ if (handler->reading_body)
+ goto process_body;
+
+ /* Copy the Status-Line info into HANDLER, if we don't yet have it. */
+ if (handler->sline.version == 0)
{
- *serf_status = status;
- return SVN_NO_ERROR; /* Handled by serf */
+ serf_status_line sl;
+
+ status = serf_bucket_response_status(response, &sl);
+ if (status != APR_SUCCESS)
+ {
+ /* The response line is not (yet) ready, or some other error. */
+ *serf_status = status;
+ return SVN_NO_ERROR; /* Handled by serf */
+ }
+
+ /* If we got APR_SUCCESS, then we should have Status-Line info. */
+ SVN_ERR_ASSERT(sl.version != 0);
+
+ handler->sline = sl;
+ handler->sline.reason = apr_pstrdup(handler->handler_pool, sl.reason);
+
+ /* HTTP/1.1? (or later) */
+ if (sl.version != SERF_HTTP_10)
+ handler->session->http10 = FALSE;
}
+ /* Keep reading from the network until we've read all the headers. */
status = serf_bucket_response_wait_for_headers(response);
if (status)
{
+ /* The typical "error" will be APR_EAGAIN, meaning that more input
+ from the network is required to complete the reading of the
+ headers. */
if (!APR_STATUS_IS_EOF(status))
{
+ /* Either the headers are not (yet) complete, or there really
+ was an error. */
*serf_status = status;
return SVN_NO_ERROR;
}
+ /* wait_for_headers() will return EOF if there is no body in this
+ response, or if we completely read the body. The latter is not
+ true since we would have set READING_BODY to get the body read,
+ and we would not be back to this code block.
+
+ It can also return EOF if we truly hit EOF while (say) processing
+ the headers. aka Badness. */
+
/* Cases where a lack of a response body (via EOF) is okay:
* - A HEAD request
* - 204/304 response
@@ -1749,102 +1812,200 @@ handle_response(serf_request_t *request,
* the server closed on us early or we're reading too much. Either way,
* scream loudly.
*/
- if (strcmp(ctx->method, "HEAD") != 0 && sl.code != 204 && sl.code != 304)
- {
- svn_error_t *err =
- svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
- svn_error_wrap_apr(status, NULL),
- _("Premature EOF seen from server "
- "(http status=%d)"), sl.code);
- /* This discard may be no-op, but let's preserve the algorithm
- used elsewhere in this function for clarity's sake. */
- svn_ra_serf__response_discard_handler(request, response, NULL, pool);
+ 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_error_wrap_apr(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;
}
}
- if (ctx->conn->last_status_code == 401 && sl.code < 400)
- {
- SVN_ERR(svn_auth_save_credentials(ctx->session->auth_state,
- ctx->session->pool));
- ctx->session->auth_attempts = 0;
- ctx->session->auth_state = NULL;
- }
-
- ctx->conn->last_status_code = sl.code;
+ /* ... and set up the header fields in HANDLER. */
+ handler->location = response_get_location(response, handler->handler_pool);
- if (sl.code == 405 || sl.code == 409 || sl.code >= 500)
+ /* 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 == 409
+ || handler->sline.code >= 500)
{
/* 405 Method Not allowed.
409 Conflict: can indicate a hook error.
5xx (Internal) Server error. */
- SVN_ERR(svn_ra_serf__handle_server_error(request, response, pool));
+ serf_bucket_t *hdrs;
+ const char *val;
- if (!ctx->session->pending_error)
+ 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)
{
- apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
+ svn_ra_serf__server_error_t *server_err;
- /* 405 == Method Not Allowed (Occurs when trying to lock a working
- copy path which no longer exists at HEAD in the repository. */
+ 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 (sl.code == 405 && !strcmp(ctx->method, "LOCK"))
- apr_err = SVN_ERR_FS_OUT_OF_DATE;
+ if (!handler->session->pending_error)
+ {
+ apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
- return
- svn_error_createf(apr_err, NULL,
- _("%s request on '%s' failed: %d %s"),
- ctx->method, ctx->path, sl.code, sl.reason);
+ /* 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:
- return SVN_NO_ERROR; /* Error is set in caller */
+ /* 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;
}
- else
+
+ /* If we are supposed to parse the body as a server_error, then do
+ that now. */
+ if (handler->server_error != NULL)
{
- svn_error_t *err;
+ err = svn_ra_serf__handle_xml_parser(request, response,
+ &handler->server_error->parser,
+ scratch_pool);
- err = ctx->response_handler(request,response, ctx->response_baton, pool);
+ /* If we do not receive an error or it is a non-transient error, return
+ immediately.
- if (err
- && (!SERF_BUCKET_READ_ERROR(err->apr_err)
- || APR_STATUS_IS_ECONNRESET(err->apr_err)))
+ 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))
{
- /* These errors are special cased in serf
- ### We hope no handler returns these by accident. */
*serf_status = err->apr_err;
svn_error_clear(err);
return SVN_NO_ERROR;
}
- return svn_error_trace(err);
+ /* 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;
+ }
+
+ /* Pass the body along to the registered response handler. */
+ err = handler->response_handler(request, response,
+ handler->response_baton,
+ scratch_pool);
+
+ if (err
+ && (!SERF_BUCKET_READ_ERROR(err->apr_err)
+ || APR_STATUS_IS_ECONNRESET(err->apr_err)))
+ {
+ /* These errors are special cased in serf
+ ### We hope no handler returns these by accident. */
+ *serf_status = err->apr_err;
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
}
+
+ return svn_error_trace(err);
}
/* Implements serf_response_handler_t for handle_response. Storing
- errors in ctx->session->pending_error if appropriate. */
+ errors in handler->session->pending_error if appropriate. */
static apr_status_t
handle_response_cb(serf_request_t *request,
serf_bucket_t *response,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
- svn_ra_serf__handler_t *ctx = baton;
- svn_ra_serf__session_t *session = ctx->session;
+ svn_ra_serf__handler_t *handler = baton;
svn_error_t *err;
- apr_status_t serf_status = APR_SUCCESS;
+ apr_status_t inner_status;
+ apr_status_t outer_status;
- err = svn_error_trace(
- handle_response(request, response, ctx, &serf_status, pool));
-
- if (err || session->pending_error)
- {
- session->pending_error = svn_error_compose_create(session->pending_error,
- err);
+ err = svn_error_trace(handle_response(request, response,
+ handler, &inner_status,
+ scratch_pool));
+
+ /* Select the right status value to return. */
+ outer_status = save_error(handler->session, err);
+ if (!outer_status)
+ outer_status = inner_status;
+
+ /* Make sure the DONE flag is set properly. */
+ if (APR_STATUS_IS_EOF(outer_status) || APR_STATUS_IS_EOF(inner_status))
+ handler->done = TRUE;
- serf_status = session->pending_error->apr_err;
- }
-
- return serf_status;
+ return outer_status;
}
/* Perform basic request setup, with special handling for HEAD requests,
@@ -1852,34 +2013,21 @@ handle_response_cb(serf_request_t *reque
headers and body. */
static svn_error_t *
setup_request(serf_request_t *request,
- svn_ra_serf__handler_t *ctx,
+ svn_ra_serf__handler_t *handler,
serf_bucket_t **req_bkt,
- serf_response_acceptor_t *acceptor,
- void **acceptor_baton,
- serf_response_handler_t *handler,
- void **handler_baton,
apr_pool_t *request_pool,
apr_pool_t *scratch_pool)
{
serf_bucket_t *body_bkt;
serf_bucket_t *headers_bkt;
- /* Default response acceptor. */
- *acceptor = accept_response;
- *acceptor_baton = ctx->session;
-
- if (strcmp(ctx->method, "HEAD") == 0)
- {
- *acceptor = accept_head;
- }
-
- if (ctx->body_delegate)
+ if (handler->body_delegate)
{
serf_bucket_alloc_t *bkt_alloc = serf_request_get_alloc(request);
/* ### should pass the scratch_pool */
- SVN_ERR(ctx->body_delegate(&body_bkt, ctx->body_delegate_baton,
- bkt_alloc, request_pool));
+ SVN_ERR(handler->body_delegate(&body_bkt, handler->body_delegate_baton,
+ bkt_alloc, request_pool));
}
else
{
@@ -1887,20 +2035,18 @@ setup_request(serf_request_t *request,
}
SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt,
- ctx->conn, ctx->method, ctx->path,
- body_bkt, ctx->body_type,
+ handler->session, handler->method, handler->path,
+ body_bkt, handler->body_type,
request_pool, scratch_pool));
- if (ctx->header_delegate)
+ if (handler->header_delegate)
{
/* ### should pass the scratch_pool */
- SVN_ERR(ctx->header_delegate(headers_bkt, ctx->header_delegate_baton,
- request_pool));
+ SVN_ERR(handler->header_delegate(headers_bkt,
+ handler->header_delegate_baton,
+ request_pool));
}
- *handler = handle_response_cb;
- *handler_baton = ctx;
-
return APR_SUCCESS;
}
@@ -1913,44 +2059,58 @@ setup_request_cb(serf_request_t *request
serf_bucket_t **req_bkt,
serf_response_acceptor_t *acceptor,
void **acceptor_baton,
- serf_response_handler_t *handler,
- void **handler_baton,
+ serf_response_handler_t *s_handler,
+ void **s_handler_baton,
apr_pool_t *pool)
{
- svn_ra_serf__handler_t *ctx = setup_baton;
+ svn_ra_serf__handler_t *handler = setup_baton;
svn_error_t *err;
/* ### construct a scratch_pool? serf gives us a pool that will live for
### the duration of the request. */
apr_pool_t *scratch_pool = pool;
- err = setup_request(request, ctx,
- req_bkt,
- acceptor, acceptor_baton,
- handler, handler_baton,
- pool /* request_pool */, scratch_pool);
+ if (strcmp(handler->method, "HEAD") == 0)
+ *acceptor = accept_head;
+ else
+ *acceptor = accept_response;
+ *acceptor_baton = handler->session;
- if (err)
- {
- ctx->session->pending_error
- = svn_error_compose_create(ctx->session->pending_error,
- err);
+ *s_handler = handle_response_cb;
+ *s_handler_baton = handler;
- return err->apr_err;
- }
+ err = svn_error_trace(setup_request(request, handler, req_bkt,
+ pool /* request_pool */, scratch_pool));
- return APR_SUCCESS;
+ return save_error(handler->session, err);
}
void
svn_ra_serf__request_create(svn_ra_serf__handler_t *handler)
{
+ SVN_ERR_ASSERT_NO_RETURN(handler->handler_pool != NULL);
+
+ /* In case HANDLER is re-queued, reset the various transient fields.
+
+ ### prior to recent changes, HANDLER was constant. maybe we should
+ ### break out these processing fields, apart from the request
+ ### definition. */
+ handler->done = FALSE;
+ handler->server_error = NULL;
+ handler->sline.version = 0;
+ handler->location = NULL;
+ handler->reading_body = FALSE;
+ handler->discard_body = FALSE;
+
+ /* ### do we ever alter the >response_handler? */
+
/* ### do we need to hold onto the returned request object, or just
### not worry about it (the serf ctx will manage it). */
(void) serf_connection_request_create(handler->conn->conn,
setup_request_cb, handler);
}
+
svn_error_t *
svn_ra_serf__discover_vcc(const char **vcc_url,
svn_ra_serf__session_t *session,
@@ -1983,26 +2143,22 @@ svn_ra_serf__discover_vcc(const char **v
apr_hash_t *props;
svn_error_t *err;
- err = svn_ra_serf__retrieve_props(&props, session, conn,
- path, SVN_INVALID_REVNUM,
- "0", base_props, pool, pool);
+ err = svn_ra_serf__fetch_node_props(&props, conn,
+ path, SVN_INVALID_REVNUM,
+ base_props, pool, pool);
if (! err)
{
- *vcc_url =
- svn_ra_serf__get_ver_prop(props, path,
- SVN_INVALID_REVNUM,
- "DAV:",
+ apr_hash_t *ns_props;
+
+ ns_props = apr_hash_get(props, "DAV:", 4);
+ *vcc_url = svn_prop_get_value(ns_props,
"version-controlled-configuration");
- relative_path = svn_ra_serf__get_ver_prop(props, path,
- SVN_INVALID_REVNUM,
- SVN_DAV_PROP_NS_DAV,
- "baseline-relative-path");
-
- uuid = svn_ra_serf__get_ver_prop(props, path,
- SVN_INVALID_REVNUM,
- SVN_DAV_PROP_NS_DAV,
- "repository-uuid");
+ ns_props = apr_hash_get(props,
+ SVN_DAV_PROP_NS_DAV, APR_HASH_KEY_STRING);
+ relative_path = svn_prop_get_value(ns_props,
+ "baseline-relative-path");
+ uuid = svn_prop_get_value(ns_props, "repository-uuid");
break;
}
else
@@ -2162,3 +2318,187 @@ svn_ra_serf__register_editor_shim_callba
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);
+ }
+
+ /* ### should we bail on anything < 200 or >= 300 ??
+ ### actually: < 200 should really be handled by the core. */
+ if (ectx->handler->sline.code == 404)
+ {
+ /* 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_error_wrap_apr(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? */
+
+ if (APR_STATUS_IS_EOF(status))
+ {
+ /* Tell expat we've reached the end of the content. Ignore the
+ return status. We just don't care. */
+ (void) XML_Parse(ectx->parser, NULL, 0, 1 /* isFinal */);
+
+ svn_ra_serf__xml_context_destroy(ectx->xmlctx);
+ apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
+ xml_parser_cleanup);
+
+ /* ### should check XMLCTX to see if it has returned to the
+ ### INITIAL state. we may have ended early... */
+ }
+
+ if (status && !SERF_BUCKET_READ_ERROR(status))
+ {
+ return svn_error_wrap_apr(status, NULL);
+ }
+ }
+
+ /* NOTREACHED */
+}
+
+
+svn_ra_serf__handler_t *
+svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx,
+ 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->cleanup_pool = result_pool;
+
+
+ handler = apr_pcalloc(result_pool, sizeof(*handler));
+ handler->handler_pool = result_pool;
+ handler->response_handler = expat_response_handler;
+ handler->response_baton = ectx;
+
+ ectx->handler = handler;
+
+ return handler;
+}
Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/xml.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/xml.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/xml.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/xml.c Wed Jun 27 15:12:37 2012
@@ -24,9 +24,6 @@
#include <apr_uri.h>
-
-#include <expat.h>
-
#include <serf.h>
#include "svn_pools.h"
@@ -37,91 +34,182 @@
#include "svn_config.h"
#include "svn_delta.h"
#include "svn_path.h"
+
#include "svn_private_config.h"
+#include "private/svn_string_private.h"
#include "ra_serf.h"
-void
-svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list,
- const char **attrs,
- apr_pool_t *pool)
+struct svn_ra_serf__xml_context_t {
+ /* Current state information. */
+ svn_ra_serf__xml_estate_t *current;
+
+ /* If WAITING.NAMESPACE != NULL, wait for NAMESPACE:NAME element to be
+ closed before looking for transitions from CURRENT->STATE. */
+ svn_ra_serf__dav_props_t waiting;
+
+ /* The transition table. */
+ const svn_ra_serf__xml_transition_t *ttable;
+
+ /* The callback information. */
+ svn_ra_serf__xml_opened_t opened_cb;
+ svn_ra_serf__xml_closed_t closed_cb;
+ svn_ra_serf__xml_cdata_t cdata_cb;
+ void *baton;
+
+ /* Linked list of free states. */
+ svn_ra_serf__xml_estate_t *free_states;
+
+#ifdef SVN_DEBUG
+ /* Used to verify we are not re-entering a callback, specifically to
+ ensure SCRATCH_POOL is not cleared while an outer callback is
+ trying to use it. */
+ svn_boolean_t within_callback;
+#define START_CALLBACK(xmlctx) \
+ do { \
+ svn_ra_serf__xml_context_t *xmlctx__tmp = (xmlctx); \
+ SVN_ERR_ASSERT(!xmlctx__tmp->within_callback); \
+ xmlctx__tmp->within_callback = TRUE; \
+ } while (0)
+#define END_CALLBACK(xmlctx) ((xmlctx)->within_callback = FALSE)
+#else
+#define START_CALLBACK(xmlctx) /* empty */
+#define END_CALLBACK(xmlctx) /* empty */
+#endif /* SVN_DEBUG */
+
+ apr_pool_t *scratch_pool;
+
+};
+
+struct svn_ra_serf__xml_estate_t {
+ /* The current state value. */
+ int state;
+
+ /* The xml tag that opened this state. Waiting for the tag to close. */
+ svn_ra_serf__dav_props_t tag;
+
+ /* Should the CLOSED_CB function be called for custom processing when
+ this tag is closed? */
+ svn_boolean_t custom_close;
+
+ /* A pool may be constructed for this state. */
+ apr_pool_t *state_pool;
+
+ /* The namespaces extent for this state/element. This will start with
+ the parent's NS_LIST, and we will push new namespaces into our
+ local list. The parent will be unaffected by our locally-scoped data. */
+ svn_ra_serf__ns_t *ns_list;
+
+ /* Any collected attribute values. char * -> svn_string_t *. May be NULL
+ if no attributes have been collected. */
+ apr_hash_t *attrs;
+
+ /* Any collected cdata. May be NULL if no cdata is being collected. */
+ svn_stringbuf_t *cdata;
+
+ /* Previous/outer state. */
+ svn_ra_serf__xml_estate_t *prev;
+
+};
+
+
+static void
+define_namespaces(svn_ra_serf__ns_t **ns_list,
+ const char *const *attrs,
+ apr_pool_t *(*get_pool)(void *baton),
+ void *baton)
{
- const char **tmp_attrs = attrs;
+ const char *const *tmp_attrs = attrs;
- while (*tmp_attrs)
+ for (tmp_attrs = attrs; *tmp_attrs != NULL; tmp_attrs += 2)
{
if (strncmp(*tmp_attrs, "xmlns", 5) == 0)
{
- svn_ra_serf__ns_t *new_ns, *cur_ns;
- int found = 0;
+ const svn_ra_serf__ns_t *cur_ns;
+ svn_boolean_t found = FALSE;
+ const char *prefix;
+
+ /* The empty prefix, or a named-prefix. */
+ if (tmp_attrs[0][5] == ':')
+ prefix = &tmp_attrs[0][6];
+ else
+ prefix = "";
/* Have we already defined this ns previously? */
for (cur_ns = *ns_list; cur_ns; cur_ns = cur_ns->next)
{
- if (strcmp(cur_ns->namespace, tmp_attrs[0] + 6) == 0)
+ if (strcmp(cur_ns->namespace, prefix) == 0)
{
- found = 1;
+ found = TRUE;
break;
}
}
if (!found)
{
+ apr_pool_t *pool;
+ svn_ra_serf__ns_t *new_ns;
+
+ if (get_pool)
+ pool = get_pool(baton);
+ else
+ pool = baton;
new_ns = apr_palloc(pool, sizeof(*new_ns));
- new_ns->namespace = apr_pstrdup(pool, tmp_attrs[0] + 6);
+ new_ns->namespace = apr_pstrdup(pool, prefix);
new_ns->url = apr_pstrdup(pool, tmp_attrs[1]);
+ /* Push into the front of NS_LIST. Parent states will point
+ to later in the chain, so will be unaffected by
+ shadowing/other namespaces pushed onto NS_LIST. */
new_ns->next = *ns_list;
-
*ns_list = new_ns;
}
}
- tmp_attrs += 2;
}
}
+
+void
+svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list,
+ const char *const *attrs,
+ apr_pool_t *result_pool)
+{
+ define_namespaces(ns_list, attrs, NULL /* get_pool */, result_pool);
+}
+
+
/*
* Look up NAME in the NS_LIST list for previously declared namespace
* definitions and return a DAV_PROPS_T-tuple that has values.
*/
void
svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
- svn_ra_serf__ns_t *ns_list,
+ const svn_ra_serf__ns_t *ns_list,
const char *name)
{
const char *colon;
- svn_ra_serf__dav_props_t prop_name;
colon = strchr(name, ':');
if (colon)
{
- svn_ra_serf__ns_t *ns;
-
- prop_name.namespace = NULL;
+ const svn_ra_serf__ns_t *ns;
for (ns = ns_list; ns; ns = ns->next)
{
if (strncmp(ns->namespace, name, colon - name) == 0)
{
- prop_name.namespace = ns->url;
- break;
+ returned_prop_name->namespace = ns->url;
+ returned_prop_name->name = colon + 1;
+ return;
}
}
-
- SVN_ERR_ASSERT_NO_RETURN(prop_name.namespace);
-
- prop_name.name = colon + 1;
- }
- else
- {
- /* use default namespace for now */
- prop_name.namespace = "";
- prop_name.name = name;
}
- *returned_prop_name = prop_name;
- return;
+ /* If there is no prefix, or if the prefix is not found, then the
+ name is NOT within a namespace. */
+ returned_prop_name->namespace = "";
+ returned_prop_name->name = name;
}
@@ -322,3 +410,401 @@ void svn_ra_serf__xml_pop_state(svn_ra_s
cur_state->prev = parser->free_state;
parser->free_state = cur_state;
}
+
+
+/* Return a pool for XES to use for self-alloc (and other specifics). */
+static apr_pool_t *
+xes_pool(const svn_ra_serf__xml_estate_t *xes)
+{
+ /* Move up through parent states looking for one with a pool. This
+ will always terminate since the initial state has a pool. */
+ while (xes->state_pool == NULL)
+ xes = xes->prev;
+ return xes->state_pool;
+}
+
+
+static void
+ensure_pool(svn_ra_serf__xml_estate_t *xes)
+{
+ if (xes->state_pool == NULL)
+ xes->state_pool = svn_pool_create(xes_pool(xes));
+}
+
+
+/* This callback is used by define_namespaces() to wait until a pool is
+ required before constructing it. */
+static apr_pool_t *
+lazy_create_pool(void *baton)
+{
+ svn_ra_serf__xml_estate_t *xes = baton;
+
+ ensure_pool(xes);
+ return xes->state_pool;
+}
+
+void
+svn_ra_serf__xml_context_destroy(
+ svn_ra_serf__xml_context_t *xmlctx)
+{
+ svn_pool_destroy(xmlctx->scratch_pool);
+}
+
+svn_ra_serf__xml_context_t *
+svn_ra_serf__xml_context_create(
+ const svn_ra_serf__xml_transition_t *ttable,
+ svn_ra_serf__xml_opened_t opened_cb,
+ svn_ra_serf__xml_closed_t closed_cb,
+ svn_ra_serf__xml_cdata_t cdata_cb,
+ void *baton,
+ apr_pool_t *result_pool)
+{
+ svn_ra_serf__xml_context_t *xmlctx;
+ svn_ra_serf__xml_estate_t *xes;
+
+ xmlctx = apr_pcalloc(result_pool, sizeof(*xmlctx));
+ xmlctx->ttable = ttable;
+ xmlctx->opened_cb = opened_cb;
+ xmlctx->closed_cb = closed_cb;
+ xmlctx->cdata_cb = cdata_cb;
+ xmlctx->baton = baton;
+ xmlctx->scratch_pool = svn_pool_create(result_pool);
+
+ xes = apr_pcalloc(result_pool, sizeof(*xes));
+ /* XES->STATE == 0 */
+
+ /* Child states may use this pool to allocate themselves. If a child
+ needs to collect information, then it will construct a subpool and
+ will use that to allocate itself and its collected data. */
+ xes->state_pool = result_pool;
+
+ xmlctx->current = xes;
+
+ return xmlctx;
+}
+
+
+apr_hash_t *
+svn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t *xes,
+ int stop_state)
+{
+ apr_hash_t *data;
+ apr_pool_t *pool;
+
+ ensure_pool(xes);
+ pool = xes->state_pool;
+
+ data = apr_hash_make(pool);
+
+ for (; xes != NULL; xes = xes->prev)
+ {
+ if (xes->attrs != NULL)
+ {
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(pool, xes->attrs); hi;
+ hi = apr_hash_next(hi))
+ {
+ const void *key;
+ apr_ssize_t klen;
+ void *val;
+
+ /* Parent name/value lifetimes are at least as long as POOL. */
+ apr_hash_this(hi, &key, &klen, &val);
+ apr_hash_set(data, key, klen, val);
+ }
+ }
+
+ if (xes->state == stop_state)
+ break;
+ }
+
+ return data;
+}
+
+
+void
+svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes,
+ int state,
+ const char *name,
+ const char *value)
+{
+ svn_ra_serf__xml_estate_t *scan;
+
+ for (scan = xes; scan != NULL && scan->state != state; scan = scan->prev)
+ /* pass */ ;
+
+ SVN_ERR_ASSERT_NO_RETURN(scan != NULL);
+
+ /* Make sure the target state has a pool. */
+ ensure_pool(scan);
+
+ /* ... and attribute storage. */
+ if (scan->attrs == NULL)
+ scan->attrs = apr_hash_make(scan->state_pool);
+
+ /* In all likelihood, NAME is a string constant. But we can't really
+ be sure. And it isn't like we're storing a billion of these into
+ the state pool. */
+ apr_hash_set(scan->attrs,
+ apr_pstrdup(scan->state_pool, name), APR_HASH_KEY_STRING,
+ apr_pstrdup(scan->state_pool, value));
+}
+
+
+apr_pool_t *
+svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes)
+{
+ /* If they asked for a pool, then ensure that we have one to provide. */
+ ensure_pool(xes);
+
+ return xes->state_pool;
+}
+
+
+svn_error_t *
+svn_ra_serf__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;
+ const svn_ra_serf__xml_transition_t *scan;
+ apr_pool_t *new_pool;
+ svn_ra_serf__xml_estate_t *new_xes;
+
+ /* If we're waiting for an element to close, then just ignore all
+ other element-opens. */
+ if (xmlctx->waiting.namespace != NULL)
+ return SVN_NO_ERROR;
+
+ /* Look for xmlns: attributes. Lazily create the state pool if any
+ were found. */
+ define_namespaces(¤t->ns_list, attrs, lazy_create_pool, current);
+
+ svn_ra_serf__expand_ns(&elemname, current->ns_list, raw_name);
+
+ for (scan = xmlctx->ttable; scan->ns != NULL; ++scan)
+ {
+ if (scan->from_state != current->state)
+ continue;
+
+ /* Wildcard tag match. */
+ if (*scan->name == '*')
+ break;
+
+ /* Found a specific transition. */
+ if (strcmp(elemname.name, scan->name) == 0
+ && strcmp(elemname.namespace, scan->ns) == 0)
+ break;
+ }
+ if (scan->ns == NULL)
+ {
+ xmlctx->waiting = elemname;
+ /* ### return? */
+ return SVN_NO_ERROR;
+ }
+
+ /* We should not be told to collect cdata if the closed_cb will not
+ be called. */
+ SVN_ERR_ASSERT(!scan->collect_cdata || scan->custom_close);
+
+ /* Found a transition. Make it happen. */
+
+ /* ### todo. push state */
+
+ /* ### how to use free states? */
+ /* This state should be allocated in the extent pool. If we will be
+ collecting information for this state, then construct a subpool.
+
+ ### potentially optimize away the subpool if none of the
+ ### attributes are present. subpools are cheap, tho... */
+ new_pool = xes_pool(current);
+ if (scan->collect_cdata || scan->collect_attrs[0])
+ {
+ new_pool = svn_pool_create(new_pool);
+
+ /* Prep the new state. */
+ new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
+ new_xes->state_pool = new_pool;
+
+ /* If we're supposed to collect cdata, then set up a buffer for
+ this. The existence of this buffer will instruct our cdata
+ callback to collect the cdata. */
+ if (scan->collect_cdata)
+ new_xes->cdata = svn_stringbuf_create_empty(new_pool);
+
+ if (scan->collect_attrs[0] != NULL)
+ {
+ const char *const *saveattr = &scan->collect_attrs[0];
+
+ new_xes->attrs = apr_hash_make(new_pool);
+ for (; *saveattr != NULL; ++saveattr)
+ {
+ const char *name;
+ const char *value;
+
+ if (**saveattr == '?')
+ {
+ name = *saveattr + 1;
+ value = svn_xml_get_attr_value(name, attrs);
+ }
+ else
+ {
+ name = *saveattr;
+ value = svn_xml_get_attr_value(name, attrs);
+ if (value == NULL)
+ return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND,
+ NULL,
+ _("Missing XML attribute: '%s'"),
+ name);
+ }
+
+ if (value)
+ apr_hash_set(new_xes->attrs, name, APR_HASH_KEY_STRING,
+ apr_pstrdup(new_pool, value));
+ }
+ }
+ }
+ else
+ {
+ /* Prep the new state. */
+ new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
+ /* STATE_POOL remains NULL. */
+ }
+
+ /* Some basic copies to set up the new estate. */
+ new_xes->state = scan->to_state;
+ new_xes->tag.name = apr_pstrdup(new_pool, elemname.name);
+ new_xes->tag.namespace = apr_pstrdup(new_pool, elemname.namespace);
+ new_xes->custom_close = scan->custom_close;
+
+ /* Start with the parent's namespace set. */
+ new_xes->ns_list = current->ns_list;
+
+ /* The new state is prepared. Make it current. */
+ new_xes->prev = current;
+ xmlctx->current = new_xes;
+
+ if (xmlctx->opened_cb)
+ {
+ START_CALLBACK(xmlctx);
+ SVN_ERR(xmlctx->opened_cb(new_xes, xmlctx->baton,
+ new_xes->state, &new_xes->tag,
+ xmlctx->scratch_pool));
+ END_CALLBACK(xmlctx);
+ svn_pool_clear(xmlctx->scratch_pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
+ const char *raw_name)
+{
+ svn_ra_serf__xml_estate_t *xes = xmlctx->current;
+ svn_ra_serf__dav_props_t elemname;
+
+ svn_ra_serf__expand_ns(&elemname, xes->ns_list, raw_name);
+
+ if (xmlctx->waiting.namespace != NULL)
+ {
+ /* If this element is not the closer, then keep waiting... */
+ if (strcmp(elemname.name, xmlctx->waiting.name) != 0
+ || strcmp(elemname.namespace, xmlctx->waiting.namespace) != 0)
+ return SVN_NO_ERROR;
+
+ /* Found it. Stop waiting, and go back for more. */
+ xmlctx->waiting.namespace = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ /* We should be looking at the same tag that opened the current state.
+
+ Unknown elements are simply skipped, so we wouldn't reach this check.
+
+ Known elements push a new state for a given tag. Some other elemname
+ would imply closing an ancestor tag (where did ours go?) or a spurious
+ tag closure. */
+ if (strcmp(elemname.name, xes->tag.name) != 0
+ || strcmp(elemname.namespace, xes->tag.namespace) != 0)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL,
+ _("The response contains invalid XML"));
+
+ if (xes->custom_close)
+ {
+ const svn_string_t *cdata;
+
+ if (xes->cdata)
+ {
+ cdata = svn_stringbuf__morph_into_string(xes->cdata);
+#ifdef SVN_DEBUG
+ /* We might toss the pool holding this structure, but it could also
+ be within a parent pool. In any case, for safety's sake, disable
+ the stringbuf against future Badness. */
+ xes->cdata->pool = NULL;
+#endif
+ }
+ else
+ cdata = NULL;
+
+ START_CALLBACK(xmlctx);
+ SVN_ERR(xmlctx->closed_cb(xes, xmlctx->baton, xes->state,
+ cdata, xes->attrs,
+ xmlctx->scratch_pool));
+ END_CALLBACK(xmlctx);
+ svn_pool_clear(xmlctx->scratch_pool);
+ }
+
+ /* Pop the state. */
+ xmlctx->current = xes->prev;
+
+ /* ### not everything should go on the free state list. XES may go
+ ### away with the state pool. */
+ xes->prev = xmlctx->free_states;
+ xmlctx->free_states = xes;
+
+ /* If there is a STATE_POOL, then toss it. This will get rid of as much
+ memory as possible. Potentially the XES (if we didn't create a pool
+ right away, then XES may be in a parent pool). */
+ if (xes->state_pool)
+ svn_pool_destroy(xes->state_pool);
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra_serf__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. */
+ if (xmlctx->waiting.namespace != NULL)
+ return SVN_NO_ERROR;
+
+ /* If the current state is collecting cdata, then copy the cdata. */
+ if (xmlctx->current->cdata != NULL)
+ {
+ svn_stringbuf_appendbytes(xmlctx->current->cdata, data, len);
+ }
+ /* ... else if a CDATA_CB has been supplied, then invoke it for
+ all states. */
+ else if (xmlctx->cdata_cb != NULL)
+ {
+ START_CALLBACK(xmlctx);
+ SVN_ERR(xmlctx->cdata_cb(xmlctx->current,
+ xmlctx->baton,
+ xmlctx->current->state,
+ data, len,
+ xmlctx->scratch_pool));
+ END_CALLBACK(xmlctx);
+ svn_pool_clear(xmlctx->scratch_pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_svn/client.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_svn/client.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_svn/client.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_svn/client.c Wed Jun 27 15:12:37 2012
@@ -1582,7 +1582,7 @@ static svn_error_t *handle_unsupported_c
{
if (err && err->apr_err == SVN_ERR_RA_SVN_UNKNOWN_CMD)
return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err,
- msg);
+ _(msg));
return err;
}
@@ -1599,8 +1599,7 @@ static svn_error_t *ra_svn_stat(svn_ra_s
SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "stat", "c(?r)", path, rev));
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
- _("'stat' not implemented")));
-
+ N_("'stat' not implemented")));
SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "(?l)", &list));
if (! list)
@@ -1659,7 +1658,7 @@ static svn_error_t *ra_svn_get_locations
/* Servers before 1.1 don't support this command. Check for this here. */
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
- _("'get-locations' not implemented")));
+ N_("'get-locations' not implemented")));
/* Read the hash items. */
is_done = FALSE;
@@ -1713,7 +1712,8 @@ ra_svn_get_location_segments(svn_ra_sess
/* Servers before 1.5 don't support this command. Check for this here. */
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
- _("'get-location-segments' not implemented")));
+ N_("'get-location-segments'"
+ " not implemented")));
/* Parse the response. */
is_done = FALSE;
@@ -1780,7 +1780,7 @@ static svn_error_t *ra_svn_get_file_revs
/* Servers before 1.1 don't support this command. Check for this here. */
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
- _("'get-file-revs' not implemented")));
+ N_("'get-file-revs' not implemented")));
while (1)
{
@@ -1911,8 +1911,8 @@ static svn_error_t *ra_svn_lock_compat(s
/* Servers before 1.2 doesn't support locking. Check this here. */
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
- _("Server doesn't support "
- "the lock command")));
+ N_("Server doesn't support "
+ "the lock command")));
err = svn_ra_svn_read_cmd_response(conn, iterpool, "l", &list);
@@ -1975,8 +1975,8 @@ static svn_error_t *ra_svn_unlock_compat
/* Servers before 1.2 don't support locking. Check this here. */
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, iterpool),
- _("Server doesn't support the unlock "
- "command")));
+ N_("Server doesn't support the unlock "
+ "command")));
err = svn_ra_svn_read_cmd_response(conn, iterpool, "");
@@ -2262,8 +2262,8 @@ static svn_error_t *ra_svn_get_lock(svn_
/* Servers before 1.2 doesn't support locking. Check this here. */
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
- _("Server doesn't support the get-lock "
- "command")));
+ N_("Server doesn't support the get-lock "
+ "command")));
SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "(?l)", &list));
if (list)
@@ -2315,8 +2315,8 @@ static svn_error_t *ra_svn_get_locks(svn
/* Servers before 1.2 doesn't support locking. Check this here. */
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
- _("Server doesn't support the get-lock "
- "command")));
+ N_("Server doesn't support the get-lock "
+ "command")));
SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "l", &list));
@@ -2372,8 +2372,8 @@ static svn_error_t *ra_svn_replay(svn_ra
low_water_mark, send_deltas));
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
- _("Server doesn't support the replay "
- "command")));
+ N_("Server doesn't support the replay "
+ "command")));
SVN_ERR(svn_ra_svn_drive_editor2(sess->conn, pool, editor, edit_baton,
NULL, TRUE));
@@ -2403,8 +2403,8 @@ ra_svn_replay_range(svn_ra_session_t *se
low_water_mark, send_deltas));
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
- _("Server doesn't support the replay-range "
- "command")));
+ N_("Server doesn't support the "
+ "replay-range command")));
iterpool = svn_pool_create(pool);
for (rev = start_revision; rev <= end_revision; rev++)
@@ -2502,7 +2502,7 @@ ra_svn_get_deleted_rev(svn_ra_session_t
/* Servers before 1.6 don't support this command. Check for this here. */
SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
- _("'get-deleted-rev' not implemented")));
+ N_("'get-deleted-rev' not implemented")));
return svn_ra_svn_read_cmd_response(conn, pool, "r", revision_deleted);
}
Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_svn/editorp.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_svn/editorp.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_svn/editorp.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_svn/editorp.c Wed Jun 27 15:12:37 2012
@@ -844,25 +844,25 @@ static const struct {
const apr_array_header_t *params,
ra_svn_driver_state_t *ds);
} ra_svn_edit_cmds[] = {
- { "target-rev", ra_svn_handle_target_rev },
- { "open-root", ra_svn_handle_open_root },
- { "delete-entry", ra_svn_handle_delete_entry },
+ { "change-file-prop", ra_svn_handle_change_file_prop },
+ { "open-file", ra_svn_handle_open_file },
+ { "apply-textdelta", ra_svn_handle_apply_textdelta },
+ { "textdelta-chunk", ra_svn_handle_textdelta_chunk },
+ { "close-file", ra_svn_handle_close_file },
{ "add-dir", ra_svn_handle_add_dir },
{ "open-dir", ra_svn_handle_open_dir },
{ "change-dir-prop", ra_svn_handle_change_dir_prop },
+ { "delete-entry", ra_svn_handle_delete_entry },
{ "close-dir", ra_svn_handle_close_dir },
{ "absent-dir", ra_svn_handle_absent_dir },
{ "add-file", ra_svn_handle_add_file },
- { "open-file", ra_svn_handle_open_file },
- { "apply-textdelta", ra_svn_handle_apply_textdelta },
- { "textdelta-chunk", ra_svn_handle_textdelta_chunk },
{ "textdelta-end", ra_svn_handle_textdelta_end },
- { "change-file-prop", ra_svn_handle_change_file_prop },
- { "close-file", ra_svn_handle_close_file },
{ "absent-file", ra_svn_handle_absent_file },
- { "close-edit", ra_svn_handle_close_edit },
{ "abort-edit", ra_svn_handle_abort_edit },
{ "finish-replay", ra_svn_handle_finish_replay },
+ { "target-rev", ra_svn_handle_target_rev },
+ { "open-root", ra_svn_handle_open_root },
+ { "close-edit", ra_svn_handle_close_edit },
{ NULL }
};