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 [16/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/property.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/property.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/property.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/property.c Wed Jun 27 15:12:37 2012
@@ -41,28 +41,23 @@
/* Our current parsing state we're in for the PROPFIND response. */
typedef enum prop_state_e {
- NONE = 0,
+ INITIAL = 0,
+ MULTISTATUS,
RESPONSE,
+ HREF,
+ PROPSTAT,
+ STATUS,
PROP,
- PROPVAL
+ PROPVAL,
+ COLLECTION,
+ HREF_VALUE
} prop_state_e;
-typedef struct prop_info_t {
- apr_pool_t *pool;
-
- /* Current ns, attribute name, and value of the property we're parsing */
- const char *ns;
- const char *name;
- svn_stringbuf_t *value;
-
- const char *encoding;
-
-} prop_info_t;
/*
* This structure represents a pending PROPFIND response.
*/
-struct svn_ra_serf__propfind_context_t {
+typedef struct propfind_context_t {
/* pool to issue allocations from */
apr_pool_t *pool;
@@ -87,31 +82,273 @@ struct svn_ra_serf__propfind_context_t {
/* hash table that will be updated with the properties
*
- * This can be shared between multiple svn_ra_serf__propfind_context_t
+ * This can be shared between multiple propfind_context_t
* structures
*/
apr_hash_t *ret_props;
- /* If we're dealing with a Depth: 1 response,
- * we may be dealing with multiple paths.
+ /* hash table containing all the properties associated with the
+ * "current" <propstat> tag. These will get copied into RET_PROPS
+ * if the status code similarly associated indicates that they are
+ * "good"; otherwise, they'll get discarded.
*/
- const char *current_path;
-
- /* Returned status code. */
- int status_code;
-
- /* Are we done issuing the PROPFIND? */
- svn_boolean_t done;
-
- /* Context from XML stream */
- svn_ra_serf__xml_parser_t *parser_ctx;
+ apr_hash_t *ps_props;
/* If not-NULL, add us to this list when we're done. */
svn_ra_serf__list_t **done_list;
svn_ra_serf__list_t done_item;
+
+} propfind_context_t;
+
+
+#define D_ "DAV:"
+#define S_ SVN_XML_NAMESPACE
+static const svn_ra_serf__xml_transition_t propfind_ttable[] = {
+ { INITIAL, D_, "multistatus", MULTISTATUS,
+ FALSE, { NULL }, TRUE },
+
+ { MULTISTATUS, D_, "response", RESPONSE,
+ FALSE, { NULL }, FALSE },
+
+ { RESPONSE, D_, "href", HREF,
+ TRUE, { NULL }, TRUE },
+
+ { RESPONSE, D_, "propstat", PROPSTAT,
+ FALSE, { NULL }, TRUE },
+
+ { PROPSTAT, D_, "status", STATUS,
+ TRUE, { NULL }, TRUE },
+
+ { PROPSTAT, D_, "prop", PROP,
+ FALSE, { NULL }, FALSE },
+
+ { PROP, "*", "*", PROPVAL,
+ TRUE, { "?V:encoding", NULL }, TRUE },
+
+ { PROPVAL, D_, "collection", COLLECTION,
+ FALSE, { NULL }, TRUE },
+
+ { PROPVAL, D_, "href", HREF_VALUE,
+ TRUE, { NULL }, TRUE },
+
+ { 0 }
};
+
+/* Return the HTTP status code contained in STATUS_LINE, or 0 if
+ there's a problem parsing it. */
+static int parse_status_code(const char *status_line)
+{
+ /* STATUS_LINE should be of form: "HTTP/1.1 200 OK" */
+ if (status_line[0] == 'H' &&
+ status_line[1] == 'T' &&
+ status_line[2] == 'T' &&
+ status_line[3] == 'P' &&
+ status_line[4] == '/' &&
+ (status_line[5] >= '0' && status_line[5] <= '9') &&
+ status_line[6] == '.' &&
+ (status_line[7] >= '0' && status_line[7] <= '9') &&
+ status_line[8] == ' ')
+ {
+ char *reason;
+
+ return apr_strtoi64(status_line + 8, &reason, 10);
+ }
+ return 0;
+}
+
+
+/* Conforms to svn_ra_serf__path_rev_walker_t */
+static svn_error_t *
+copy_into_ret_props(void *baton,
+ const char *path, apr_ssize_t path_len,
+ const char *ns, apr_ssize_t ns_len,
+ const char *name, apr_ssize_t name_len,
+ const svn_string_t *val,
+ apr_pool_t *pool)
+{
+ propfind_context_t *ctx = baton;
+
+ svn_ra_serf__set_ver_prop(ctx->ret_props, path, ctx->rev, ns, name,
+ val, ctx->pool);
+ return SVN_NO_ERROR;
+}
+
+
+/* Conforms to svn_ra_serf__xml_opened_t */
+static svn_error_t *
+propfind_opened(svn_ra_serf__xml_estate_t *xes,
+ void *baton,
+ int entered_state,
+ const svn_ra_serf__dav_props_t *tag,
+ apr_pool_t *scratch_pool)
+{
+ propfind_context_t *ctx = baton;
+
+ if (entered_state == PROPVAL)
+ {
+ svn_ra_serf__xml_note(xes, PROPVAL, "ns", tag->namespace);
+ svn_ra_serf__xml_note(xes, PROPVAL, "name", tag->name);
+ }
+ else if (entered_state == PROPSTAT)
+ {
+ ctx->ps_props = apr_hash_make(ctx->pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Conforms to svn_ra_serf__xml_closed_t */
+static svn_error_t *
+propfind_closed(svn_ra_serf__xml_estate_t *xes,
+ void *baton,
+ int leaving_state,
+ const svn_string_t *cdata,
+ apr_hash_t *attrs,
+ apr_pool_t *scratch_pool)
+{
+ propfind_context_t *ctx = baton;
+
+ if (leaving_state == MULTISTATUS)
+ {
+ /* We've gathered all the data from the reponse. Add this item
+ onto the "done list". External callers will then know this
+ request has been completed (tho stray response bytes may still
+ arrive). */
+ if (ctx->done_list)
+ {
+ ctx->done_item.data = ctx->handler;
+ ctx->done_item.next = *ctx->done_list;
+ *ctx->done_list = &ctx->done_item;
+ }
+ }
+ else if (leaving_state == HREF)
+ {
+ const char *path;
+ const svn_string_t *val_str;
+
+ if (strcmp(ctx->depth, "1") == 0)
+ path = svn_urlpath__canonicalize(cdata->data, scratch_pool);
+ else
+ path = ctx->path;
+
+ svn_ra_serf__xml_note(xes, RESPONSE, "path", path);
+
+ /* Copy the value into the right pool, then save the HREF. */
+ val_str = svn_string_dup(cdata, ctx->pool);
+ svn_ra_serf__set_ver_prop(ctx->ret_props,
+ path, ctx->rev, D_, "href", val_str,
+ ctx->pool);
+ }
+ else if (leaving_state == COLLECTION)
+ {
+ svn_ra_serf__xml_note(xes, PROPVAL, "altvalue", "collection");
+ }
+ else if (leaving_state == HREF_VALUE)
+ {
+ svn_ra_serf__xml_note(xes, PROPVAL, "altvalue", cdata->data);
+ }
+ else if (leaving_state == STATUS)
+ {
+ /* Parse the status field, and remember if this is a property
+ that we wish to ignore. (Typically, if it's not a 200, the
+ status will be 404 to indicate that a property we
+ specifically requested from the server doesn't exist.) */
+ int status = parse_status_code(cdata->data);
+ if (status != 200)
+ svn_ra_serf__xml_note(xes, PROPSTAT, "ignore-prop", "*");
+ }
+ else if (leaving_state == PROPVAL)
+ {
+ const char *encoding = apr_hash_get(attrs, "V:encoding",
+ APR_HASH_KEY_STRING);
+ const svn_string_t *val_str;
+ apr_hash_t *gathered;
+ const char *path;
+ const char *ns;
+ const char *name;
+ const char *altvalue;
+
+ if (encoding)
+ {
+ if (strcmp(encoding, "base64") != 0)
+ return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
+ NULL,
+ _("Got unrecognized encoding '%s'"),
+ encoding);
+
+ /* Decode into the right pool. */
+ val_str = svn_base64_decode_string(cdata, ctx->pool);
+ }
+ else
+ {
+ /* Copy into the right pool. */
+ val_str = svn_string_dup(cdata, ctx->pool);
+ }
+
+ /* The current path sits on the RESPONSE state. Gather up all the
+ state from this PROPVAL to the (grandparent) RESPONSE state,
+ and grab the path from there.
+
+ Now, it would be nice if we could, at this point, know that
+ the status code for this property indicated a problem -- then
+ we could simply bail out here and ignore the property.
+ Sadly, though, we might get the status code *after* we get
+ the property value. So we'll carry on with our processing
+ here, setting the property and value as expected. Once we
+ know for sure the status code associate with the property,
+ we'll decide its fate. */
+ gathered = svn_ra_serf__xml_gather_since(xes, RESPONSE);
+
+ /* These will be dup'd into CTX->POOL, as necessary. */
+ path = apr_hash_get(gathered, "path", APR_HASH_KEY_STRING);
+ if (path == NULL)
+ path = ctx->path;
+
+ ns = apr_hash_get(attrs, "ns", APR_HASH_KEY_STRING);
+ name = apr_pstrdup(ctx->pool,
+ apr_hash_get(attrs, "name", APR_HASH_KEY_STRING));
+
+ altvalue = apr_hash_get(attrs, "altvalue", APR_HASH_KEY_STRING);
+ if (altvalue != NULL)
+ val_str = svn_string_create(altvalue, ctx->pool);
+
+ svn_ra_serf__set_ver_prop(ctx->ps_props,
+ path, ctx->rev, ns, name, val_str,
+ ctx->pool);
+ }
+ else
+ {
+ apr_hash_t *gathered;
+ const char *path;
+
+ SVN_ERR_ASSERT(leaving_state == PROPSTAT);
+
+ gathered = svn_ra_serf__xml_gather_since(xes, PROPSTAT);
+
+ path = apr_hash_get(gathered, "path", APR_HASH_KEY_STRING);
+ if (path == NULL)
+ path = ctx->path;
+
+ /* If we've squirreled away a note that says we want to ignore
+ these properties, we'll do so. Otherwise, we need to copy
+ them from the temporary hash into the ctx->ret_props hash. */
+ if (! apr_hash_get(gathered, "ignore-prop", APR_HASH_KEY_STRING))
+ {
+ SVN_ERR(svn_ra_serf__walk_all_paths(ctx->ps_props, ctx->rev,
+ copy_into_ret_props, ctx,
+ scratch_pool));
+ }
+
+ ctx->ps_props = NULL;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
const svn_string_t *
svn_ra_serf__get_ver_prop_string(apr_hash_t *props,
const char *path,
@@ -228,197 +465,15 @@ svn_ra_serf__set_prop(apr_hash_t *props,
val, pool);
}
-static prop_info_t *
-push_state(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__propfind_context_t *propfind,
- prop_state_e state)
-{
- svn_ra_serf__xml_push_state(parser, state);
-
- if (state == PROPVAL)
- {
- prop_info_t *info;
-
- info = apr_pcalloc(parser->state->pool, sizeof(*info));
- info->pool = parser->state->pool;
- info->value = svn_stringbuf_create_empty(info->pool);
-
- parser->state->private = info;
- }
-
- return parser->state->private;
-}
-
-/*
- * Expat callback invoked on a start element tag for a PROPFIND response.
- */
-static svn_error_t *
-start_propfind(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__propfind_context_t *ctx = parser->user_data;
- prop_state_e state;
- prop_info_t *info;
-
- state = parser->state->current_state;
-
- if (state == NONE && strcmp(name.name, "response") == 0)
- {
- svn_ra_serf__xml_push_state(parser, RESPONSE);
- }
- else if (state == RESPONSE && strcmp(name.name, "href") == 0)
- {
- info = push_state(parser, ctx, PROPVAL);
- info->ns = name.namespace;
- info->name = "href";
- }
- else if (state == RESPONSE && strcmp(name.name, "prop") == 0)
- {
- push_state(parser, ctx, PROP);
- }
- else if (state == PROP)
- {
- info = push_state(parser, ctx, PROPVAL);
- info->ns = name.namespace;
- info->name = apr_pstrdup(info->pool, name.name);
- info->encoding = apr_pstrdup(info->pool,
- svn_xml_get_attr_value("V:encoding", attrs));
- }
-
- return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on an end element tag for a PROPFIND response.
- */
-static svn_error_t *
-end_propfind(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__propfind_context_t *ctx = parser->user_data;
- prop_state_e state;
- prop_info_t *info;
-
- state = parser->state->current_state;
- info = parser->state->private;
-
- if (state == RESPONSE && strcmp(name.name, "response") == 0)
- {
- svn_ra_serf__xml_pop_state(parser);
- }
- else if (state == PROP && strcmp(name.name, "prop") == 0)
- {
- svn_ra_serf__xml_pop_state(parser);
- }
- else if (state == PROPVAL)
- {
- const char *ns;
- const char *pname;
- const svn_string_t *val_str = NULL;
-
- /* if we didn't see a CDATA element, we may want the tag name
- * as long as it isn't equivalent to the property name.
- */
- /* ### gstein sez: I have no idea what this is about. */
- if (*info->value->data == '\0')
- {
- if (strcmp(info->name, name.name) != 0)
- val_str = svn_string_create(name.name, ctx->pool);
- else
- val_str = svn_string_create_empty(ctx->pool);
- }
-
- if (parser->state->prev->current_state == RESPONSE &&
- strcmp(name.name, "href") == 0)
- {
- if (strcmp(ctx->depth, "1") == 0)
- {
- ctx->current_path =
- svn_urlpath__canonicalize(info->value->data, ctx->pool);
- }
- else
- {
- ctx->current_path = ctx->path;
- }
- }
- else if (info->encoding)
- {
- if (strcmp(info->encoding, "base64") == 0)
- {
- const svn_string_t *morph;
-
- morph = svn_stringbuf__morph_into_string(info->value);
-#ifdef SVN_DEBUG
- info->value = NULL; /* morph killed the stringbuf. */
-#endif
- val_str = svn_base64_decode_string(morph, ctx->pool);
- }
- else
- {
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
- NULL,
- _("Got unrecognized encoding '%s'"),
- info->encoding);
- }
- }
-
- /* ### there may be better logic to ensure this is set above, but just
- ### going for the easy win here. */
- if (val_str == NULL)
- val_str = svn_string_create_from_buf(info->value, ctx->pool);
-
- ns = apr_pstrdup(ctx->pool, info->ns);
- pname = apr_pstrdup(ctx->pool, info->name);
-
- /* set the return props and update our cache too. */
- svn_ra_serf__set_ver_prop(ctx->ret_props,
- ctx->current_path, ctx->rev,
- ns, pname, val_str,
- ctx->pool);
-
- svn_ra_serf__xml_pop_state(parser);
- }
-
- return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on CDATA elements in a PROPFIND response.
- *
- * This callback can be called multiple times.
- */
-static svn_error_t *
-cdata_propfind(svn_ra_serf__xml_parser_t *parser,
- const char *data,
- apr_size_t len,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__propfind_context_t *ctx = parser->user_data;
- prop_state_e state;
- prop_info_t *info;
-
- UNUSED_CTX(ctx);
-
- state = parser->state->current_state;
- info = parser->state->private;
-
- if (state == PROPVAL)
- svn_stringbuf_appendbytes(info->value, data, len);
-
- return SVN_NO_ERROR;
-}
static svn_error_t *
setup_propfind_headers(serf_bucket_t *headers,
void *setup_baton,
apr_pool_t *pool)
{
- svn_ra_serf__propfind_context_t *ctx = setup_baton;
+ propfind_context_t *ctx = setup_baton;
- if (ctx->conn->using_compression)
+ if (ctx->sess->using_compression)
{
serf_bucket_headers_setn(headers, "Accept-Encoding", "gzip");
}
@@ -440,7 +495,7 @@ create_propfind_body(serf_bucket_t **bkt
serf_bucket_alloc_t *alloc,
apr_pool_t *pool)
{
- svn_ra_serf__propfind_context_t *ctx = setup_baton;
+ propfind_context_t *ctx = setup_baton;
serf_bucket_t *body_bkt, *tmp;
const svn_ra_serf__dav_props_t *prop;
@@ -513,7 +568,7 @@ create_propfind_body(serf_bucket_t **bkt
svn_error_t *
-svn_ra_serf__deliver_props(svn_ra_serf__propfind_context_t **prop_ctx,
+svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler,
apr_hash_t *ret_props,
svn_ra_serf__session_t *sess,
svn_ra_serf__connection_t *conn,
@@ -524,9 +579,9 @@ svn_ra_serf__deliver_props(svn_ra_serf__
svn_ra_serf__list_t **done_list,
apr_pool_t *pool)
{
- svn_ra_serf__propfind_context_t *new_prop_ctx;
+ propfind_context_t *new_prop_ctx;
svn_ra_serf__handler_t *handler;
- svn_ra_serf__xml_parser_t *parser_ctx;
+ svn_ra_serf__xml_context_t *xmlctx;
new_prop_ctx = apr_pcalloc(pool, sizeof(*new_prop_ctx));
@@ -535,7 +590,6 @@ svn_ra_serf__deliver_props(svn_ra_serf__
new_prop_ctx->find_props = find_props;
new_prop_ctx->ret_props = ret_props;
new_prop_ctx->depth = depth;
- new_prop_ctx->done = FALSE;
new_prop_ctx->sess = sess;
new_prop_ctx->conn = conn;
new_prop_ctx->rev = rev;
@@ -550,7 +604,13 @@ svn_ra_serf__deliver_props(svn_ra_serf__
new_prop_ctx->label = NULL;
}
- handler = apr_pcalloc(pool, sizeof(*handler));
+ xmlctx = svn_ra_serf__xml_context_create(propfind_ttable,
+ propfind_opened,
+ propfind_closed,
+ NULL,
+ new_prop_ctx,
+ pool);
+ handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
handler->method = "PROPFIND";
handler->path = path;
@@ -565,57 +625,28 @@ svn_ra_serf__deliver_props(svn_ra_serf__
new_prop_ctx->handler = handler;
- parser_ctx = apr_pcalloc(pool, sizeof(*new_prop_ctx->parser_ctx));
- parser_ctx->pool = pool;
- parser_ctx->user_data = new_prop_ctx;
- parser_ctx->start = start_propfind;
- parser_ctx->end = end_propfind;
- parser_ctx->cdata = cdata_propfind;
- parser_ctx->status_code = &new_prop_ctx->status_code;
- parser_ctx->done = &new_prop_ctx->done;
- parser_ctx->done_list = new_prop_ctx->done_list;
- parser_ctx->done_item = &new_prop_ctx->done_item;
-
- new_prop_ctx->parser_ctx = parser_ctx;
-
- handler->response_handler = svn_ra_serf__handle_xml_parser;
- handler->response_baton = parser_ctx;
-
- /* create request */
- svn_ra_serf__request_create(new_prop_ctx->handler);
-
- *prop_ctx = new_prop_ctx;
+ *propfind_handler = handler;
return SVN_NO_ERROR;
}
-svn_boolean_t
-svn_ra_serf__propfind_is_done(svn_ra_serf__propfind_context_t *ctx)
-{
- return ctx->done;
-}
-
-int
-svn_ra_serf__propfind_status_code(svn_ra_serf__propfind_context_t *ctx)
-{
- return ctx->status_code;
-}
/*
* This helper function will block until the PROP_CTX indicates that is done
* or another error is returned.
*/
svn_error_t *
-svn_ra_serf__wait_for_props(svn_ra_serf__propfind_context_t *prop_ctx,
- svn_ra_serf__session_t *sess,
- apr_pool_t *pool)
+svn_ra_serf__wait_for_props(svn_ra_serf__handler_t *handler,
+ apr_pool_t *scratch_pool)
{
- svn_error_t *err, *err2;
+ svn_error_t *err;
+ svn_error_t *err2;
- err = svn_ra_serf__context_run_wait(&prop_ctx->done, sess, pool);
+ err = svn_ra_serf__context_run_one(handler, scratch_pool);
- err2 = svn_ra_serf__error_on_status(prop_ctx->status_code,
- prop_ctx->path, NULL);
+ err2 = svn_ra_serf__error_on_status(handler->sline.code,
+ handler->path,
+ NULL);
if (err2)
{
svn_error_clear(err);
@@ -639,45 +670,63 @@ svn_ra_serf__retrieve_props(apr_hash_t *
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- svn_ra_serf__propfind_context_t *prop_ctx;
+ svn_ra_serf__handler_t *handler;
*results = apr_hash_make(result_pool);
- SVN_ERR(svn_ra_serf__deliver_props(&prop_ctx, *results, sess, conn, url,
+ SVN_ERR(svn_ra_serf__deliver_props(&handler, *results, sess, conn, url,
rev, depth, props, NULL, result_pool));
- SVN_ERR(svn_ra_serf__wait_for_props(prop_ctx, sess, result_pool));
+ SVN_ERR(svn_ra_serf__wait_for_props(handler, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
-svn_ra_serf__walk_all_props(apr_hash_t *props,
- const char *name,
- svn_revnum_t rev,
- svn_ra_serf__walker_visitor_t walker,
- void *baton,
- apr_pool_t *scratch_pool)
+svn_ra_serf__fetch_node_props(apr_hash_t **results,
+ svn_ra_serf__connection_t *conn,
+ const char *url,
+ svn_revnum_t revision,
+ const svn_ra_serf__dav_props_t *which_props,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- apr_hash_index_t *ns_hi;
+ apr_hash_t *multiprops;
apr_hash_t *ver_props;
- apr_hash_t *path_props;
- apr_pool_t *iterpool;
- ver_props = apr_hash_get(props, &rev, sizeof(rev));
- if (!ver_props)
- {
- return SVN_NO_ERROR;
- }
+ /* Note: a couple extra hash tables and whatnot get into RESULT_POOL.
+ Not a big deal at this point. Theoretically, we could fetch all
+ props into SCRATCH_POOL, then copy just the REVISION/URL props
+ into RESULT_POOL. Too much work for too little gain... */
+ SVN_ERR(svn_ra_serf__retrieve_props(&multiprops, conn->session, conn,
+ url, revision, "0", which_props,
+ result_pool, scratch_pool));
+
+ ver_props = apr_hash_get(multiprops, &revision, sizeof(revision));
+ if (ver_props != NULL)
+ {
+ *results = apr_hash_get(ver_props, url, APR_HASH_KEY_STRING);
+ if (*results != NULL)
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
+ _("The PROPFIND response did not include "
+ "the requested properties"));
+}
- path_props = apr_hash_get(ver_props, name, APR_HASH_KEY_STRING);
- if (!path_props)
- {
- return SVN_NO_ERROR;
- }
+
+svn_error_t *
+svn_ra_serf__walk_node_props(apr_hash_t *props,
+ svn_ra_serf__walker_visitor_t walker,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool;
+ apr_hash_index_t *ns_hi;
iterpool = svn_pool_create(scratch_pool);
- for (ns_hi = apr_hash_first(scratch_pool, path_props); ns_hi;
+ for (ns_hi = apr_hash_first(scratch_pool, props); ns_hi;
ns_hi = apr_hash_next(ns_hi))
{
void *ns_val;
@@ -711,6 +760,31 @@ svn_ra_serf__walk_all_props(apr_hash_t *
svn_error_t *
+svn_ra_serf__walk_all_props(apr_hash_t *props,
+ const char *name,
+ svn_revnum_t rev,
+ svn_ra_serf__walker_visitor_t walker,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *ver_props;
+ apr_hash_t *path_props;
+
+ ver_props = apr_hash_get(props, &rev, sizeof(rev));
+ if (!ver_props)
+ return SVN_NO_ERROR;
+
+ path_props = apr_hash_get(ver_props, name, APR_HASH_KEY_STRING);
+ if (!path_props)
+ return SVN_NO_ERROR;
+
+ return svn_error_trace(svn_ra_serf__walk_node_props(path_props,
+ walker, baton,
+ scratch_pool));
+}
+
+
+svn_error_t *
svn_ra_serf__walk_all_paths(apr_hash_t *props,
svn_revnum_t rev,
svn_ra_serf__path_rev_walker_t walker,
@@ -832,15 +906,13 @@ set_flat_props(void *baton,
svn_error_t *
svn_ra_serf__flatten_props(apr_hash_t **flat_props,
apr_hash_t *props,
- const char *path,
- svn_revnum_t revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
*flat_props = apr_hash_make(result_pool);
- return svn_error_trace(svn_ra_serf__walk_all_props(
- props, path, revision,
+ return svn_error_trace(svn_ra_serf__walk_node_props(
+ props,
set_flat_props,
*flat_props /* baton */,
scratch_pool));
@@ -899,7 +971,7 @@ svn_ra_serf__select_revprops(apr_hash_t
/*
- * Contact the server (using SESSION and CONN) to calculate baseline
+ * Contact the server (using CONN) to calculate baseline
* information for BASELINE_URL at REVISION (which may be
* SVN_INVALID_REVNUM to query the HEAD revision).
*
@@ -911,209 +983,295 @@ svn_ra_serf__select_revprops(apr_hash_t
static svn_error_t *
retrieve_baseline_info(svn_revnum_t *actual_revision,
const char **basecoll_url_p,
- svn_ra_serf__session_t *session,
svn_ra_serf__connection_t *conn,
const char *baseline_url,
svn_revnum_t revision,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
apr_hash_t *props;
+ apr_hash_t *dav_props;
const char *basecoll_url;
- const char *version_name;
-
- SVN_ERR(svn_ra_serf__retrieve_props(&props, session, conn,
- baseline_url, revision, "0",
- baseline_props,
- pool, pool));
- basecoll_url = svn_ra_serf__get_ver_prop(props, baseline_url, revision,
- "DAV:", "baseline-collection");
+ SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn,
+ baseline_url, revision,
+ baseline_props,
+ scratch_pool, scratch_pool));
+ dav_props = apr_hash_get(props, "DAV:", 4);
+ /* If DAV_PROPS is NULL, then svn_prop_get_value() will return NULL. */
+ basecoll_url = svn_prop_get_value(dav_props, "baseline-collection");
if (!basecoll_url)
{
return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
_("The PROPFIND response did not include "
"the requested baseline-collection value"));
}
-
- *basecoll_url_p = svn_urlpath__canonicalize(basecoll_url, pool);
-
- version_name = svn_ra_serf__get_ver_prop(props, baseline_url, revision,
- "DAV:", SVN_DAV__VERSION_NAME);
- if (!version_name)
- {
- return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
- _("The PROPFIND response did not include "
- "the requested version-name value"));
- }
+ *basecoll_url_p = svn_urlpath__canonicalize(basecoll_url, result_pool);
if (actual_revision)
{
+ const char *version_name;
+
+ version_name = svn_prop_get_value(dav_props, SVN_DAV__VERSION_NAME);
+ if (!version_name)
+ return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
+ _("The PROPFIND response did not include "
+ "the requested version-name value"));
+
*actual_revision = SVN_STR_TO_REV(version_name);
}
return SVN_NO_ERROR;
}
+
+/* For HTTPv1 servers, do a PROPFIND dance on the VCC to fetch the youngest
+ revnum. If BASECOLL_URL is non-NULL, then the corresponding baseline
+ collection URL is also returned.
+
+ Do the work over CONN.
+
+ *BASECOLL_URL (if requested) will be allocated in RESULT_POOL. All
+ temporary allocations will be made in SCRATCH_POOL. */
+static svn_error_t *
+v1_get_youngest_revnum(svn_revnum_t *youngest,
+ const char **basecoll_url,
+ svn_ra_serf__connection_t *conn,
+ const char *vcc_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *baseline_url;
+ const char *bc_url;
+
+ /* Fetching DAV:checked-in from the VCC (with no Label: to specify a
+ revision) will return the latest Baseline resource's URL. */
+ SVN_ERR(svn_ra_serf__fetch_dav_prop(&baseline_url, conn, vcc_url,
+ SVN_INVALID_REVNUM,
+ "checked-in",
+ scratch_pool, scratch_pool));
+ if (!baseline_url)
+ {
+ return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
+ _("The OPTIONS response did not include "
+ "the requested checked-in value"));
+ }
+ baseline_url = svn_urlpath__canonicalize(baseline_url, scratch_pool);
+
+ /* From the Baseline resource, we can fetch the DAV:baseline-collection
+ and DAV:version-name properties. The latter is the revision number,
+ which is formally the name used in Label: headers. */
+
+ /* First check baseline information cache. */
+ SVN_ERR(svn_ra_serf__blncache_get_baseline_info(&bc_url,
+ youngest,
+ conn->session->blncache,
+ baseline_url,
+ scratch_pool));
+ if (!bc_url)
+ {
+ SVN_ERR(retrieve_baseline_info(youngest, &bc_url, conn,
+ baseline_url, SVN_INVALID_REVNUM,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_ra_serf__blncache_set(conn->session->blncache,
+ baseline_url, *youngest,
+ bc_url, scratch_pool));
+ }
+
+ if (basecoll_url != NULL)
+ *basecoll_url = apr_pstrdup(result_pool, bc_url);
+
+ return SVN_NO_ERROR;
+}
+
+
svn_error_t *
-svn_ra_serf__get_baseline_info(const char **bc_url,
- const char **bc_relative,
- svn_ra_serf__session_t *session,
- svn_ra_serf__connection_t *conn,
- const char *url,
- svn_revnum_t revision,
- svn_revnum_t *latest_revnum,
- apr_pool_t *pool)
+svn_ra_serf__get_youngest_revnum(svn_revnum_t *youngest,
+ svn_ra_serf__session_t *session,
+ apr_pool_t *scratch_pool)
{
- const char *vcc_url, *relative_url, *basecoll_url, *baseline_url;
+ const char *vcc_url;
- /* No URL? No sweat. We'll use the session URL. */
- if (! url)
- url = session->session_url.path;
+ if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
+ return svn_error_trace(svn_ra_serf__v2_get_youngest_revnum(
+ youngest, session->conns[0], scratch_pool));
- /* If the caller didn't provide a specific connection for us to use,
- we'll use the default one. */
- if (! conn)
- conn = session->conns[0];
+ SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, scratch_pool));
+
+ return svn_error_trace(v1_get_youngest_revnum(youngest, NULL,
+ session->conns[0], vcc_url,
+ scratch_pool, scratch_pool));
+}
+
+/* Set *BC_URL to the baseline collection url for REVISION. If REVISION
+ is SVN_INVALID_REVNUM, then the youngest revnum ("HEAD") is used.
+
+ *REVNUM_USED will be set to the revision used.
+
+ Uses the specified CONN, which is part of SESSION.
+
+ All allocations (results and temporary) are performed in POOL. */
+static svn_error_t *
+get_baseline_info(const char **bc_url,
+ svn_revnum_t *revnum_used,
+ svn_ra_serf__session_t *session,
+ svn_ra_serf__connection_t *conn,
+ svn_revnum_t revision,
+ apr_pool_t *pool)
+{
/* If we detected HTTP v2 support on the server, we can construct
the baseline collection URL ourselves, and fetch the latest
revision (if needed) with an OPTIONS request. */
if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
{
- svn_revnum_t actual_revision;
-
if (SVN_IS_VALID_REVNUM(revision))
{
- actual_revision = revision;
+ *revnum_used = revision;
}
else
{
- svn_ra_serf__options_context_t *opt_ctx;
-
- SVN_ERR(svn_ra_serf__create_options_req(&opt_ctx, session, conn,
- session->session_url.path,
- pool));
- SVN_ERR(svn_ra_serf__context_run_wait(
- svn_ra_serf__get_options_done_ptr(opt_ctx), session, pool));
-
- actual_revision = svn_ra_serf__options_get_youngest_rev(opt_ctx);
- if (! SVN_IS_VALID_REVNUM(actual_revision))
+ SVN_ERR(svn_ra_serf__v2_get_youngest_revnum(
+ revnum_used, conn, pool));
+ if (! SVN_IS_VALID_REVNUM(*revnum_used))
return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
_("The OPTIONS response did not include "
"the youngest revision"));
}
- basecoll_url = apr_psprintf(pool, "%s/%ld",
- session->rev_root_stub, actual_revision);
- if (latest_revnum)
- *latest_revnum = actual_revision;
+ *bc_url = apr_psprintf(pool, "%s/%ld",
+ session->rev_root_stub, *revnum_used);
}
/* Otherwise, we fall back to the old VCC_URL PROPFIND hunt. */
else
{
+ const char *vcc_url;
+
SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, conn, pool));
if (SVN_IS_VALID_REVNUM(revision))
{
/* First check baseline information cache. */
- SVN_ERR(svn_ra_serf__blncache_get_bc_url(&basecoll_url,
+ SVN_ERR(svn_ra_serf__blncache_get_bc_url(bc_url,
session->blncache,
revision, pool));
-
- if (!basecoll_url)
+ if (!*bc_url)
{
- SVN_ERR(retrieve_baseline_info(NULL, &basecoll_url, session,
- conn, vcc_url, revision, pool));
+ SVN_ERR(retrieve_baseline_info(NULL, bc_url, conn,
+ vcc_url, revision, pool, pool));
SVN_ERR(svn_ra_serf__blncache_set(session->blncache, NULL,
- revision, basecoll_url, pool));
+ revision, *bc_url, pool));
}
- if (latest_revnum)
- {
- *latest_revnum = revision;
- }
+ *revnum_used = revision;
}
else
{
- apr_hash_t *props;
- svn_revnum_t actual_revision;
+ SVN_ERR(v1_get_youngest_revnum(revnum_used, bc_url,
+ conn, vcc_url,
+ pool, pool));
+ }
+ }
- SVN_ERR(svn_ra_serf__retrieve_props(&props, session, conn,
- vcc_url, revision, "0",
- checked_in_props,
- pool, pool));
- baseline_url = svn_ra_serf__get_ver_prop(props, vcc_url, revision,
- "DAV:", "checked-in");
- if (!baseline_url)
- {
- return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
- _("The OPTIONS response did not include "
- "the requested checked-in value"));
- }
+ return SVN_NO_ERROR;
+}
- baseline_url = svn_urlpath__canonicalize(baseline_url, pool);
- /* First check baseline information cache. */
- SVN_ERR(svn_ra_serf__blncache_get_baseline_info(&basecoll_url,
- &actual_revision,
- session->blncache,
- baseline_url,
- pool));
- if (!basecoll_url)
- {
- SVN_ERR(retrieve_baseline_info(&actual_revision, &basecoll_url,
- session, conn,
- baseline_url, revision, pool));
- SVN_ERR(svn_ra_serf__blncache_set(session->blncache,
- baseline_url, actual_revision,
- basecoll_url, pool));
- }
+svn_error_t *
+svn_ra_serf__get_stable_url(const char **stable_url,
+ svn_revnum_t *latest_revnum,
+ svn_ra_serf__session_t *session,
+ svn_ra_serf__connection_t *conn,
+ const char *url,
+ svn_revnum_t revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *basecoll_url;
+ const char *repos_relpath;
+ svn_revnum_t revnum_used;
- if (latest_revnum)
- {
- *latest_revnum = actual_revision;
- }
- }
- }
+ /* No URL? No sweat. We'll use the session URL. */
+ if (! url)
+ url = session->session_url.path;
- /* And let's not forget to calculate our relative path. */
- SVN_ERR(svn_ra_serf__get_relative_path(&relative_url, url, session,
- conn, pool));
+ /* If the caller didn't provide a specific connection for us to use,
+ we'll use the default connection. */
+ if (! conn)
+ conn = session->conns[0];
+
+ SVN_ERR(get_baseline_info(&basecoll_url, &revnum_used,
+ session, conn, revision, scratch_pool));
+ SVN_ERR(svn_ra_serf__get_relative_path(&repos_relpath, url,
+ session, conn, scratch_pool));
+
+ *stable_url = svn_path_url_add_component2(basecoll_url, repos_relpath,
+ result_pool);
+ if (latest_revnum)
+ *latest_revnum = revnum_used;
- *bc_url = basecoll_url;
- *bc_relative = relative_url;
return SVN_NO_ERROR;
}
svn_error_t *
-svn_ra_serf__get_resource_type(svn_node_kind_t *kind,
- apr_hash_t *props,
- const char *url,
- svn_revnum_t revision)
+svn_ra_serf__get_resource_type(svn_kind_t *kind,
+ apr_hash_t *props)
{
+ apr_hash_t *dav_props;
const char *res_type;
- res_type = svn_ra_serf__get_ver_prop(props, url, revision,
- "DAV:", "resourcetype");
+ dav_props = apr_hash_get(props, "DAV:", 4);
+ res_type = svn_prop_get_value(dav_props, "resourcetype");
if (!res_type)
{
/* How did this happen? */
return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
_("The PROPFIND response did not include the "
- "requested resourcetype value"));
+ "requested resourcetype value"));
}
if (strcmp(res_type, "collection") == 0)
{
- *kind = svn_node_dir;
+ *kind = svn_kind_dir;
}
else
{
- *kind = svn_node_file;
+ *kind = svn_kind_file;
}
return SVN_NO_ERROR;
}
+
+
+svn_error_t *
+svn_ra_serf__fetch_dav_prop(const char **value,
+ svn_ra_serf__connection_t *conn,
+ const char *url,
+ svn_revnum_t revision,
+ const char *propname,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *props;
+ apr_hash_t *dav_props;
+
+ SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn, url, revision,
+ checked_in_props,
+ scratch_pool, scratch_pool));
+ dav_props = apr_hash_get(props, "DAV:", 4);
+ if (dav_props == NULL)
+ return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
+ _("The PROPFIND response did not include "
+ "the requested 'DAV:' properties"));
+
+ /* We wouldn't get here if the resource was not found (404), so the
+ property should be present.
+
+ Note: it is okay to call apr_pstrdup() with NULL. */
+ *value = apr_pstrdup(result_pool, svn_prop_get_value(dav_props, propname));
+
+ return SVN_NO_ERROR;
+}
Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h Wed Jun 27 15:12:37 2012
@@ -26,7 +26,7 @@
#include <serf.h>
-#include <expat.h>
+#include <expat.h> /* for XML_Parser */
#include <apr_uri.h>
#include "svn_types.h"
@@ -71,22 +71,11 @@ typedef struct svn_ra_serf__connection_t
/* Our connection to a server. */
serf_connection_t *conn;
- /* The server is not Apache/mod_dav_svn (directly) and only supports
- HTTP/1.0. Thus, we cannot send chunked requests. */
- svn_boolean_t http10;
-
/* Bucket allocator for this connection. */
serf_bucket_alloc_t *bkt_alloc;
- /* Host name */
- const char *hostname;
-
- /* Are we using ssl */
- svn_boolean_t using_ssl;
- int server_cert_failures; /* Collected cert failures in chain */
-
- /* Should we ask for compressed responses? */
- svn_boolean_t using_compression;
+ /* Collected cert failures in chain. */
+ int server_cert_failures;
/* What was the last HTTP status code we got on this connection? */
int last_status_code;
@@ -98,11 +87,11 @@ typedef struct svn_ra_serf__connection_t
svn_ra_serf__session_t *session;
- /* user agent string */
- const char *useragent;
-
} svn_ra_serf__connection_t;
+/** Max. number of connctions we'll open to the server. */
+#define MAX_NR_OF_CONNS 4
+
/*
* The master serf RA session.
*
@@ -121,8 +110,11 @@ struct svn_ra_serf__session_t {
/* Should we ask for compressed responses? */
svn_boolean_t using_compression;
+ /* The user agent string */
+ const char *useragent;
+
/* The current connection */
- svn_ra_serf__connection_t **conns;
+ svn_ra_serf__connection_t *conns[MAX_NR_OF_CONNS];
int num_conns;
int cur_conn;
@@ -134,6 +126,10 @@ struct svn_ra_serf__session_t {
apr_uri_t repos_root;
const char *repos_root_str;
+ /* The server is not Apache/mod_dav_svn (directly) and only supports
+ HTTP/1.0. Thus, we cannot send chunked requests. */
+ svn_boolean_t http10;
+
/* Our Version-Controlled-Configuration; may be NULL until we know it. */
const char *vcc_url;
@@ -186,7 +182,7 @@ struct svn_ra_serf__session_t {
const char *uuid;
/* Connection timeout value */
- long timeout;
+ apr_short_interval_time_t timeout;
/* HTTPv1 flags */
svn_tristate_t supports_deadprop_count;
@@ -280,40 +276,21 @@ static const svn_ra_serf__dav_props_t al
{ NULL }
};
-static const svn_ra_serf__dav_props_t vcc_props[] =
-{
- { "DAV:", "version-controlled-configuration" },
- { NULL }
-};
-
static const svn_ra_serf__dav_props_t check_path_props[] =
{
{ "DAV:", "resourcetype" },
{ NULL }
};
-static const svn_ra_serf__dav_props_t uuid_props[] =
-{
- { SVN_DAV_PROP_NS_DAV, "repository-uuid" },
- { NULL }
-};
-
-static const svn_ra_serf__dav_props_t repos_root_props[] =
-{
- { SVN_DAV_PROP_NS_DAV, "baseline-relative-path" },
- { NULL }
-};
-
-static const svn_ra_serf__dav_props_t href_props[] =
+static const svn_ra_serf__dav_props_t type_and_checksum_props[] =
{
- { "DAV:", "href" },
+ { "DAV:", "resourcetype" },
+ { SVN_DAV_PROP_NS_DAV, "sha1-checksum" },
{ NULL }
};
/* WC props compatibility with ra_neon. */
-#define SVN_RA_SERF__WC_NAMESPACE SVN_PROP_WC_PREFIX "ra_dav:"
-#define SVN_RA_SERF__WC_ACTIVITY_URL SVN_RA_SERF__WC_NAMESPACE "activity-url"
-#define SVN_RA_SERF__WC_CHECKED_IN_URL SVN_RA_SERF__WC_NAMESPACE "version-url"
+#define SVN_RA_SERF__WC_CHECKED_IN_URL SVN_PROP_WC_PREFIX "ra_dav:version-url"
/** Serf utility functions **/
@@ -364,7 +341,7 @@ typedef svn_error_t *
(*svn_ra_serf__response_handler_t)(serf_request_t *request,
serf_bucket_t *response,
void *handler_baton,
- apr_pool_t *pool);
+ apr_pool_t *scratch_pool);
/* Callback for when a request body is needed. */
/* ### should pass a scratch_pool */
@@ -388,6 +365,9 @@ typedef svn_error_t *
int status_code,
void *baton);
+/* ### we should reorder the types in this file. */
+typedef struct svn_ra_serf__server_error_t svn_ra_serf__server_error_t;
+
/*
* Structure that can be passed to our default handler to guide the
* execution of the request through its lifecycle.
@@ -402,10 +382,24 @@ typedef struct svn_ra_serf__handler_t {
/* The content-type of the request body. */
const char *body_type;
+ /* Has the request/response been completed? */
+ svn_boolean_t done;
+
+ /* If we captured an error from the server, then this will be non-NULL.
+ It will be allocated from HANDLER_POOL. */
+ svn_ra_serf__server_error_t *server_error;
+
/* The handler and baton pair for our handler. */
svn_ra_serf__response_handler_t response_handler;
void *response_baton;
+ /* When REPONSE_HANDLER is invoked, the following fields will be set
+ based on the response header. HANDLER_POOL must be non-NULL for these
+ values to be filled in. SLINE.REASON and LOCATION will be allocated
+ within HANDLER_POOL. */
+ serf_status_line sline; /* The parsed Status-Line */
+ const char *location; /* The Location: header, if any */
+
/* The handler and baton pair to be executed when a non-recoverable error
* is detected. If it is NULL in the presence of an error, an abort() may
* be triggered.
@@ -437,8 +431,34 @@ typedef struct svn_ra_serf__handler_t {
/* The connection and session to be used for this request. */
svn_ra_serf__connection_t *conn;
svn_ra_serf__session_t *session;
+
+ /* Internal flag to indicate we've parsed the headers. */
+ svn_boolean_t reading_body;
+
+ /* When this flag will be set, the core handler will discard any unread
+ portion of the response body. The registered response handler will
+ no longer be called. */
+ svn_boolean_t discard_body;
+
+ /* Pool for allocating SLINE.REASON and LOCATION. If this pool is NULL,
+ then the requestor does not care about SLINE and LOCATION. */
+ apr_pool_t *handler_pool;
+
} svn_ra_serf__handler_t;
+
+/* Run one request and process the response.
+
+ Similar to context_run_wait(), but this creates the request for HANDLER
+ and then waits for it to complete.
+
+ WARNING: context_run_wait() does NOT create a request, whereas this
+ function DOES. Avoid a double-create. */
+svn_error_t *
+svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler,
+ apr_pool_t *scratch_pool);
+
+
/*
* Helper function to queue a request in the @a handler's connection.
*/
@@ -532,16 +552,6 @@ struct svn_ra_serf__xml_parser_t {
/* Our previously used states (will be reused). */
svn_ra_serf__xml_state_t *free_state;
- /* If non-NULL, the status code of the response will be stored here.
- *
- * If this is NULL and an error is received, an abort will be triggered.
- */
- int *status_code;
-
- /* If non-NULL, this is the value of the response's Location header.
- */
- const char *location;
-
/* If non-NULL, this value will be set to TRUE when the response is
* completed.
*/
@@ -601,19 +611,217 @@ struct svn_ra_serf__xml_parser_t {
apr_off_t read_size; /* Number of bytes read from response */
};
+
+/* v2 of the XML parsing functions */
+
+/* The XML parsing context. */
+typedef struct svn_ra_serf__xml_context_t svn_ra_serf__xml_context_t;
+
+
+/* An opaque structure for the XML parse element/state. */
+typedef struct svn_ra_serf__xml_estate_t svn_ra_serf__xml_estate_t;
+
+/* Called just after the parser moves into ENTERED_STATE. The tag causing
+ the transition is passed in TAG.
+
+ This callback is applied to a parsing context by using the
+ svn_ra_serf__xml_context_customize() function.
+
+ NOTE: this callback, when set, will be invoked on *every* transition.
+ The callback must examine ENTERED_STATE to determine if any action
+ must be taken. The original state is not provided, but must be derived
+ from ENTERED_STATE and/or the TAG causing the transition (if needed). */
+typedef svn_error_t *
+(*svn_ra_serf__xml_opened_t)(svn_ra_serf__xml_estate_t *xes,
+ void *baton,
+ int entered_state,
+ const svn_ra_serf__dav_props_t *tag,
+ apr_pool_t *scratch_pool);
+
+
+/* Called just before the parser leaves LEAVING_STATE.
+
+ If cdata collection was enabled for this state, then CDATA will be
+ non-NULL and contain the collected cdata.
+
+ If attribute collection was enabled for this state, then ATTRS will
+ contain the attributes collected for this element only, along with
+ any values stored via svn_ra_serf__xml_note().
+
+ Use svn_ra_serf__xml_gather_since() to gather up data from outer states.
+
+ ATTRS is char* -> char*.
+
+ Temporary allocations may be made in SCRATCH_POOL. */
+typedef svn_error_t *
+(*svn_ra_serf__xml_closed_t)(svn_ra_serf__xml_estate_t *xes,
+ void *baton,
+ int leaving_state,
+ const svn_string_t *cdata,
+ apr_hash_t *attrs,
+ apr_pool_t *scratch_pool);
+
+
+/* Called for all states that are not using the builtin cdata collection.
+ This callback is (only) appropriate for unbounded-size cdata content.
+
+ CURRENT_STATE may be used to decide what to do with the data.
+
+ Temporary allocations may be made in SCRATCH_POOL. */
+typedef svn_error_t *
+(*svn_ra_serf__xml_cdata_t)(svn_ra_serf__xml_estate_t *xes,
+ void *baton,
+ int current_state,
+ const char *data,
+ apr_size_t len,
+ apr_pool_t *scratch_pool);
+
+
+/* State transition table.
+
+ When the XML Context is constructed, it is in state 0. User states are
+ positive integers.
+
+ In a list of transitions, use { 0 } to indicate the end. Specifically,
+ the code looks for NS == NULL.
+
+ ### more docco
+*/
+typedef struct svn_ra_serf__xml_transition_t {
+ /* This transition applies when in this state */
+ int from_state;
+
+ /* And when this tag is observed */
+ const char *ns;
+ const char *name;
+
+ /* Moving to this state */
+ int to_state;
+
+ /* Should the cdata of NAME be collected? Note that CUSTOM_CLOSE should
+ be TRUE in order to capture this cdata. */
+ svn_boolean_t collect_cdata;
+
+ /* Which attributes of NAME should be collected? Terminate with NULL.
+ Maximum of 10 attributes may be collected. Note that attribute
+ namespaces are ignored at this time.
+
+ Attribute names beginning with "?" are optional. Other names must
+ exist on the element, or SVN_ERR_XML_ATTRIB_NOT_FOUND will be raised. */
+ const char *collect_attrs[11];
+
+ /* When NAME is closed, should the callback be invoked? */
+ svn_boolean_t custom_close;
+
+} svn_ra_serf__xml_transition_t;
+
+
+/* Construct an XML parsing context, based on the TTABLE transition table.
+ As content is parsed, the CLOSED_CB callback will be invoked according
+ to the definition in the table.
+
+ If OPENED_CB is not NULL, then it will be invoked for *every* tag-open
+ event. The callback will need to use the ENTERED_STATE and TAG parameters
+ to decide what it would like to do.
+
+ If CDATA_CB is not NULL, then it will be called for all cdata that is
+ not be automatically collected (based on the transition table record's
+ COLLECT_CDATA flag). It will be called in every state, so the callback
+ must examine the CURRENT_STATE parameter to decide what to do.
+
+ The same BATON value will be passed to all three callbacks.
+
+ The context will be created within RESULT_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);
+
+/* Destroy all subpools for this structure. */
+void
+svn_ra_serf__xml_context_destroy(
+ svn_ra_serf__xml_context_t *xmlctx);
+
+/* Construct a handler with the response function/baton set up to parse
+ a response body using the given XML context. The handler and its
+ internal structures are allocated in RESULT_POOL.
+
+ This also initializes HANDLER_POOL to the given RESULT_POOL. */
+svn_ra_serf__handler_t *
+svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx,
+ apr_pool_t *result_pool);
+
+
+/* Allocated within XES->STATE_POOL. Changes are not allowd (callers
+ should make a deep copy if they need to make changes).
+
+ The resulting hash maps char* names to char* values. */
+apr_hash_t *
+svn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t *xes,
+ int stop_state);
+
+
+/* Attach the NAME/VALUE pair onto this/parent state identified by STATE.
+ The name and value will be copied into the target state's pool.
+
+ These values will be available to the CLOSED_CB for the target state,
+ or part of the gathered state via xml_gather_since().
+
+ Typically, this function is used by a child state's close callback,
+ or within an opening callback to store additional data.
+
+ Note: if the state is not found, then a programmer error has occurred,
+ so the function will invoke SVN_ERR_MALFUNCTION(). */
+void
+svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes,
+ int state,
+ const char *name,
+ const char *value);
+
+
+/* Returns XES->STATE_POOL for allocating structures that should live
+ as long as the state identified by XES.
+
+ Note: a state pool is created upon demand, so only use this function
+ when memory is required for a given state. */
+apr_pool_t *
+svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes);
+
+
+/* Any XML parser may be used. When an opening tag is seen, call this
+ function to feed the information into XMLCTX. */
+svn_error_t *
+svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
+ const char *raw_name,
+ const char *const *attrs);
+
+
+/* When a close tag is seen, call this function to feed the information
+ into XMLCTX. */
+svn_error_t *
+svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
+ const char *raw_name);
+
+
+/* When cdata is parsed by the wrapping XML parser, call this function to
+ feed the cdata into the XMLCTX. */
+svn_error_t *
+svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
+ const char *data,
+ apr_size_t len);
+
+
/*
* Parses a server-side error message into a local Subversion error.
*/
-typedef struct svn_ra_serf__server_error_t {
+struct svn_ra_serf__server_error_t {
/* Our local representation of the error. */
svn_error_t *error;
- /* Have we checked to see if there's an XML error in this response? */
- svn_boolean_t init;
-
- /* Was there an XML error response? */
- svn_boolean_t has_xml_response;
-
/* Are we done with the response? */
svn_boolean_t done;
@@ -631,41 +839,8 @@ typedef struct svn_ra_serf__server_error
/* XML parser and namespace used to parse the remote response */
svn_ra_serf__xml_parser_t parser;
-} svn_ra_serf__server_error_t;
-
-/* A simple request context that can be passed to handle_status_only. */
-typedef struct svn_ra_serf__simple_request_context_t {
- apr_pool_t *pool;
-
- /* The HTTP status code of the response */
- int status;
-
- /* The HTTP status line of the response */
- const char *reason;
-
- /* The Location header value of the response, or NULL if there
- wasn't one. */
- const char *location;
-
- /* This value is set to TRUE when the response is completed. */
- svn_boolean_t done;
+};
- /* If an error occurred, this value will be initialized. */
- svn_ra_serf__server_error_t server_error;
-} svn_ra_serf__simple_request_context_t;
-
-/*
- * Serf handler for @a request / @a response pair that takes in a
- * @a baton (@see svn_ra_serf__simple_request_context_t).
- * Implements svn_ra_serf__response_handler_t.
- *
- * Temporary allocations are made in @a pool.
- */
-svn_error_t *
-svn_ra_serf__handle_status_only(serf_request_t *request,
- serf_bucket_t *response,
- void *baton,
- apr_pool_t *pool);
/*
* Handler that discards the entire @a response body associated with a
@@ -682,32 +857,45 @@ svn_ra_serf__handle_discard_body(serf_re
void *baton,
apr_pool_t *pool);
-/*
- * Handler that retrieves the embedded XML error response from the
- * the @a response body associated with a @a request.
- * Implements svn_ra_serf__response_handler_t.
- *
- * All temporary allocations will be made in a @a pool.
- */
-svn_error_t *
-svn_ra_serf__handle_server_error(serf_request_t *request,
- serf_bucket_t *response,
- apr_pool_t *pool);
/*
* Handler that retrieves the embedded XML multistatus response from the
- * the @a RESPONSE body associated with a @a REQUEST. *DONE is set to TRUE.
+ * the @a RESPONSE body associated with a @a REQUEST.
+ *
* Implements svn_ra_serf__response_handler_t.
*
- * The @a BATON should be of type svn_ra_serf__simple_request_context_t.
+ * The @a BATON should be of type svn_ra_serf__handler_t. When the request
+ * is complete, the handler's DONE flag will be set to TRUE.
*
- * All temporary allocations will be made in a @a pool.
+ * All temporary allocations will be made in a @a scratch_pool.
*/
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);
+
+
+/* Handler that expects an empty body.
+
+ If a body IS present, and it is text/xml, then it will be parsed for
+ a server-side error.
+
+ BATON should be the svn_ra_serf__handler_t running REQUEST.
+
+ Status line information will be in HANDLER->SLINE.
+
+ Any parsed errors will be left in HANDLER->SERVER_ERROR. That member
+ may be NULL if no body was present, or a problem occurred trying to
+ parse the body.
+
+ All temporary allocations will be made in SCRATCH_POOL. */
+svn_error_t *
+svn_ra_serf__expect_empty_body(serf_request_t *request,
+ serf_bucket_t *response,
+ void *baton,
+ apr_pool_t *scratch_pool);
+
/*
* This function will feed the RESPONSE body into XMLP. When parsing is
@@ -736,12 +924,6 @@ svn_ra_serf__response_discard_handler(se
void *baton,
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.
- */
-const char *
-svn_ra_serf__response_get_location(serf_bucket_t *response,
- apr_pool_t *pool);
/** XML helper functions. **/
@@ -827,12 +1009,12 @@ svn_ra_serf__add_cdata_len_buckets(serf_
* Look up the @a attrs array for namespace definitions and add each one
* to the @a ns_list of namespaces.
*
- * New namespaces will be allocated in @a pool.
+ * New namespaces will be allocated in RESULT_POOL.
*/
void
svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list,
- const char **attrs,
- apr_pool_t *pool);
+ const char *const *attrs,
+ apr_pool_t *result_pool);
/*
* Look up @a name in the @a ns_list list for previously declared namespace
@@ -843,27 +1025,12 @@ svn_ra_serf__define_ns(svn_ra_serf__ns_t
*/
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);
/** PROPFIND-related functions **/
-/* Opaque structure representing PROPFINDs. */
-typedef struct svn_ra_serf__propfind_context_t svn_ra_serf__propfind_context_t;
-
-/*
- * Returns a flag representing whether the PROPFIND @a ctx is completed.
- */
-svn_boolean_t
-svn_ra_serf__propfind_is_done(svn_ra_serf__propfind_context_t *ctx);
-
-/*
- * Returns the response status code of the PROPFIND @a ctx.
- */
-int
-svn_ra_serf__propfind_status_code(svn_ra_serf__propfind_context_t *ctx);
-
/*
* This function will deliver a PROP_CTX PROPFIND request in the SESS
* serf context for the properties listed in LOOKUP_PROPS at URL for
@@ -873,7 +1040,7 @@ svn_ra_serf__propfind_status_code(svn_ra
* expected to call svn_ra_serf__wait_for_props().
*/
svn_error_t *
-svn_ra_serf__deliver_props(svn_ra_serf__propfind_context_t **prop_ctx,
+svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler,
apr_hash_t *prop_vals,
svn_ra_serf__session_t *sess,
svn_ra_serf__connection_t *conn,
@@ -885,13 +1052,12 @@ svn_ra_serf__deliver_props(svn_ra_serf__
apr_pool_t *pool);
/*
- * This helper function will block until the PROP_CTX indicates that is done
- * or another error is returned.
+ * This helper function will block until PROPFIND_HANDLER indicates that is
+ * done or another error is returned.
*/
svn_error_t *
-svn_ra_serf__wait_for_props(svn_ra_serf__propfind_context_t *prop_ctx,
- svn_ra_serf__session_t *sess,
- apr_pool_t *pool);
+svn_ra_serf__wait_for_props(svn_ra_serf__handler_t *handler,
+ apr_pool_t *scratch_pool);
/* This is a blocking version of deliver_props.
@@ -913,6 +1079,50 @@ svn_ra_serf__retrieve_props(apr_hash_t *
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
+
+/* Using CONN, fetch the properties specified by WHICH_PROPS using CONN
+ for URL at REVISION. The resulting properties are placed into a 2-level
+ hash in RESULTS, mapping NAMESPACE -> hash<PROPNAME, PROPVALUE>, which
+ is allocated in RESULT_POOL.
+
+ If REVISION is SVN_INVALID_REVNUM, then the properties are fetched
+ from HEAD for URL.
+
+ This function performs the request synchronously.
+
+ Temporary allocations are made in SCRATCH_POOL. */
+svn_error_t *
+svn_ra_serf__fetch_node_props(apr_hash_t **results,
+ svn_ra_serf__connection_t *conn,
+ const char *url,
+ svn_revnum_t revision,
+ const svn_ra_serf__dav_props_t *which_props,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+
+/* Using CONN, fetch a DAV: property from the resource identified by URL
+ within REVISION. The PROPNAME may be one of:
+
+ "checked-in"
+ "href"
+
+ The resulting value will be allocated in RESULT_POOL, and may be NULL
+ if the property does not exist (note: "href" always exists).
+
+ This function performs the request synchronously.
+
+ Temporary allocations are made in SCRATCH_POOL. */
+svn_error_t *
+svn_ra_serf__fetch_dav_prop(const char **value,
+ svn_ra_serf__connection_t *conn,
+ const char *url,
+ svn_revnum_t revision,
+ const char *propname,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+
/* Set PROPS for PATH at REV revision with a NS:NAME VAL.
*
* The POOL governs allocation.
@@ -941,6 +1151,15 @@ svn_ra_serf__walk_all_props(apr_hash_t *
void *baton,
apr_pool_t *pool);
+
+/* Like walk_all_props(), but a 2-level hash. */
+svn_error_t *
+svn_ra_serf__walk_node_props(apr_hash_t *props,
+ svn_ra_serf__walker_visitor_t walker,
+ void *baton,
+ apr_pool_t *scratch_pool);
+
+
typedef svn_error_t *
(*svn_ra_serf__path_rev_walker_t)(void *baton,
const char *path, apr_ssize_t path_len,
@@ -979,9 +1198,8 @@ svn_ra_serf__select_revprops(apr_hash_t
apr_pool_t *scratch_pool);
-/* PROPS is nested hash tables mapping REV -> PATH -> NS -> NAME -> VALUE.
- This function takes the tree of tables identified by PATH and REVISION
- (resulting in NS:NAME:VALUE hashes) and flattens them into a set of
+/* PROPS is nested hash tables mapping NS -> NAME -> VALUE.
+ This function takes the NS:NAME:VALUE hashes and flattens them into a set of
names to VALUE. The names are composed of NS:NAME, with specific
rewrite from wire names (DAV) to SVN names. This mapping is managed
by the svn_ra_serf__set_baton_props() function.
@@ -994,8 +1212,6 @@ svn_ra_serf__select_revprops(apr_hash_t
svn_error_t *
svn_ra_serf__flatten_props(apr_hash_t **flat_props,
apr_hash_t *props,
- const char *path,
- svn_revnum_t revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
@@ -1035,25 +1251,12 @@ svn_ra_serf__set_prop(apr_hash_t *props,
const svn_string_t *val, apr_pool_t *pool);
svn_error_t *
-svn_ra_serf__get_resource_type(svn_node_kind_t *kind,
- apr_hash_t *props,
- const char *url,
- svn_revnum_t revision);
+svn_ra_serf__get_resource_type(svn_kind_t *kind,
+ apr_hash_t *props);
/** MERGE-related functions **/
-typedef struct svn_ra_serf__merge_context_t svn_ra_serf__merge_context_t;
-
-svn_boolean_t*
-svn_ra_serf__merge_get_done_ptr(svn_ra_serf__merge_context_t *ctx);
-
-svn_commit_info_t*
-svn_ra_serf__merge_get_commit_info(svn_ra_serf__merge_context_t *ctx);
-
-int
-svn_ra_serf__merge_get_status(svn_ra_serf__merge_context_t *ctx);
-
void
svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens,
const char *parent,
@@ -1067,38 +1270,48 @@ svn_ra_serf__merge_lock_token_list(apr_h
client. If KEEP_LOCKS is set, instruct the server to not release
locks set on the paths included in this commit. */
svn_error_t *
-svn_ra_serf__merge_create_req(svn_ra_serf__merge_context_t **merge_ctx,
- svn_ra_serf__session_t *session,
- svn_ra_serf__connection_t *conn,
- const char *merge_resource_url,
- apr_hash_t *lock_tokens,
- svn_boolean_t keep_locks,
- apr_pool_t *pool);
+svn_ra_serf__run_merge(const svn_commit_info_t **commit_info,
+ int *response_code,
+ svn_ra_serf__session_t *session,
+ svn_ra_serf__connection_t *conn,
+ const char *merge_resource_url,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
/** OPTIONS-related functions **/
-typedef struct svn_ra_serf__options_context_t svn_ra_serf__options_context_t;
+/* On HTTPv2 connections, run an OPTIONS request over CONN to fetch the
+ current youngest revnum, returning it in *YOUNGEST.
-/* Is this OPTIONS-request done yet? */
-svn_boolean_t*
-svn_ra_serf__get_options_done_ptr(svn_ra_serf__options_context_t *ctx);
+ (the revnum is headers of the OPTIONS response)
-const char *
-svn_ra_serf__options_get_activity_collection(svn_ra_serf__options_context_t *ctx);
+ This function performs the request synchronously.
-svn_revnum_t
-svn_ra_serf__options_get_youngest_rev(svn_ra_serf__options_context_t *ctx);
+ All temporary allocations will be made in SCRATCH_POOL. */
+svn_error_t *
+svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest,
+ svn_ra_serf__connection_t *conn,
+ apr_pool_t *scratch_pool);
+
+
+/* On HTTPv1 connections, run an OPTIONS request over CONN to fetch the
+ activity collection set and return it in *ACTIVITY_URL, allocated
+ from RESULT_POOL.
+
+ (the activity-collection-set is in the body of the OPTIONS response)
+
+ This function performs the request synchronously.
+
+ All temporary allocations will be made in SCRATCH_POOL. */
+svn_error_t *
+svn_ra_serf__v1_get_activity_collection(const char **activity_url,
+ svn_ra_serf__connection_t *conn,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
-/* Create an OPTIONS request. When run, ask for an
- activity-collection-set in the request body (retrievable via
- accessor above) and also parse the server's capability headers into
- the SESSION->capabilites hash. */
-svn_error_t *
-svn_ra_serf__create_options_req(svn_ra_serf__options_context_t **opt_ctx,
- svn_ra_serf__session_t *session,
- svn_ra_serf__connection_t *conn,
- const char *path,
- apr_pool_t *pool);
/* Set @a VCC_URL to the default VCC for our repository based on @a
* ORIG_PATH for the session @a SESSION, ensuring that the VCC URL and
@@ -1138,34 +1351,63 @@ svn_ra_serf__get_relative_path(const cha
svn_ra_serf__connection_t *conn,
apr_pool_t *pool);
-/* Set *BC_URL to the baseline collection url, and set *BC_RELATIVE to
- * the path relative to that url for URL in REVISION using SESSION.
- * BC_RELATIVE will be URI decoded.
- *
- * REVISION may be SVN_INVALID_REVNUM (to mean "the current HEAD
- * revision"). If URL is NULL, use SESSION's session url.
- *
- * If LATEST_REVNUM is not NULL, set it to the baseline revision. If
- * REVISION was set to SVN_INVALID_REVNUM, this will return the current
- * HEAD revision.
- *
- * If non-NULL, use CONN for communications with the server;
- * otherwise, use the default connection.
- *
- * Use POOL for all allocations.
- */
+
+/* Using the default connection in SESSION (conns[0]), get the youngest
+ revnum from the server, returning it in *YOUNGEST.
+
+ This function operates synchronously.
+
+ All temporary allocations are performed in SCRATCH_POOL. */
svn_error_t *
-svn_ra_serf__get_baseline_info(const char **bc_url,
- const char **bc_relative,
- svn_ra_serf__session_t *session,
- svn_ra_serf__connection_t *conn,
- const char *url,
- svn_revnum_t revision,
- svn_revnum_t *latest_revnum,
- apr_pool_t *pool);
+svn_ra_serf__get_youngest_revnum(svn_revnum_t *youngest,
+ svn_ra_serf__session_t *session,
+ apr_pool_t *scratch_pool);
+
+
+/* Generate a revision-stable URL.
+
+ The RA APIs all refer to user/public URLs that float along with the
+ youngest revision. In many cases, we do NOT want to work with that URL
+ since it can change from one moment to the next. Especially if we
+ attempt to operation against multiple floating URLs -- we could end up
+ referring to two separate revisions.
+
+ The DAV RA provider(s) solve this by generating a URL that is specific
+ to a revision by using a URL into a "baseline collection".
+
+ For a specified SESSION, with an optional CONN (if NULL, then the
+ session's default connection will be used; specifically SESSION->conns[0]),
+ generate a revision-stable URL for URL at REVISION. If REVISION is
+ SVN_INVALID_REVNUM, then the stable URL will refer to the youngest
+ revision at the time this function was called.
+
+ If URL is NULL, then the session root will be used.
+
+ The stable URL will be placed into *STABLE_URL, allocated from RESULT_POOL.
+
+ If LATEST_REVNUM is not NULL, then the revision used will be placed into
+ *LATEST_REVNUM. That will be equal to youngest, or the given REVISION.
+
+ This function operates synchronously, if any communication to the server
+ is required. Communication is needed if REVISION is SVN_INVALID_REVNUM
+ (to get the current youngest revnum), or if the specified REVISION is not
+ (yet) in our cache of baseline collections.
+
+ All temporary allocations are performed in SCRATCH_POOL. */
+svn_error_t *
+svn_ra_serf__get_stable_url(const char **stable_url,
+ svn_revnum_t *latest_revnum,
+ svn_ra_serf__session_t *session,
+ svn_ra_serf__connection_t *conn,
+ const char *url,
+ svn_revnum_t revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
/** RA functions **/
+/* Implements svn_ra__vtable_t.get_log(). */
svn_error_t *
svn_ra_serf__get_log(svn_ra_session_t *session,
const apr_array_header_t *paths,
@@ -1180,6 +1422,7 @@ svn_ra_serf__get_log(svn_ra_session_t *s
void *receiver_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.get_locations(). */
svn_error_t *
svn_ra_serf__get_locations(svn_ra_session_t *session,
apr_hash_t **locations,
@@ -1188,6 +1431,7 @@ svn_ra_serf__get_locations(svn_ra_sessio
const apr_array_header_t *location_revisions,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.get_location_segments(). */
svn_error_t *
svn_ra_serf__get_location_segments(svn_ra_session_t *session,
const char *path,
@@ -1198,6 +1442,7 @@ svn_ra_serf__get_location_segments(svn_r
void *receiver_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.do_diff(). */
svn_error_t *
svn_ra_serf__do_diff(svn_ra_session_t *session,
const svn_ra_reporter3_t **reporter,
@@ -1212,6 +1457,7 @@ svn_ra_serf__do_diff(svn_ra_session_t *s
void *diff_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.do_status(). */
svn_error_t *
svn_ra_serf__do_status(svn_ra_session_t *ra_session,
const svn_ra_reporter3_t **reporter,
@@ -1223,6 +1469,7 @@ svn_ra_serf__do_status(svn_ra_session_t
void *status_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.do_update(). */
svn_error_t *
svn_ra_serf__do_update(svn_ra_session_t *ra_session,
const svn_ra_reporter3_t **reporter,
@@ -1235,6 +1482,7 @@ svn_ra_serf__do_update(svn_ra_session_t
void *update_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.do_switch(). */
svn_error_t *
svn_ra_serf__do_switch(svn_ra_session_t *ra_session,
const svn_ra_reporter3_t **reporter,
@@ -1247,6 +1495,7 @@ svn_ra_serf__do_switch(svn_ra_session_t
void *switch_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.get_file_revs(). */
svn_error_t *
svn_ra_serf__get_file_revs(svn_ra_session_t *session,
const char *path,
@@ -1257,12 +1506,14 @@ svn_ra_serf__get_file_revs(svn_ra_sessio
void *handler_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.get_dated_revision(). */
svn_error_t *
svn_ra_serf__get_dated_revision(svn_ra_session_t *session,
svn_revnum_t *revision,
apr_time_t tm,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.get_commit_editor(). */
svn_error_t *
svn_ra_serf__get_commit_editor(svn_ra_session_t *session,
const svn_delta_editor_t **editor,
@@ -1274,6 +1525,7 @@ svn_ra_serf__get_commit_editor(svn_ra_se
svn_boolean_t keep_locks,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.get_file(). */
svn_error_t *
svn_ra_serf__get_file(svn_ra_session_t *session,
const char *path,
@@ -1283,6 +1535,7 @@ svn_ra_serf__get_file(svn_ra_session_t *
apr_hash_t **props,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.change_rev_prop(). */
svn_error_t *
svn_ra_serf__change_rev_prop(svn_ra_session_t *session,
svn_revnum_t rev,
@@ -1291,6 +1544,7 @@ svn_ra_serf__change_rev_prop(svn_ra_sess
const svn_string_t *value,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.replay(). */
svn_error_t *
svn_ra_serf__replay(svn_ra_session_t *ra_session,
svn_revnum_t revision,
@@ -1300,6 +1554,7 @@ svn_ra_serf__replay(svn_ra_session_t *ra
void *edit_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.replay_range(). */
svn_error_t *
svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
svn_revnum_t start_revision,
@@ -1311,6 +1566,7 @@ svn_ra_serf__replay_range(svn_ra_session
void *replay_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.lock(). */
svn_error_t *
svn_ra_serf__lock(svn_ra_session_t *ra_session,
apr_hash_t *path_revs,
@@ -1320,6 +1576,7 @@ svn_ra_serf__lock(svn_ra_session_t *ra_s
void *lock_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.unlock(). */
svn_error_t *
svn_ra_serf__unlock(svn_ra_session_t *ra_session,
apr_hash_t *path_tokens,
@@ -1328,12 +1585,14 @@ svn_ra_serf__unlock(svn_ra_session_t *ra
void *lock_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.get_lock(). */
svn_error_t *
svn_ra_serf__get_lock(svn_ra_session_t *ra_session,
svn_lock_t **lock,
const char *path,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.get_locks(). */
svn_error_t *
svn_ra_serf__get_locks(svn_ra_session_t *ra_session,
apr_hash_t **locks,
@@ -1341,13 +1600,19 @@ svn_ra_serf__get_locks(svn_ra_session_t
svn_depth_t depth,
apr_pool_t *pool);
-svn_error_t * svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session,
- apr_hash_t **mergeinfo,
- const apr_array_header_t *paths,
- svn_revnum_t revision,
- svn_mergeinfo_inheritance_t inherit,
- svn_boolean_t include_descendants,
- apr_pool_t *pool);
+/* Request a mergeinfo-report from the URL attached to SESSION,
+ and fill in the MERGEINFO hash with the results.
+
+ Implements svn_ra__vtable_t.get_mergeinfo().
+ */
+svn_error_t *
+svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session,
+ apr_hash_t **mergeinfo,
+ const apr_array_header_t *paths,
+ svn_revnum_t revision,
+ svn_mergeinfo_inheritance_t inherit,
+ svn_boolean_t include_descendants,
+ apr_pool_t *pool);
/* Exchange capabilities with the server, by sending an OPTIONS
* request announcing the client's capabilities, and by filling
@@ -1366,14 +1631,14 @@ svn_ra_serf__exchange_capabilities(svn_r
const char **corrected_url,
apr_pool_t *pool);
-/* Implements the has_capability RA layer function. */
+/* Implements svn_ra__vtable_t.has_capability(). */
svn_error_t *
svn_ra_serf__has_capability(svn_ra_session_t *ra_session,
svn_boolean_t *has,
const char *capability,
apr_pool_t *pool);
-/* Implements the get_deleted_rev RA layer function. */
+/* Implements svn_ra__vtable_t.get_deleted_rev(). */
svn_error_t *
svn_ra_serf__get_deleted_rev(svn_ra_session_t *session,
const char *path,
@@ -1382,6 +1647,18 @@ svn_ra_serf__get_deleted_rev(svn_ra_sess
svn_revnum_t *revision_deleted,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.get_repos_root(). */
+svn_error_t *
+svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session,
+ const char **url,
+ apr_pool_t *pool);
+
+/* Implements svn_ra__vtable_t.register_editor_shim_callbacks(). */
+svn_error_t *
+svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *session,
+ svn_delta_shim_callbacks_t *callbacks);
+
+
/*** Authentication handler declarations ***/
/**
@@ -1408,16 +1685,14 @@ svn_ra_serf__error_on_status(int status_
const char *path,
const char *location);
-svn_error_t *
-svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *session,
- svn_delta_shim_callbacks_t *callbacks);
-
-
+/* ###? */
svn_error_t *
svn_ra_serf__copy_into_spillbuf(svn_spillbuf_t **spillbuf,
serf_bucket_t *bkt,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
+
+/* ###? */
serf_bucket_t *
svn_ra_serf__create_sb_bucket(svn_spillbuf_t *spillbuf,
serf_bucket_alloc_t *allocator,
@@ -1425,12 +1700,6 @@ svn_ra_serf__create_sb_bucket(svn_spillb
apr_pool_t *scratch_pool);
-svn_error_t *
-svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session,
- const char **url,
- apr_pool_t *pool);
-
-
#ifdef __cplusplus
}
#endif /* __cplusplus */