summaryrefslogtreecommitdiff
path: root/subversion/libsvn_ra_serf
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_ra_serf')
-rw-r--r--subversion/libsvn_ra_serf/README2
-rw-r--r--subversion/libsvn_ra_serf/blame.c50
-rw-r--r--subversion/libsvn_ra_serf/blncache.c6
-rw-r--r--subversion/libsvn_ra_serf/blncache.h4
-rw-r--r--subversion/libsvn_ra_serf/commit.c1309
-rw-r--r--subversion/libsvn_ra_serf/eagain_bucket.c122
-rw-r--r--subversion/libsvn_ra_serf/get_deleted_rev.c18
-rw-r--r--subversion/libsvn_ra_serf/get_file.c425
-rw-r--r--subversion/libsvn_ra_serf/get_lock.c337
-rw-r--r--subversion/libsvn_ra_serf/getdate.c33
-rw-r--r--subversion/libsvn_ra_serf/getlocations.c32
-rw-r--r--subversion/libsvn_ra_serf/getlocationsegments.c36
-rw-r--r--subversion/libsvn_ra_serf/getlocks.c50
-rw-r--r--subversion/libsvn_ra_serf/inherited_props.c199
-rw-r--r--subversion/libsvn_ra_serf/libsvn_ra_serf.pc.in12
-rw-r--r--subversion/libsvn_ra_serf/lock.c679
-rw-r--r--subversion/libsvn_ra_serf/locks.c669
-rw-r--r--subversion/libsvn_ra_serf/log.c83
-rw-r--r--subversion/libsvn_ra_serf/merge.c82
-rw-r--r--subversion/libsvn_ra_serf/mergeinfo.c27
-rw-r--r--subversion/libsvn_ra_serf/multistatus.c750
-rw-r--r--subversion/libsvn_ra_serf/options.c148
-rw-r--r--subversion/libsvn_ra_serf/property.c760
-rw-r--r--subversion/libsvn_ra_serf/ra_serf.h714
-rw-r--r--subversion/libsvn_ra_serf/replay.c955
-rw-r--r--subversion/libsvn_ra_serf/serf.c883
-rw-r--r--subversion/libsvn_ra_serf/stat.c615
-rw-r--r--subversion/libsvn_ra_serf/update.c4038
-rw-r--r--subversion/libsvn_ra_serf/util.c1549
-rw-r--r--subversion/libsvn_ra_serf/util_error.c3
-rw-r--r--subversion/libsvn_ra_serf/xml.c556
31 files changed, 7715 insertions, 7431 deletions
diff --git a/subversion/libsvn_ra_serf/README b/subversion/libsvn_ra_serf/README
index d3baf33..98a48a6 100644
--- a/subversion/libsvn_ra_serf/README
+++ b/subversion/libsvn_ra_serf/README
@@ -22,7 +22,7 @@ support for ra_serf:
For more about how ra_serf/ra_neon talk WebDAV, consult notes/webdav-protocol.
-Working copies are interchangable between ra_serf and ra_neon. (They both use
+Working copies are interchangeable between ra_serf and ra_neon. (They both use
the svn:wc:ra_dav:version-url property to store the latest revision of a file.)
Completed tasks
diff --git a/subversion/libsvn_ra_serf/blame.c b/subversion/libsvn_ra_serf/blame.c
index b6f136a..d915a19 100644
--- a/subversion/libsvn_ra_serf/blame.c
+++ b/subversion/libsvn_ra_serf/blame.c
@@ -47,7 +47,7 @@
* This enum represents the current state of our XML parsing for a REPORT.
*/
typedef enum blame_state_e {
- INITIAL = 0,
+ INITIAL = XML_STATE_INITIAL,
FILE_REVS_REPORT,
FILE_REV,
REV_PROP,
@@ -111,7 +111,6 @@ static const svn_ra_serf__xml_transition_t blame_ttable[] = {
{ 0 }
};
-
/* Conforms to svn_ra_serf__xml_opened_t */
static svn_error_t *
blame_opened(svn_ra_serf__xml_estate_t *xes,
@@ -140,17 +139,20 @@ blame_opened(svn_ra_serf__xml_estate_t *xes,
apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes);
apr_hash_t *gathered = svn_ra_serf__xml_gather_since(xes, FILE_REV);
const char *path;
- const char *rev;
+ const char *rev_str;
const char *merged_revision;
svn_txdelta_window_handler_t txdelta;
void *txdelta_baton;
+ apr_int64_t rev;
path = svn_hash_gets(gathered, "path");
- rev = svn_hash_gets(gathered, "rev");
+ rev_str = svn_hash_gets(gathered, "rev");
+
+ SVN_ERR(svn_cstring_atoi64(&rev, rev_str));
merged_revision = svn_hash_gets(gathered, "merged-revision");
SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton,
- path, SVN_STR_TO_REV(rev),
+ path, (svn_revnum_t)rev,
blame_ctx->rev_props,
merged_revision != NULL,
&txdelta, &txdelta_baton,
@@ -278,7 +280,8 @@ static svn_error_t *
create_file_revs_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
serf_bucket_t *buckets;
blame_context_t *blame_ctx = baton;
@@ -288,7 +291,7 @@ create_file_revs_body(serf_bucket_t **body_bkt,
svn_ra_serf__add_open_tag_buckets(buckets, alloc,
"S:file-revs-report",
"xmlns:S", SVN_XML_NAMESPACE,
- NULL);
+ SVN_VA_NULL);
svn_ra_serf__add_tag_buckets(buckets,
"S:start-revision", apr_ltoa(pool, blame_ctx->start),
@@ -300,9 +303,8 @@ create_file_revs_body(serf_bucket_t **body_bkt,
if (blame_ctx->include_merged_revisions)
{
- svn_ra_serf__add_tag_buckets(buckets,
- "S:include-merged-revisions", NULL,
- alloc);
+ svn_ra_serf__add_empty_tag_buckets(buckets, alloc,
+ "S:include-merged-revisions", SVN_VA_NULL);
}
svn_ra_serf__add_tag_buckets(buckets,
@@ -331,7 +333,7 @@ svn_ra_serf__get_file_revs(svn_ra_session_t *ra_session,
svn_ra_serf__handler_t *handler;
svn_ra_serf__xml_context_t *xmlctx;
const char *req_url;
- svn_error_t *err;
+ svn_revnum_t peg_rev;
blame_ctx = apr_pcalloc(pool, sizeof(*blame_ctx));
blame_ctx->pool = pool;
@@ -342,9 +344,16 @@ svn_ra_serf__get_file_revs(svn_ra_session_t *ra_session,
blame_ctx->end = end;
blame_ctx->include_merged_revisions = include_merged_revisions;
+ /* Since Subversion 1.8 we allow retrieving blames backwards. So we can't
+ just unconditionally use end_rev as the peg revision as before */
+ if (end > start)
+ peg_rev = end;
+ else
+ peg_rev = start;
+
SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
- session, NULL /* conn */,
- NULL /* url */, end,
+ session,
+ NULL /* url */, peg_rev,
pool, pool));
xmlctx = svn_ra_serf__xml_context_create(blame_ttable,
@@ -353,23 +362,18 @@ svn_ra_serf__get_file_revs(svn_ra_session_t *ra_session,
blame_cdata,
blame_ctx,
pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool);
handler->method = "REPORT";
handler->path = req_url;
handler->body_type = "text/xml";
handler->body_delegate = create_file_revs_body;
handler->body_delegate_baton = blame_ctx;
- handler->conn = session->conns[0];
- handler->session = session;
- err = svn_ra_serf__context_run_one(handler, pool);
+ SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
- err = svn_error_compose_create(
- svn_ra_serf__error_on_status(handler->sline,
- handler->path,
- handler->location),
- err);
+ if (handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
- return svn_error_trace(err);
+ return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_ra_serf/blncache.c b/subversion/libsvn_ra_serf/blncache.c
index d6abcdf..6fefa93 100644
--- a/subversion/libsvn_ra_serf/blncache.c
+++ b/subversion/libsvn_ra_serf/blncache.c
@@ -112,7 +112,7 @@ svn_ra_serf__blncache_set(svn_ra_serf__blncache_t *blncache,
const char *baseline_url,
svn_revnum_t revision,
const char *bc_url,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
if (bc_url && SVN_IS_VALID_REVNUM(revision))
{
@@ -147,11 +147,11 @@ svn_error_t *
svn_ra_serf__blncache_get_bc_url(const char **bc_url_p,
svn_ra_serf__blncache_t *blncache,
svn_revnum_t revnum,
- apr_pool_t *pool)
+ apr_pool_t *result_pool)
{
const char *value = apr_hash_get(blncache->revnum_to_bc,
&revnum, sizeof(revnum));
- *bc_url_p = value ? apr_pstrdup(pool, value) : NULL;
+ *bc_url_p = value ? apr_pstrdup(result_pool, value) : NULL;
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_ra_serf/blncache.h b/subversion/libsvn_ra_serf/blncache.h
index 5ad4eba..fe14799 100644
--- a/subversion/libsvn_ra_serf/blncache.h
+++ b/subversion/libsvn_ra_serf/blncache.h
@@ -59,7 +59,7 @@ svn_ra_serf__blncache_set(svn_ra_serf__blncache_t *blncache,
const char *baseline_url,
svn_revnum_t revnum,
const char *bc_url,
- apr_pool_t *pool);
+ apr_pool_t *scratch_pool);
/* Sets *BC_URL_P with a pointer to baseline collection URL for the given
* REVNUM. *BC_URL_P will be NULL if cache doesn't have information about
@@ -69,7 +69,7 @@ svn_error_t *
svn_ra_serf__blncache_get_bc_url(const char **bc_url_p,
svn_ra_serf__blncache_t *blncache,
svn_revnum_t revnum,
- apr_pool_t *pool);
+ apr_pool_t *result_pool);
/* Sets *BC_URL_P with pointer to baseline collection URL and *REVISION_P
* with revision number of baseline BASELINE_URL. *BC_URL_P will be NULL,
diff --git a/subversion/libsvn_ra_serf/commit.c b/subversion/libsvn_ra_serf/commit.c
index 9d48d41..b1e81c7 100644
--- a/subversion/libsvn_ra_serf/commit.c
+++ b/subversion/libsvn_ra_serf/commit.c
@@ -51,7 +51,6 @@ typedef struct commit_context_t {
apr_pool_t *pool;
svn_ra_serf__session_t *session;
- svn_ra_serf__connection_t *conn;
apr_hash_t *revprop_table;
@@ -83,15 +82,13 @@ typedef struct proppatch_context_t {
const char *relpath;
const char *path;
- commit_context_t *commit;
+ commit_context_t *commit_ctx;
- /* Changed and removed properties. */
- apr_hash_t *changed_props;
- apr_hash_t *removed_props;
+ /* Changed properties. const char * -> svn_prop_t * */
+ apr_hash_t *prop_changes;
- /* Same, for the old value (*old_value_p). */
- apr_hash_t *previous_changed_props;
- apr_hash_t *previous_removed_props;
+ /* Same, for the old value, or NULL. */
+ apr_hash_t *old_props;
/* In HTTP v2, this is the file/directory version we think we're changing. */
svn_revnum_t base_revision;
@@ -103,7 +100,9 @@ typedef struct delete_context_t {
svn_revnum_t revision;
- commit_context_t *commit;
+ commit_context_t *commit_ctx;
+
+ svn_boolean_t non_recursive_if; /* Only create a non-recursive If header */
} delete_context_t;
/* Represents a directory. */
@@ -112,7 +111,7 @@ typedef struct dir_context_t {
apr_pool_t *pool;
/* The root commit we're in progress for. */
- commit_context_t *commit;
+ commit_context_t *commit_ctx;
/* URL to operate against (used for CHECKOUT and PROPPATCH before
HTTP v2, for PROPPATCH in HTTP v2). */
@@ -139,9 +138,8 @@ typedef struct dir_context_t {
const char *copy_path;
svn_revnum_t copy_revision;
- /* Changed and removed properties */
- apr_hash_t *changed_props;
- apr_hash_t *removed_props;
+ /* Changed properties (const char * -> svn_prop_t *) */
+ apr_hash_t *prop_changes;
/* The checked-out working resource for this directory. May be NULL; if so
call checkout_dir() first. */
@@ -154,7 +152,7 @@ typedef struct file_context_t {
apr_pool_t *pool;
/* The root commit we're in progress for. */
- commit_context_t *commit;
+ commit_context_t *commit_ctx;
/* Is this file being added? (Otherwise, just opened.) */
svn_boolean_t added;
@@ -186,9 +184,8 @@ typedef struct file_context_t {
/* Our resulting checksum as reported by the WC. */
const char *result_checksum;
- /* Changed and removed properties. */
- apr_hash_t *changed_props;
- apr_hash_t *removed_props;
+ /* Changed properties (const char * -> svn_prop_t *) */
+ apr_hash_t *prop_changes;
/* URL to PUT the file at. */
const char *url;
@@ -198,39 +195,13 @@ typedef struct file_context_t {
/* Setup routines and handlers for various requests we'll invoke. */
-static svn_error_t *
-return_response_err(svn_ra_serf__handler_t *handler)
-{
- svn_error_t *err;
-
- /* We should have captured SLINE and LOCATION in the HANDLER. */
- SVN_ERR_ASSERT(handler->handler_pool != NULL);
-
- /* Ye Olde Fallback Error */
- err = svn_error_compose_create(
- handler->server_error != NULL
- ? handler->server_error->error
- : SVN_NO_ERROR,
- svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
- _("%s of '%s': %d %s"),
- handler->method, handler->path,
- handler->sline.code, handler->sline.reason));
-
- /* Try to return one of the standard errors for 301, 404, etc.,
- then look for an error embedded in the response. */
- return svn_error_compose_create(svn_ra_serf__error_on_status(
- handler->sline,
- handler->path,
- handler->location),
- err);
-}
-
/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_checkout_body(serf_bucket_t **bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
const char *activity_url = baton;
serf_bucket_t *body_bkt;
@@ -240,9 +211,11 @@ create_checkout_body(serf_bucket_t **bkt,
svn_ra_serf__add_xml_header_buckets(body_bkt, alloc);
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:checkout",
"xmlns:D", "DAV:",
- NULL);
- svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:activity-set", NULL);
- svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:href", NULL);
+ SVN_VA_NULL);
+ svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:activity-set",
+ SVN_VA_NULL);
+ svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:href",
+ SVN_VA_NULL);
SVN_ERR_ASSERT(activity_url != NULL);
svn_ra_serf__add_cdata_len_buckets(body_bkt, alloc,
@@ -251,7 +224,8 @@ create_checkout_body(serf_bucket_t **bkt,
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:href");
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:activity-set");
- svn_ra_serf__add_tag_buckets(body_bkt, "D:apply-to-version", NULL, alloc);
+ svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc,
+ "D:apply-to-version", SVN_VA_NULL);
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:checkout");
*bkt = body_bkt;
@@ -285,32 +259,30 @@ checkout_node(const char **working_url,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- svn_ra_serf__handler_t handler = { 0 };
+ svn_ra_serf__handler_t *handler;
apr_status_t status;
apr_uri_t uri;
/* HANDLER_POOL is the scratch pool since we don't need to remember
anything from the handler. We just want the working resource. */
- handler.handler_pool = scratch_pool;
- handler.session = commit_ctx->session;
- handler.conn = commit_ctx->conn;
+ handler = svn_ra_serf__create_handler(commit_ctx->session, scratch_pool);
- handler.body_delegate = create_checkout_body;
- handler.body_delegate_baton = (/* const */ void *)commit_ctx->activity_url;
- handler.body_type = "text/xml";
+ handler->body_delegate = create_checkout_body;
+ handler->body_delegate_baton = (/* const */ void *)commit_ctx->activity_url;
+ handler->body_type = "text/xml";
- handler.response_handler = svn_ra_serf__expect_empty_body;
- handler.response_baton = &handler;
+ handler->response_handler = svn_ra_serf__expect_empty_body;
+ handler->response_baton = handler;
- handler.method = "CHECKOUT";
- handler.path = node_url;
+ handler->method = "CHECKOUT";
+ handler->path = node_url;
- SVN_ERR(svn_ra_serf__context_run_one(&handler, scratch_pool));
+ SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
- if (handler.sline.code != 201)
- return svn_error_trace(return_response_err(&handler));
+ if (handler->sline.code != 201)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
- if (handler.location == NULL)
+ if (handler->location == NULL)
return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
_("No Location header received"));
@@ -319,7 +291,7 @@ checkout_node(const char **working_url,
'https:' transaction ... we'll work around that by stripping the
scheme, host, and port here and re-adding the correct ones
later. */
- status = apr_uri_parse(scratch_pool, handler.location, &uri);
+ status = apr_uri_parse(scratch_pool, handler->location, &uri);
if (status)
return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
_("Error parsing Location header value"));
@@ -365,11 +337,11 @@ retry_checkout_node(const char **working_url,
error and retry a few times, asking for the latest baseline
again. */
if (err && (err->apr_err != SVN_ERR_APMOD_BAD_BASELINE))
- return err;
+ return svn_error_trace(err);
}
while (err && retry_count--);
- return err;
+ return svn_error_trace(err);
}
@@ -377,8 +349,7 @@ static svn_error_t *
checkout_dir(dir_context_t *dir,
apr_pool_t *scratch_pool)
{
- svn_error_t *err;
- dir_context_t *p_dir = dir;
+ dir_context_t *c_dir = dir;
const char *checkout_url;
const char **working;
@@ -389,35 +360,35 @@ checkout_dir(dir_context_t *dir,
/* Is this directory or one of our parent dirs newly added?
* If so, we're already implicitly checked out. */
- while (p_dir)
+ while (c_dir)
{
- if (p_dir->added)
+ if (c_dir->added)
{
- /* Calculate the working_url by skipping the shared ancestor bewteen
- * the parent->relpath and dir->relpath. This is safe since an
+ /* Calculate the working_url by skipping the shared ancestor between
+ * the c_dir_parent->relpath and dir->relpath. This is safe since an
* add is guaranteed to have a parent that is checked out. */
- dir_context_t *parent = p_dir->parent_dir;
- const char *relpath = svn_relpath_skip_ancestor(parent->relpath,
+ dir_context_t *c_dir_parent = c_dir->parent_dir;
+ const char *relpath = svn_relpath_skip_ancestor(c_dir_parent->relpath,
dir->relpath);
/* Implicitly checkout this dir now. */
- SVN_ERR_ASSERT(parent->working_url);
+ SVN_ERR_ASSERT(c_dir_parent->working_url);
dir->working_url = svn_path_url_add_component2(
- parent->working_url,
+ c_dir_parent->working_url,
relpath, dir->pool);
return SVN_NO_ERROR;
}
- p_dir = p_dir->parent_dir;
+ c_dir = c_dir->parent_dir;
}
/* We could be called twice for the root: once to checkout the baseline;
* once to checkout the directory itself if we need to do so.
* Note: CHECKOUT_URL should live longer than HANDLER.
*/
- if (!dir->parent_dir && !dir->commit->baseline_url)
+ if (!dir->parent_dir && !dir->commit_ctx->baseline_url)
{
- checkout_url = dir->commit->vcc_url;
- working = &dir->commit->baseline_url;
+ checkout_url = dir->commit_ctx->vcc_url;
+ working = &dir->commit_ctx->baseline_url;
}
else
{
@@ -426,18 +397,9 @@ checkout_dir(dir_context_t *dir,
}
/* Checkout our directory into the activity URL now. */
- err = retry_checkout_node(working, dir->commit, checkout_url,
- dir->pool, scratch_pool);
- if (err)
- {
- if (err->apr_err == SVN_ERR_FS_CONFLICT)
- SVN_ERR_W(err, apr_psprintf(scratch_pool,
- _("Directory '%s' is out of date; try updating"),
- svn_dirent_local_style(dir->relpath, scratch_pool)));
- return err;
- }
-
- return SVN_NO_ERROR;
+ return svn_error_trace(retry_checkout_node(working, dir->commit_ctx,
+ checkout_url,
+ dir->pool, scratch_pool));
}
@@ -493,7 +455,6 @@ get_version_url(const char **checked_in_url,
else
{
const char *propfind_url;
- svn_ra_serf__connection_t *conn = session->conns[0];
if (SVN_IS_VALID_REVNUM(base_revision))
{
@@ -502,10 +463,9 @@ get_version_url(const char **checked_in_url,
this lookup, so we'll do things the hard(er) way, by
looking up the version URL from a resource in the
baseline collection. */
- /* ### conn==NULL for session->conns[0]. same as CONN. */
SVN_ERR(svn_ra_serf__get_stable_url(&propfind_url,
NULL /* latest_revnum */,
- session, NULL /* conn */,
+ session,
NULL /* url */, base_revision,
scratch_pool, scratch_pool));
}
@@ -514,8 +474,8 @@ get_version_url(const char **checked_in_url,
propfind_url = session->session_url.path;
}
- SVN_ERR(svn_ra_serf__fetch_dav_prop(&root_checkout,
- conn, propfind_url, base_revision,
+ SVN_ERR(svn_ra_serf__fetch_dav_prop(&root_checkout, session,
+ propfind_url, base_revision,
"checked-in",
scratch_pool, scratch_pool));
if (!root_checkout)
@@ -536,7 +496,6 @@ static svn_error_t *
checkout_file(file_context_t *file,
apr_pool_t *scratch_pool)
{
- svn_error_t *err;
dir_context_t *parent_dir = file->parent_dir;
const char *checkout_url;
@@ -548,6 +507,7 @@ checkout_file(file_context_t *file,
if (parent_dir->added)
{
/* Implicitly checkout this file now. */
+ SVN_ERR_ASSERT(parent_dir->working_url);
file->working_url = svn_path_url_add_component2(
parent_dir->working_url,
svn_relpath_skip_ancestor(
@@ -559,23 +519,14 @@ checkout_file(file_context_t *file,
}
SVN_ERR(get_version_url(&checkout_url,
- file->commit->session,
+ file->commit_ctx->session,
file->relpath, file->base_revision,
NULL, scratch_pool, scratch_pool));
/* Checkout our file into the activity URL now. */
- err = retry_checkout_node(&file->working_url, file->commit, checkout_url,
- file->pool, scratch_pool);
- if (err)
- {
- if (err->apr_err == SVN_ERR_FS_CONFLICT)
- SVN_ERR_W(err, apr_psprintf(scratch_pool,
- _("File '%s' is out of date; try updating"),
- svn_dirent_local_style(file->relpath, scratch_pool)));
- return err;
- }
-
- return SVN_NO_ERROR;
+ return svn_error_trace(retry_checkout_node(&file->working_url,
+ file->commit_ctx, checkout_url,
+ file->pool, scratch_pool));
}
/* Helper function for proppatch_walker() below. */
@@ -612,101 +563,23 @@ get_encoding_and_cdata(const char **encoding_p,
return SVN_NO_ERROR;
}
-typedef struct walker_baton_t {
- serf_bucket_t *body_bkt;
- apr_pool_t *body_pool;
-
- apr_hash_t *previous_changed_props;
- apr_hash_t *previous_removed_props;
-
- const char *path;
-
- /* Hack, since change_rev_prop(old_value_p != NULL, value = NULL) uses D:set
- rather than D:remove... (see notes/http-and-webdav/webdav-protocol) */
- enum {
- filter_all_props,
- filter_props_with_old_value,
- filter_props_without_old_value
- } filter;
-
- /* Is the property being deleted? */
- svn_boolean_t deleting;
-} walker_baton_t;
-
-/* If we have (recorded in WB) the old value of the property named NS:NAME,
- * then set *HAVE_OLD_VAL to TRUE and set *OLD_VAL_P to that old value
- * (which may be NULL); else set *HAVE_OLD_VAL to FALSE. */
-static svn_error_t *
-derive_old_val(svn_boolean_t *have_old_val,
- const svn_string_t **old_val_p,
- walker_baton_t *wb,
- const char *ns,
- const char *name)
-{
- *have_old_val = FALSE;
-
- if (wb->previous_changed_props)
- {
- const svn_string_t *val;
- val = svn_ra_serf__get_prop_string(wb->previous_changed_props,
- wb->path, ns, name);
- if (val)
- {
- *have_old_val = TRUE;
- *old_val_p = val;
- }
- }
-
- if (wb->previous_removed_props)
- {
- const svn_string_t *val;
- val = svn_ra_serf__get_prop_string(wb->previous_removed_props,
- wb->path, ns, name);
- if (val)
- {
- *have_old_val = TRUE;
- *old_val_p = NULL;
- }
- }
-
- return SVN_NO_ERROR;
-}
-
+/* Helper for create_proppatch_body. Writes per property xml to body */
static svn_error_t *
-proppatch_walker(void *baton,
- const char *ns,
- const char *name,
- const svn_string_t *val,
- apr_pool_t *scratch_pool)
+write_prop_xml(const proppatch_context_t *proppatch,
+ serf_bucket_t *body_bkt,
+ serf_bucket_alloc_t *alloc,
+ const svn_prop_t *prop,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- walker_baton_t *wb = baton;
- serf_bucket_t *body_bkt = wb->body_bkt;
serf_bucket_t *cdata_bkt;
- serf_bucket_alloc_t *alloc;
const char *encoding;
- svn_boolean_t have_old_val;
- const svn_string_t *old_val;
const svn_string_t *encoded_value;
const char *prop_name;
+ const svn_prop_t *old_prop;
- SVN_ERR(derive_old_val(&have_old_val, &old_val, wb, ns, name));
-
- /* Jump through hoops to work with D:remove and its val = (""-for-NULL)
- * representation. */
- if (wb->filter != filter_all_props)
- {
- if (wb->filter == filter_props_with_old_value && ! have_old_val)
- return SVN_NO_ERROR;
- if (wb->filter == filter_props_without_old_value && have_old_val)
- return SVN_NO_ERROR;
- }
- if (wb->deleting)
- val = NULL;
-
- alloc = body_bkt->allocator;
-
- SVN_ERR(get_encoding_and_cdata(&encoding, &encoded_value, alloc, val,
- wb->body_pool, scratch_pool));
+ SVN_ERR(get_encoding_and_cdata(&encoding, &encoded_value, alloc, prop->value,
+ result_pool, scratch_pool));
if (encoded_value)
{
cdata_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(encoded_value->data,
@@ -720,29 +593,40 @@ proppatch_walker(void *baton,
/* Use the namespace prefix instead of adding the xmlns attribute to support
property names containing ':' */
- if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0)
- prop_name = apr_pstrcat(wb->body_pool, "S:", name, (char *)NULL);
- else if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0)
- prop_name = apr_pstrcat(wb->body_pool, "C:", name, (char *)NULL);
+ if (strncmp(prop->name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
+ {
+ prop_name = apr_pstrcat(result_pool,
+ "S:", prop->name + sizeof(SVN_PROP_PREFIX) - 1,
+ SVN_VA_NULL);
+ }
+ else
+ {
+ prop_name = apr_pstrcat(result_pool,
+ "C:", prop->name,
+ SVN_VA_NULL);
+ }
if (cdata_bkt)
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, prop_name,
"V:encoding", encoding,
- NULL);
+ SVN_VA_NULL);
else
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, prop_name,
"V:" SVN_DAV__OLD_VALUE__ABSENT, "1",
- NULL);
+ SVN_VA_NULL);
- if (have_old_val)
+ old_prop = proppatch->old_props
+ ? svn_hash_gets(proppatch->old_props, prop->name)
+ : NULL;
+ if (old_prop)
{
const char *encoding2;
const svn_string_t *encoded_value2;
serf_bucket_t *cdata_bkt2;
SVN_ERR(get_encoding_and_cdata(&encoding2, &encoded_value2,
- alloc, old_val,
- wb->body_pool, scratch_pool));
+ alloc, old_prop->value,
+ result_pool, scratch_pool));
if (encoded_value2)
{
@@ -759,12 +643,12 @@ proppatch_walker(void *baton,
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
"V:" SVN_DAV__OLD_VALUE,
"V:encoding", encoding2,
- NULL);
+ SVN_VA_NULL);
else
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
"V:" SVN_DAV__OLD_VALUE,
"V:" SVN_DAV__OLD_VALUE__ABSENT, "1",
- NULL);
+ SVN_VA_NULL);
if (cdata_bkt2)
serf_bucket_aggregate_append(body_bkt, cdata_bkt2);
@@ -799,7 +683,7 @@ maybe_set_lock_token_header(serf_bucket_t *headers,
{
const char *token;
- if (! (relpath && commit_ctx->lock_tokens))
+ if (! (*relpath && commit_ctx->lock_tokens))
return SVN_NO_ERROR;
if (! svn_hash_gets(commit_ctx->deleted_entries, relpath))
@@ -819,7 +703,7 @@ maybe_set_lock_token_header(serf_bucket_t *headers,
token_uri = apr_uri_unparse(pool, &uri, 0);
token_header = apr_pstrcat(pool, "<", token_uri, "> (<", token, ">)",
- (char *)NULL);
+ SVN_VA_NULL);
serf_bucket_headers_set(headers, "If", token_header);
}
}
@@ -830,7 +714,8 @@ maybe_set_lock_token_header(serf_bucket_t *headers,
static svn_error_t *
setup_proppatch_headers(serf_bucket_t *headers,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
proppatch_context_t *proppatch = baton;
@@ -841,31 +726,26 @@ setup_proppatch_headers(serf_bucket_t *headers,
proppatch->base_revision));
}
- SVN_ERR(maybe_set_lock_token_header(headers, proppatch->commit,
- proppatch->relpath, pool));
+ if (proppatch->relpath && proppatch->commit_ctx)
+ SVN_ERR(maybe_set_lock_token_header(headers, proppatch->commit_ctx,
+ proppatch->relpath, pool));
return SVN_NO_ERROR;
}
-struct proppatch_body_baton_t {
- proppatch_context_t *proppatch;
-
- /* Content in the body should be allocated here, to live long enough. */
- apr_pool_t *body_pool;
-};
-
/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_proppatch_body(serf_bucket_t **bkt,
void *baton,
serf_bucket_alloc_t *alloc,
+ apr_pool_t *pool /* request pool */,
apr_pool_t *scratch_pool)
{
- struct proppatch_body_baton_t *pbb = baton;
- proppatch_context_t *ctx = pbb->proppatch;
+ proppatch_context_t *ctx = baton;
serf_bucket_t *body_bkt;
- walker_baton_t wb = { 0 };
+ svn_boolean_t opened = FALSE;
+ apr_hash_index_t *hi;
body_bkt = serf_bucket_aggregate_create(alloc);
@@ -875,58 +755,66 @@ create_proppatch_body(serf_bucket_t **bkt,
"xmlns:V", SVN_DAV_PROP_NS_DAV,
"xmlns:C", SVN_DAV_PROP_NS_CUSTOM,
"xmlns:S", SVN_DAV_PROP_NS_SVN,
- NULL);
+ SVN_VA_NULL);
- wb.body_bkt = body_bkt;
- wb.body_pool = pbb->body_pool;
- wb.previous_changed_props = ctx->previous_changed_props;
- wb.previous_removed_props = ctx->previous_removed_props;
- wb.path = ctx->path;
-
- if (apr_hash_count(ctx->changed_props) > 0)
+ /* First we write property SETs */
+ for (hi = apr_hash_first(scratch_pool, ctx->prop_changes);
+ hi;
+ hi = apr_hash_next(hi))
{
- svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:set", NULL);
- svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL);
+ svn_prop_t *prop = apr_hash_this_val(hi);
- wb.filter = filter_all_props;
- wb.deleting = FALSE;
- SVN_ERR(svn_ra_serf__walk_all_props(ctx->changed_props, ctx->path,
- SVN_INVALID_REVNUM,
- proppatch_walker, &wb,
- scratch_pool));
+ if (prop->value
+ || (ctx->old_props && svn_hash_gets(ctx->old_props, prop->name)))
+ {
+ if (!opened)
+ {
+ opened = TRUE;
+ svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:set",
+ SVN_VA_NULL);
+ svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop",
+ SVN_VA_NULL);
+ }
- svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop");
- svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:set");
+ SVN_ERR(write_prop_xml(ctx, body_bkt, alloc, prop,
+ pool, scratch_pool));
+ }
}
- if (apr_hash_count(ctx->removed_props) > 0)
+ if (opened)
{
- svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:set", NULL);
- svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL);
-
- wb.filter = filter_props_with_old_value;
- wb.deleting = TRUE;
- SVN_ERR(svn_ra_serf__walk_all_props(ctx->removed_props, ctx->path,
- SVN_INVALID_REVNUM,
- proppatch_walker, &wb,
- scratch_pool));
-
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop");
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:set");
}
- if (apr_hash_count(ctx->removed_props) > 0)
+ /* And then property REMOVEs */
+ opened = FALSE;
+
+ for (hi = apr_hash_first(scratch_pool, ctx->prop_changes);
+ hi;
+ hi = apr_hash_next(hi))
{
- svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:remove", NULL);
- svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL);
+ svn_prop_t *prop = apr_hash_this_val(hi);
- wb.filter = filter_props_without_old_value;
- wb.deleting = TRUE;
- SVN_ERR(svn_ra_serf__walk_all_props(ctx->removed_props, ctx->path,
- SVN_INVALID_REVNUM,
- proppatch_walker, &wb,
- scratch_pool));
+ if (!prop->value
+ && !(ctx->old_props && svn_hash_gets(ctx->old_props, prop->name)))
+ {
+ if (!opened)
+ {
+ opened = TRUE;
+ svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:remove",
+ SVN_VA_NULL);
+ svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop",
+ SVN_VA_NULL);
+ }
+ SVN_ERR(write_prop_xml(ctx, body_bkt, alloc, prop,
+ pool, scratch_pool));
+ }
+ }
+
+ if (opened)
+ {
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop");
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:remove");
}
@@ -938,45 +826,47 @@ create_proppatch_body(serf_bucket_t **bkt,
}
static svn_error_t*
-proppatch_resource(proppatch_context_t *proppatch,
- commit_context_t *commit,
+proppatch_resource(svn_ra_serf__session_t *session,
+ proppatch_context_t *proppatch,
apr_pool_t *pool)
{
svn_ra_serf__handler_t *handler;
- struct proppatch_body_baton_t pbb;
+ svn_error_t *err;
+
+ handler = svn_ra_serf__create_handler(session, pool);
- handler = apr_pcalloc(pool, sizeof(*handler));
- handler->handler_pool = pool;
handler->method = "PROPPATCH";
handler->path = proppatch->path;
- handler->conn = commit->conn;
- handler->session = commit->session;
handler->header_delegate = setup_proppatch_headers;
handler->header_delegate_baton = proppatch;
- pbb.proppatch = proppatch;
- pbb.body_pool = pool;
handler->body_delegate = create_proppatch_body;
- handler->body_delegate_baton = &pbb;
+ handler->body_delegate_baton = proppatch;
+ handler->body_type = "text/xml";
handler->response_handler = svn_ra_serf__handle_multistatus_only;
handler->response_baton = handler;
- SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
+ err = svn_ra_serf__context_run_one(handler, pool);
- if (handler->sline.code != 207
- || (handler->server_error != NULL
- && handler->server_error->error != NULL))
+ if (!err && handler->sline.code != 207)
+ err = svn_error_trace(svn_ra_serf__unexpected_status(handler));
+
+ /* Use specific error code for property handling errors.
+ Use loop to provide the right result with tracing */
+ if (err && err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
{
- return svn_error_create(
- SVN_ERR_RA_DAV_PROPPATCH_FAILED,
- return_response_err(handler),
- _("At least one property change failed; repository"
- " is unchanged"));
+ svn_error_t *e = err;
+
+ while (e && e->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
+ {
+ e->apr_err = SVN_ERR_RA_DAV_PROPPATCH_FAILED;
+ e = e->child;
+ }
}
- return SVN_NO_ERROR;
+ return svn_error_trace(err);
}
/* Implements svn_ra_serf__request_body_delegate_t */
@@ -984,7 +874,8 @@ static svn_error_t *
create_put_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
file_context_t *ctx = baton;
apr_off_t offset;
@@ -998,12 +889,10 @@ create_put_body(serf_bucket_t **body_bkt,
* check the buffer status; but serf will fall through and create a file
* bucket for us on the buffered svndiff handle.
*/
- apr_file_flush(ctx->svndiff);
-#if APR_VERSION_AT_LEAST(1, 3, 0)
+ SVN_ERR(svn_io_file_flush(ctx->svndiff, pool));
apr_file_buffer_set(ctx->svndiff, NULL, 0);
-#endif
offset = 0;
- apr_file_seek(ctx->svndiff, APR_SET, &offset);
+ SVN_ERR(svn_io_file_seek(ctx->svndiff, APR_SET, &offset, pool));
*body_bkt = serf_bucket_file_create(ctx->svndiff, alloc);
return SVN_NO_ERROR;
@@ -1014,7 +903,8 @@ static svn_error_t *
create_empty_put_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
*body_bkt = SERF_BUCKET_SIMPLE_STRING("", alloc);
return SVN_NO_ERROR;
@@ -1023,7 +913,8 @@ create_empty_put_body(serf_bucket_t **body_bkt,
static svn_error_t *
setup_put_headers(serf_bucket_t *headers,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
file_context_t *ctx = baton;
@@ -1045,7 +936,7 @@ setup_put_headers(serf_bucket_t *headers,
ctx->result_checksum);
}
- SVN_ERR(maybe_set_lock_token_header(headers, ctx->commit,
+ SVN_ERR(maybe_set_lock_token_header(headers, ctx->commit_ctx,
ctx->relpath, pool));
return APR_SUCCESS;
@@ -1054,21 +945,21 @@ setup_put_headers(serf_bucket_t *headers,
static svn_error_t *
setup_copy_file_headers(serf_bucket_t *headers,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
file_context_t *file = baton;
apr_uri_t uri;
const char *absolute_uri;
/* The Dest URI must be absolute. Bummer. */
- uri = file->commit->session->session_url;
+ uri = file->commit_ctx->session->session_url;
uri.path = (char*)file->url;
absolute_uri = apr_uri_unparse(pool, &uri, 0);
serf_bucket_headers_set(headers, "Destination", absolute_uri);
- serf_bucket_headers_setn(headers, "Depth", "0");
- serf_bucket_headers_setn(headers, "Overwrite", "T");
+ serf_bucket_headers_setn(headers, "Overwrite", "F");
return SVN_NO_ERROR;
}
@@ -1102,7 +993,7 @@ setup_if_header_recursive(svn_boolean_t *added,
hi;
hi = apr_hash_next(hi))
{
- const char *relpath = svn__apr_hash_index_key(hi);
+ const char *relpath = apr_hash_this_key(hi);
apr_uri_t uri;
if (!svn_relpath_skip_ancestor(rq_relpath, relpath))
@@ -1132,7 +1023,7 @@ setup_if_header_recursive(svn_boolean_t *added,
svn_stringbuf_appendbyte(sb, '<');
svn_stringbuf_appendcstr(sb, apr_uri_unparse(iterpool, &uri, 0));
svn_stringbuf_appendcstr(sb, "> (<");
- svn_stringbuf_appendcstr(sb, svn__apr_hash_index_val(hi));
+ svn_stringbuf_appendcstr(sb, apr_hash_this_val(hi));
svn_stringbuf_appendcstr(sb, ">)");
}
@@ -1153,29 +1044,31 @@ setup_if_header_recursive(svn_boolean_t *added,
static svn_error_t *
setup_add_dir_common_headers(serf_bucket_t *headers,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
dir_context_t *dir = baton;
svn_boolean_t added;
return svn_error_trace(
- setup_if_header_recursive(&added, headers, dir->commit, dir->relpath,
+ setup_if_header_recursive(&added, headers, dir->commit_ctx, dir->relpath,
pool));
}
static svn_error_t *
setup_copy_dir_headers(serf_bucket_t *headers,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
dir_context_t *dir = baton;
apr_uri_t uri;
const char *absolute_uri;
/* The Dest URI must be absolute. Bummer. */
- uri = dir->commit->session->session_url;
+ uri = dir->commit_ctx->session->session_url;
- if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
+ if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx))
{
uri.path = (char *)dir->url;
}
@@ -1190,18 +1083,20 @@ setup_copy_dir_headers(serf_bucket_t *headers,
serf_bucket_headers_set(headers, "Destination", absolute_uri);
serf_bucket_headers_setn(headers, "Depth", "infinity");
- serf_bucket_headers_setn(headers, "Overwrite", "T");
+ serf_bucket_headers_setn(headers, "Overwrite", "F");
/* Implicitly checkout this dir now. */
dir->working_url = apr_pstrdup(dir->pool, uri.path);
- return svn_error_trace(setup_add_dir_common_headers(headers, baton, pool));
+ return svn_error_trace(setup_add_dir_common_headers(headers, baton, pool,
+ scratch_pool));
}
static svn_error_t *
setup_delete_headers(serf_bucket_t *headers,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
delete_context_t *del = baton;
svn_boolean_t added;
@@ -1209,34 +1104,23 @@ setup_delete_headers(serf_bucket_t *headers,
serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER,
apr_ltoa(pool, del->revision));
- SVN_ERR(setup_if_header_recursive(&added, headers, del->commit,
- del->relpath, pool));
+ if (! del->non_recursive_if)
+ SVN_ERR(setup_if_header_recursive(&added, headers, del->commit_ctx,
+ del->relpath, pool));
+ else
+ {
+ SVN_ERR(maybe_set_lock_token_header(headers, del->commit_ctx,
+ del->relpath, pool));
+ added = TRUE;
+ }
- if (added && del->commit->keep_locks)
+ if (added && del->commit_ctx->keep_locks)
serf_bucket_headers_setn(headers, SVN_DAV_OPTIONS_HEADER,
SVN_DAV_OPTION_KEEP_LOCKS);
return SVN_NO_ERROR;
}
-/* Helper function to write the svndiff stream to temporary file. */
-static svn_error_t *
-svndiff_stream_write(void *file_baton,
- const char *data,
- apr_size_t *len)
-{
- file_context_t *ctx = file_baton;
- apr_status_t status;
-
- status = apr_file_write_full(ctx->svndiff, data, *len, NULL);
- if (status)
- return svn_error_wrap_apr(status, _("Failed writing updated file"));
-
- return SVN_NO_ERROR;
-}
-
-
-
/* POST against 'me' resource handlers. */
/* Implements svn_ra_serf__request_body_delegate_t */
@@ -1244,7 +1128,8 @@ static svn_error_t *
create_txn_post_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
apr_hash_t *revprops = baton;
svn_skel_t *request_skel;
@@ -1273,7 +1158,8 @@ create_txn_post_body(serf_bucket_t **body_bkt,
static svn_error_t *
setup_post_headers(serf_bucket_t *headers,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
#ifdef SVN_DAV_SEND_VTXN_NAME
/* Enable this to exercise the VTXN-NAME code based on a client
@@ -1365,66 +1251,47 @@ open_root(void *edit_baton,
apr_pool_t *dir_pool,
void **root_baton)
{
- commit_context_t *ctx = edit_baton;
+ commit_context_t *commit_ctx = edit_baton;
svn_ra_serf__handler_t *handler;
proppatch_context_t *proppatch_ctx;
dir_context_t *dir;
apr_hash_index_t *hi;
const char *proppatch_target = NULL;
+ apr_pool_t *scratch_pool = svn_pool_create(dir_pool);
- if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->session))
+ if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(commit_ctx->session))
{
post_response_ctx_t *prc;
const char *rel_path;
svn_boolean_t post_with_revprops
- = (NULL != svn_hash_gets(ctx->session->supported_posts,
+ = (NULL != svn_hash_gets(commit_ctx->session->supported_posts,
"create-txn-with-props"));
/* Create our activity URL now on the server. */
- handler = apr_pcalloc(ctx->pool, sizeof(*handler));
- handler->handler_pool = ctx->pool;
+ handler = svn_ra_serf__create_handler(commit_ctx->session, scratch_pool);
+
handler->method = "POST";
handler->body_type = SVN_SKEL_MIME_TYPE;
handler->body_delegate = create_txn_post_body;
handler->body_delegate_baton =
- post_with_revprops ? ctx->revprop_table : NULL;
+ post_with_revprops ? commit_ctx->revprop_table : NULL;
handler->header_delegate = setup_post_headers;
handler->header_delegate_baton = NULL;
- handler->path = ctx->session->me_resource;
- handler->conn = ctx->session->conns[0];
- handler->session = ctx->session;
+ handler->path = commit_ctx->session->me_resource;
- prc = apr_pcalloc(ctx->pool, sizeof(*prc));
+ prc = apr_pcalloc(scratch_pool, sizeof(*prc));
prc->handler = handler;
- prc->commit_ctx = ctx;
+ prc->commit_ctx = commit_ctx;
handler->response_handler = post_response_handler;
handler->response_baton = prc;
- SVN_ERR(svn_ra_serf__context_run_one(handler, ctx->pool));
+ SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
if (handler->sline.code != 201)
- {
- apr_status_t status = SVN_ERR_RA_DAV_REQUEST_FAILED;
-
- switch (handler->sline.code)
- {
- case 403:
- status = SVN_ERR_RA_DAV_FORBIDDEN;
- break;
- case 404:
- status = SVN_ERR_FS_NOT_FOUND;
- break;
- }
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
- return svn_error_createf(status, NULL,
- _("%s of '%s': %d %s (%s://%s)"),
- handler->method, handler->path,
- handler->sline.code, handler->sline.reason,
- ctx->session->session_url.scheme,
- ctx->session->session_url.hostinfo);
- }
- if (! (ctx->txn_root_url && ctx->txn_url))
+ if (! (commit_ctx->txn_root_url && commit_ctx->txn_url))
{
return svn_error_createf(
SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
@@ -1432,114 +1299,82 @@ open_root(void *edit_baton,
}
/* Fixup the txn_root_url to point to the anchor of the commit. */
- SVN_ERR(svn_ra_serf__get_relative_path(&rel_path,
- ctx->session->session_url.path,
- ctx->session, NULL, dir_pool));
- ctx->txn_root_url = svn_path_url_add_component2(ctx->txn_root_url,
- rel_path, ctx->pool);
+ SVN_ERR(svn_ra_serf__get_relative_path(
+ &rel_path,
+ commit_ctx->session->session_url.path,
+ commit_ctx->session,
+ scratch_pool));
+ commit_ctx->txn_root_url = svn_path_url_add_component2(
+ commit_ctx->txn_root_url,
+ rel_path, commit_ctx->pool);
/* Build our directory baton. */
dir = apr_pcalloc(dir_pool, sizeof(*dir));
dir->pool = dir_pool;
- dir->commit = ctx;
+ dir->commit_ctx = commit_ctx;
dir->base_revision = base_revision;
dir->relpath = "";
dir->name = "";
- dir->changed_props = apr_hash_make(dir->pool);
- dir->removed_props = apr_hash_make(dir->pool);
- dir->url = apr_pstrdup(dir->pool, ctx->txn_root_url);
+ dir->prop_changes = apr_hash_make(dir->pool);
+ dir->url = apr_pstrdup(dir->pool, commit_ctx->txn_root_url);
/* If we included our revprops in the POST, we need not
PROPPATCH them. */
- proppatch_target = post_with_revprops ? NULL : ctx->txn_url;
+ proppatch_target = post_with_revprops ? NULL : commit_ctx->txn_url;
}
else
{
- const char *activity_str = ctx->session->activity_collection_url;
+ const char *activity_str = commit_ctx->session->activity_collection_url;
if (!activity_str)
- SVN_ERR(svn_ra_serf__v1_get_activity_collection(&activity_str,
- ctx->session->conns[0],
- ctx->pool,
- ctx->pool));
-
- /* Cache the result. */
- if (activity_str)
- {
- ctx->session->activity_collection_url =
- apr_pstrdup(ctx->session->pool, activity_str);
- }
- else
- {
- return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
- _("The OPTIONS response did not include the "
- "requested activity-collection-set value"));
- }
+ SVN_ERR(svn_ra_serf__v1_get_activity_collection(
+ &activity_str,
+ commit_ctx->session,
+ scratch_pool, scratch_pool));
- ctx->activity_url =
- svn_path_url_add_component2(activity_str, svn_uuid_generate(ctx->pool),
- ctx->pool);
+ commit_ctx->activity_url = svn_path_url_add_component2(
+ activity_str,
+ svn_uuid_generate(scratch_pool),
+ commit_ctx->pool);
/* Create our activity URL now on the server. */
- handler = apr_pcalloc(ctx->pool, sizeof(*handler));
- handler->handler_pool = ctx->pool;
+ handler = svn_ra_serf__create_handler(commit_ctx->session, scratch_pool);
+
handler->method = "MKACTIVITY";
- handler->path = ctx->activity_url;
- handler->conn = ctx->session->conns[0];
- handler->session = ctx->session;
+ handler->path = commit_ctx->activity_url;
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
- SVN_ERR(svn_ra_serf__context_run_one(handler, ctx->pool));
+ SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
if (handler->sline.code != 201)
- {
- apr_status_t status = SVN_ERR_RA_DAV_REQUEST_FAILED;
-
- switch (handler->sline.code)
- {
- case 403:
- status = SVN_ERR_RA_DAV_FORBIDDEN;
- break;
- case 404:
- status = SVN_ERR_FS_NOT_FOUND;
- break;
- }
-
- return svn_error_createf(status, NULL,
- _("%s of '%s': %d %s (%s://%s)"),
- handler->method, handler->path,
- handler->sline.code, handler->sline.reason,
- ctx->session->session_url.scheme,
- ctx->session->session_url.hostinfo);
- }
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
/* Now go fetch our VCC and baseline so we can do a CHECKOUT. */
- SVN_ERR(svn_ra_serf__discover_vcc(&(ctx->vcc_url), ctx->session,
- ctx->conn, ctx->pool));
+ SVN_ERR(svn_ra_serf__discover_vcc(&(commit_ctx->vcc_url),
+ commit_ctx->session, scratch_pool));
/* Build our directory baton. */
dir = apr_pcalloc(dir_pool, sizeof(*dir));
dir->pool = dir_pool;
- dir->commit = ctx;
+ dir->commit_ctx = commit_ctx;
dir->base_revision = base_revision;
dir->relpath = "";
dir->name = "";
- dir->changed_props = apr_hash_make(dir->pool);
- dir->removed_props = apr_hash_make(dir->pool);
+ dir->prop_changes = apr_hash_make(dir->pool);
- SVN_ERR(get_version_url(&dir->url, dir->commit->session,
+ SVN_ERR(get_version_url(&dir->url, dir->commit_ctx->session,
dir->relpath,
- dir->base_revision, ctx->checked_in_url,
- dir->pool, dir->pool /* scratch_pool */));
- ctx->checked_in_url = dir->url;
+ dir->base_revision, commit_ctx->checked_in_url,
+ dir->pool, scratch_pool));
+ commit_ctx->checked_in_url = apr_pstrdup(commit_ctx->pool, dir->url);
/* Checkout our root dir */
- SVN_ERR(checkout_dir(dir, dir->pool /* scratch_pool */));
+ SVN_ERR(checkout_dir(dir, scratch_pool));
- proppatch_target = ctx->baseline_url;
+ proppatch_target = commit_ctx->baseline_url;
}
/* Unless this is NULL -- which means we don't need to PROPPATCH the
@@ -1547,44 +1382,58 @@ open_root(void *edit_baton,
transaction with our revprops. */
if (proppatch_target)
{
- proppatch_ctx = apr_pcalloc(ctx->pool, sizeof(*proppatch_ctx));
- proppatch_ctx->pool = dir_pool;
- proppatch_ctx->commit = ctx;
+ proppatch_ctx = apr_pcalloc(scratch_pool, sizeof(*proppatch_ctx));
+ proppatch_ctx->pool = scratch_pool;
+ proppatch_ctx->commit_ctx = NULL; /* No lock info */
proppatch_ctx->path = proppatch_target;
- proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
- proppatch_ctx->removed_props = apr_hash_make(proppatch_ctx->pool);
+ proppatch_ctx->prop_changes = apr_hash_make(proppatch_ctx->pool);
proppatch_ctx->base_revision = SVN_INVALID_REVNUM;
- for (hi = apr_hash_first(ctx->pool, ctx->revprop_table); hi;
+ for (hi = apr_hash_first(scratch_pool, commit_ctx->revprop_table);
+ hi;
hi = apr_hash_next(hi))
{
- const char *name = svn__apr_hash_index_key(hi);
- svn_string_t *value = svn__apr_hash_index_val(hi);
- const char *ns;
+ svn_prop_t *prop = apr_palloc(scratch_pool, sizeof(*prop));
- if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
- {
- ns = SVN_DAV_PROP_NS_SVN;
- name += sizeof(SVN_PROP_PREFIX) - 1;
- }
- else
- {
- ns = SVN_DAV_PROP_NS_CUSTOM;
- }
+ prop->name = apr_hash_this_key(hi);
+ prop->value = apr_hash_this_val(hi);
- svn_ra_serf__set_prop(proppatch_ctx->changed_props,
- proppatch_ctx->path,
- ns, name, value, proppatch_ctx->pool);
+ svn_hash_sets(proppatch_ctx->prop_changes, prop->name, prop);
}
- SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, ctx->pool));
+ SVN_ERR(proppatch_resource(commit_ctx->session,
+ proppatch_ctx, scratch_pool));
}
+ svn_pool_destroy(scratch_pool);
+
*root_baton = dir;
return SVN_NO_ERROR;
}
+/* Implements svn_ra_serf__request_body_delegate_t */
+static svn_error_t *
+create_delete_body(serf_bucket_t **body_bkt,
+ void *baton,
+ serf_bucket_alloc_t *alloc,
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
+{
+ delete_context_t *ctx = baton;
+ serf_bucket_t *body;
+
+ body = serf_bucket_aggregate_create(alloc);
+
+ svn_ra_serf__add_xml_header_buckets(body, alloc);
+
+ svn_ra_serf__merge_lock_token_list(ctx->commit_ctx->lock_tokens,
+ ctx->relpath, body, alloc, pool);
+
+ *body_bkt = body;
+ return SVN_NO_ERROR;
+}
+
static svn_error_t *
delete_entry(const char *path,
svn_revnum_t revision,
@@ -1596,10 +1445,11 @@ delete_entry(const char *path,
svn_ra_serf__handler_t *handler;
const char *delete_target;
- if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
+ if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx))
{
- delete_target = svn_path_url_add_component2(dir->commit->txn_root_url,
- path, dir->pool);
+ delete_target = svn_path_url_add_component2(
+ dir->commit_ctx->txn_root_url,
+ path, dir->pool);
}
else
{
@@ -1615,12 +1465,9 @@ delete_entry(const char *path,
delete_ctx = apr_pcalloc(pool, sizeof(*delete_ctx));
delete_ctx->relpath = apr_pstrdup(pool, path);
delete_ctx->revision = revision;
- delete_ctx->commit = dir->commit;
+ delete_ctx->commit_ctx = dir->commit_ctx;
- handler = apr_pcalloc(pool, sizeof(*handler));
- handler->handler_pool = pool;
- handler->session = dir->commit->session;
- handler->conn = dir->commit->conn;
+ handler = svn_ra_serf__create_handler(dir->commit_ctx->session, pool);
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
@@ -1630,17 +1477,43 @@ delete_entry(const char *path,
handler->method = "DELETE";
handler->path = delete_target;
+ handler->no_fail_on_http_failure_status = TRUE;
SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
- /* 204 No Content: item successfully deleted */
- if (handler->sline.code != 204)
+ if (handler->sline.code == 400)
{
- return svn_error_trace(return_response_err(handler));
+ /* Try again with non-standard body to overcome Apache Httpd
+ header limit */
+ delete_ctx->non_recursive_if = TRUE;
+
+ handler = svn_ra_serf__create_handler(dir->commit_ctx->session, pool);
+
+ handler->response_handler = svn_ra_serf__expect_empty_body;
+ handler->response_baton = handler;
+
+ handler->header_delegate = setup_delete_headers;
+ handler->header_delegate_baton = delete_ctx;
+
+ handler->method = "DELETE";
+ handler->path = delete_target;
+
+ handler->body_type = "text/xml";
+ handler->body_delegate = create_delete_body;
+ handler->body_delegate_baton = delete_ctx;
+
+ SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
}
- svn_hash_sets(dir->commit->deleted_entries,
- apr_pstrdup(dir->commit->pool, path), (void *)1);
+ if (handler->server_error)
+ return svn_ra_serf__server_error_create(handler, pool);
+
+ /* 204 No Content: item successfully deleted */
+ if (handler->sline.code != 204)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
+
+ svn_hash_sets(dir->commit_ctx->deleted_entries,
+ apr_pstrdup(dir->commit_ctx->pool, path), (void *)1);
return SVN_NO_ERROR;
}
@@ -1663,19 +1536,18 @@ add_directory(const char *path,
dir->pool = dir_pool;
dir->parent_dir = parent;
- dir->commit = parent->commit;
+ dir->commit_ctx = parent->commit_ctx;
dir->added = TRUE;
dir->base_revision = SVN_INVALID_REVNUM;
dir->copy_revision = copyfrom_revision;
dir->copy_path = apr_pstrdup(dir->pool, copyfrom_path);
dir->relpath = apr_pstrdup(dir->pool, path);
dir->name = svn_relpath_basename(dir->relpath, NULL);
- dir->changed_props = apr_hash_make(dir->pool);
- dir->removed_props = apr_hash_make(dir->pool);
+ dir->prop_changes = apr_hash_make(dir->pool);
- if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
+ if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx))
{
- dir->url = svn_path_url_add_component2(parent->commit->txn_root_url,
+ dir->url = svn_path_url_add_component2(parent->commit_ctx->txn_root_url,
path, dir->pool);
mkcol_target = dir->url;
}
@@ -1684,17 +1556,14 @@ add_directory(const char *path,
/* Ensure our parent is checked out. */
SVN_ERR(checkout_dir(parent, dir->pool /* scratch_pool */));
- dir->url = svn_path_url_add_component2(parent->commit->checked_in_url,
+ dir->url = svn_path_url_add_component2(parent->commit_ctx->checked_in_url,
dir->name, dir->pool);
mkcol_target = svn_path_url_add_component2(
parent->working_url,
dir->name, dir->pool);
}
- handler = apr_pcalloc(dir->pool, sizeof(*handler));
- handler->handler_pool = dir->pool;
- handler->conn = dir->commit->conn;
- handler->session = dir->commit->session;
+ handler = svn_ra_serf__create_handler(dir->commit_ctx->session, dir->pool);
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
@@ -1719,10 +1588,8 @@ add_directory(const char *path,
dir->copy_path);
}
- /* ### conn==NULL for session->conns[0]. same as commit->conn. */
SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
- dir->commit->session,
- NULL /* conn */,
+ dir->commit_ctx->session,
uri.path, dir->copy_revision,
dir_pool, dir_pool));
@@ -1732,26 +1599,13 @@ add_directory(const char *path,
handler->header_delegate = setup_copy_dir_headers;
handler->header_delegate_baton = dir;
}
-
+ /* We have the same problem as with DELETE here: if there are too many
+ locks, the request fails. But in this case there is no way to retry
+ with a non-standard request. #### How to fix? */
SVN_ERR(svn_ra_serf__context_run_one(handler, dir->pool));
- switch (handler->sline.code)
- {
- case 201: /* Created: item was successfully copied */
- case 204: /* No Content: item successfully replaced an existing target */
- break;
-
- case 403:
- return svn_error_createf(SVN_ERR_RA_DAV_FORBIDDEN, NULL,
- _("Access to '%s' forbidden"),
- handler->path);
- default:
- return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
- _("Adding directory failed: %s on %s "
- "(%d %s)"),
- handler->method, handler->path,
- handler->sline.code, handler->sline.reason);
- }
+ if (handler->sline.code != 201)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
*child_baton = dir;
@@ -1773,26 +1627,25 @@ open_directory(const char *path,
dir->pool = dir_pool;
dir->parent_dir = parent;
- dir->commit = parent->commit;
+ dir->commit_ctx = parent->commit_ctx;
dir->added = FALSE;
dir->base_revision = base_revision;
dir->relpath = apr_pstrdup(dir->pool, path);
dir->name = svn_relpath_basename(dir->relpath, NULL);
- dir->changed_props = apr_hash_make(dir->pool);
- dir->removed_props = apr_hash_make(dir->pool);
+ dir->prop_changes = apr_hash_make(dir->pool);
- if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
+ if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx))
{
- dir->url = svn_path_url_add_component2(parent->commit->txn_root_url,
+ dir->url = svn_path_url_add_component2(parent->commit_ctx->txn_root_url,
path, dir->pool);
}
else
{
SVN_ERR(get_version_url(&dir->url,
- dir->commit->session,
+ dir->commit_ctx->session,
dir->relpath, dir->base_revision,
- dir->commit->checked_in_url,
+ dir->commit_ctx->checked_in_url,
dir->pool, dir->pool /* scratch_pool */));
}
*child_baton = dir;
@@ -1804,48 +1657,23 @@ static svn_error_t *
change_dir_prop(void *dir_baton,
const char *name,
const svn_string_t *value,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
dir_context_t *dir = dir_baton;
- const char *ns;
- const char *proppatch_target;
+ svn_prop_t *prop;
-
- if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
- {
- proppatch_target = dir->url;
- }
- else
+ if (! USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx))
{
/* Ensure we have a checked out dir. */
- SVN_ERR(checkout_dir(dir, pool /* scratch_pool */));
-
- proppatch_target = dir->working_url;
+ SVN_ERR(checkout_dir(dir, scratch_pool));
}
- name = apr_pstrdup(dir->pool, name);
- if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
- {
- ns = SVN_DAV_PROP_NS_SVN;
- name += sizeof(SVN_PROP_PREFIX) - 1;
- }
- else
- {
- ns = SVN_DAV_PROP_NS_CUSTOM;
- }
+ prop = apr_palloc(dir->pool, sizeof(*prop));
- if (value)
- {
- value = svn_string_dup(value, dir->pool);
- svn_ra_serf__set_prop(dir->changed_props, proppatch_target,
- ns, name, value, dir->pool);
- }
- else
- {
- value = svn_string_create_empty(dir->pool);
- svn_ra_serf__set_prop(dir->removed_props, proppatch_target,
- ns, name, value, dir->pool);
- }
+ prop->name = apr_pstrdup(dir->pool, name);
+ prop->value = svn_string_dup(value, dir->pool);
+
+ svn_hash_sets(dir->prop_changes, prop->name, prop);
return SVN_NO_ERROR;
}
@@ -1861,20 +1689,18 @@ close_directory(void *dir_baton,
*/
/* PROPPATCH our prop change and pass it along. */
- if (apr_hash_count(dir->changed_props) ||
- apr_hash_count(dir->removed_props))
+ if (apr_hash_count(dir->prop_changes))
{
proppatch_context_t *proppatch_ctx;
proppatch_ctx = apr_pcalloc(pool, sizeof(*proppatch_ctx));
proppatch_ctx->pool = pool;
- proppatch_ctx->commit = dir->commit;
+ proppatch_ctx->commit_ctx = NULL /* No lock tokens necessary */;
proppatch_ctx->relpath = dir->relpath;
- proppatch_ctx->changed_props = dir->changed_props;
- proppatch_ctx->removed_props = dir->removed_props;
+ proppatch_ctx->prop_changes = dir->prop_changes;
proppatch_ctx->base_revision = dir->base_revision;
- if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
+ if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx))
{
proppatch_ctx->path = dir->url;
}
@@ -1883,7 +1709,8 @@ close_directory(void *dir_baton,
proppatch_ctx->path = dir->working_url;
}
- SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, dir->pool));
+ SVN_ERR(proppatch_resource(dir->commit_ctx->session,
+ proppatch_ctx, dir->pool));
}
return SVN_NO_ERROR;
@@ -1900,6 +1727,7 @@ add_file(const char *path,
dir_context_t *dir = parent_baton;
file_context_t *new_file;
const char *deleted_parent = path;
+ apr_pool_t *scratch_pool = svn_pool_create(file_pool);
new_file = apr_pcalloc(file_pool, sizeof(*new_file));
new_file->pool = file_pool;
@@ -1907,28 +1735,27 @@ add_file(const char *path,
dir->ref_count++;
new_file->parent_dir = dir;
- new_file->commit = dir->commit;
+ new_file->commit_ctx = dir->commit_ctx;
new_file->relpath = apr_pstrdup(new_file->pool, path);
new_file->name = svn_relpath_basename(new_file->relpath, NULL);
new_file->added = TRUE;
new_file->base_revision = SVN_INVALID_REVNUM;
new_file->copy_path = apr_pstrdup(new_file->pool, copy_path);
new_file->copy_revision = copy_revision;
- new_file->changed_props = apr_hash_make(new_file->pool);
- new_file->removed_props = apr_hash_make(new_file->pool);
+ new_file->prop_changes = apr_hash_make(new_file->pool);
/* Ensure that the file doesn't exist by doing a HEAD on the
resource. If we're using HTTP v2, we'll just look into the
transaction root tree for this thing. */
- if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit))
+ if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx))
{
- new_file->url = svn_path_url_add_component2(dir->commit->txn_root_url,
+ new_file->url = svn_path_url_add_component2(dir->commit_ctx->txn_root_url,
path, new_file->pool);
}
else
{
/* Ensure our parent directory has been checked out */
- SVN_ERR(checkout_dir(dir, new_file->pool /* scratch_pool */));
+ SVN_ERR(checkout_dir(dir, scratch_pool));
new_file->url =
svn_path_url_add_component2(dir->working_url,
@@ -1937,49 +1764,78 @@ add_file(const char *path,
while (deleted_parent && deleted_parent[0] != '\0')
{
- if (svn_hash_gets(dir->commit->deleted_entries, deleted_parent))
+ if (svn_hash_gets(dir->commit_ctx->deleted_entries, deleted_parent))
{
break;
}
deleted_parent = svn_relpath_dirname(deleted_parent, file_pool);
}
- if (! ((dir->added && !dir->copy_path) ||
- (deleted_parent && deleted_parent[0] != '\0')))
+ if (copy_path)
{
svn_ra_serf__handler_t *handler;
+ apr_uri_t uri;
+ const char *req_url;
+ apr_status_t status;
+
+ /* Create the copy directly as cheap 'does exist/out of date'
+ check. We update the copy (if needed) from close_file() */
+
+ status = apr_uri_parse(scratch_pool, copy_path, &uri);
+ if (status)
+ return svn_ra_serf__wrap_err(status, NULL);
+
+ SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
+ dir->commit_ctx->session,
+ uri.path, copy_revision,
+ scratch_pool, scratch_pool));
+
+ handler = svn_ra_serf__create_handler(dir->commit_ctx->session,
+ scratch_pool);
+ handler->method = "COPY";
+ handler->path = req_url;
- handler = apr_pcalloc(new_file->pool, sizeof(*handler));
- handler->handler_pool = new_file->pool;
- handler->session = new_file->commit->session;
- handler->conn = new_file->commit->conn;
- handler->method = "HEAD";
- handler->path = svn_path_url_add_component2(
- dir->commit->session->session_url.path,
- path, new_file->pool);
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
- SVN_ERR(svn_ra_serf__context_run_one(handler, new_file->pool));
+ handler->header_delegate = setup_copy_file_headers;
+ handler->header_delegate_baton = new_file;
- if (handler->sline.code != 404)
- {
- if (handler->sline.code != 200)
- {
- svn_error_t *err;
+ SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
- err = svn_ra_serf__error_on_status(handler->sline,
- handler->path,
- handler->location);
+ if (handler->sline.code != 201)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
+ }
+ else if (! ((dir->added && !dir->copy_path) ||
+ (deleted_parent && deleted_parent[0] != '\0')))
+ {
+ svn_ra_serf__handler_t *handler;
+ svn_error_t *err;
- SVN_ERR(err);
- }
+ handler = svn_ra_serf__create_handler(dir->commit_ctx->session,
+ scratch_pool);
+ handler->method = "HEAD";
+ handler->path = svn_path_url_add_component2(
+ dir->commit_ctx->session->session_url.path,
+ path, scratch_pool);
+ handler->response_handler = svn_ra_serf__expect_empty_body;
+ handler->response_baton = handler;
+ handler->no_dav_headers = TRUE; /* Read only operation outside txn */
+
+ err = svn_ra_serf__context_run_one(handler, scratch_pool);
- return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
- _("File '%s' already exists"), path);
+ if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err); /* Great. We can create a new file! */
}
+ else if (err)
+ return svn_error_trace(err);
+ else
+ return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
+ _("File '%s' already exists"), path);
}
+ svn_pool_destroy(scratch_pool);
*file_baton = new_file;
return SVN_NO_ERROR;
@@ -2001,17 +1857,16 @@ open_file(const char *path,
parent->ref_count++;
new_file->parent_dir = parent;
- new_file->commit = parent->commit;
+ new_file->commit_ctx = parent->commit_ctx;
new_file->relpath = apr_pstrdup(new_file->pool, path);
new_file->name = svn_relpath_basename(new_file->relpath, NULL);
new_file->added = FALSE;
new_file->base_revision = base_revision;
- new_file->changed_props = apr_hash_make(new_file->pool);
- new_file->removed_props = apr_hash_make(new_file->pool);
+ new_file->prop_changes = apr_hash_make(new_file->pool);
- if (USING_HTTPV2_COMMIT_SUPPORT(parent->commit))
+ if (USING_HTTPV2_COMMIT_SUPPORT(parent->commit_ctx))
{
- new_file->url = svn_path_url_add_component2(parent->commit->txn_root_url,
+ new_file->url = svn_path_url_add_component2(parent->commit_ctx->txn_root_url,
path, new_file->pool);
}
else
@@ -2027,6 +1882,23 @@ open_file(const char *path,
return SVN_NO_ERROR;
}
+/* Implements svn_stream_lazyopen_func_t for apply_textdelta */
+static svn_error_t *
+delayed_commit_stream_open(svn_stream_t **stream,
+ void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ file_context_t *file_ctx = baton;
+
+ SVN_ERR(svn_io_open_unique_file3(&file_ctx->svndiff, NULL, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ file_ctx->pool, scratch_pool));
+
+ *stream = svn_stream_from_aprfile2(file_ctx->svndiff, TRUE, result_pool);
+ return SVN_NO_ERROR;
+}
+
static svn_error_t *
apply_textdelta(void *file_baton,
const char *base_checksum,
@@ -2043,18 +1915,10 @@ apply_textdelta(void *file_baton,
* writing to a temporary file (ugh). A special svn stream serf bucket
* that returns EAGAIN until we receive the done call? But, when
* would we run through the serf context? Grr.
- *
- * ctx->pool is the same for all files in the commit that send a
- * textdelta so this file is explicitly closed in close_file to
- * avoid too many simultaneously open files.
*/
- SVN_ERR(svn_io_open_unique_file3(&ctx->svndiff, NULL, NULL,
- svn_io_file_del_on_pool_cleanup,
- ctx->pool, pool));
-
- ctx->stream = svn_stream_create(ctx, pool);
- svn_stream_set_write(ctx->stream, svndiff_stream_write);
+ ctx->stream = svn_stream_lazyopen_create(delayed_commit_stream_open,
+ ctx, FALSE, ctx->pool);
svn_txdelta_to_svndiff3(handler, handler_baton, ctx->stream, 0,
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
@@ -2072,33 +1936,14 @@ change_file_prop(void *file_baton,
apr_pool_t *pool)
{
file_context_t *file = file_baton;
- const char *ns;
+ svn_prop_t *prop;
- name = apr_pstrdup(file->pool, name);
+ prop = apr_palloc(file->pool, sizeof(*prop));
- if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
- {
- ns = SVN_DAV_PROP_NS_SVN;
- name += sizeof(SVN_PROP_PREFIX) - 1;
- }
- else
- {
- ns = SVN_DAV_PROP_NS_CUSTOM;
- }
-
- if (value)
- {
- value = svn_string_dup(value, file->pool);
- svn_ra_serf__set_prop(file->changed_props, file->url,
- ns, name, value, file->pool);
- }
- else
- {
- value = svn_string_create_empty(file->pool);
+ prop->name = apr_pstrdup(file->pool, name);
+ prop->value = svn_string_dup(value, file->pool);
- svn_ra_serf__set_prop(file->removed_props, file->url,
- ns, name, value, file->pool);
- }
+ svn_hash_sets(file->prop_changes, prop->name, prop);
return SVN_NO_ERROR;
}
@@ -2110,69 +1955,26 @@ close_file(void *file_baton,
{
file_context_t *ctx = file_baton;
svn_boolean_t put_empty_file = FALSE;
- apr_status_t status;
ctx->result_checksum = text_checksum;
- if (ctx->copy_path)
- {
- svn_ra_serf__handler_t *handler;
- apr_uri_t uri;
- const char *req_url;
-
- status = apr_uri_parse(scratch_pool, ctx->copy_path, &uri);
- if (status)
- {
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Unable to parse URL '%s'"),
- ctx->copy_path);
- }
-
- /* ### conn==NULL for session->conns[0]. same as commit->conn. */
- SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
- ctx->commit->session,
- NULL /* conn */,
- uri.path, ctx->copy_revision,
- scratch_pool, scratch_pool));
-
- handler = apr_pcalloc(scratch_pool, sizeof(*handler));
- handler->handler_pool = scratch_pool;
- handler->method = "COPY";
- handler->path = req_url;
- handler->conn = ctx->commit->conn;
- handler->session = ctx->commit->session;
-
- handler->response_handler = svn_ra_serf__expect_empty_body;
- handler->response_baton = handler;
-
- handler->header_delegate = setup_copy_file_headers;
- handler->header_delegate_baton = ctx;
-
- SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
-
- if (handler->sline.code != 201 && handler->sline.code != 204)
- {
- return svn_error_trace(return_response_err(handler));
- }
- }
-
/* If we got no stream of changes, but this is an added-without-history
* file, make a note that we'll be PUTting a zero-byte file to the server.
*/
- if ((!ctx->stream) && ctx->added && (!ctx->copy_path))
+ if ((!ctx->svndiff) && ctx->added && (!ctx->copy_path))
put_empty_file = TRUE;
/* If we had a stream of changes, push them to the server... */
- if (ctx->stream || put_empty_file)
+ if (ctx->svndiff || put_empty_file)
{
svn_ra_serf__handler_t *handler;
+ int expected_result;
+
+ handler = svn_ra_serf__create_handler(ctx->commit_ctx->session,
+ scratch_pool);
- handler = apr_pcalloc(scratch_pool, sizeof(*handler));
- handler->handler_pool = scratch_pool;
handler->method = "PUT";
handler->path = ctx->url;
- handler->conn = ctx->commit->conn;
- handler->session = ctx->commit->session;
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
@@ -2195,31 +1997,33 @@ close_file(void *file_baton,
SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
- if (handler->sline.code != 204 && handler->sline.code != 201)
- {
- return svn_error_trace(return_response_err(handler));
- }
+ if (ctx->added && ! ctx->copy_path)
+ expected_result = 201; /* Created */
+ else
+ expected_result = 204; /* Updated */
+
+ if (handler->sline.code != expected_result)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
}
if (ctx->svndiff)
SVN_ERR(svn_io_file_close(ctx->svndiff, scratch_pool));
/* If we had any prop changes, push them via PROPPATCH. */
- if (apr_hash_count(ctx->changed_props) ||
- apr_hash_count(ctx->removed_props))
+ if (apr_hash_count(ctx->prop_changes))
{
proppatch_context_t *proppatch;
- proppatch = apr_pcalloc(ctx->pool, sizeof(*proppatch));
- proppatch->pool = ctx->pool;
+ proppatch = apr_pcalloc(scratch_pool, sizeof(*proppatch));
+ proppatch->pool = scratch_pool;
proppatch->relpath = ctx->relpath;
proppatch->path = ctx->url;
- proppatch->commit = ctx->commit;
- proppatch->changed_props = ctx->changed_props;
- proppatch->removed_props = ctx->removed_props;
+ proppatch->commit_ctx = ctx->commit_ctx;
+ proppatch->prop_changes = ctx->prop_changes;
proppatch->base_revision = ctx->base_revision;
- SVN_ERR(proppatch_resource(proppatch, ctx->commit, ctx->pool));
+ SVN_ERR(proppatch_resource(ctx->commit_ctx->session,
+ proppatch, scratch_pool));
}
return SVN_NO_ERROR;
@@ -2233,26 +2037,16 @@ close_edit(void *edit_baton,
const char *merge_target =
ctx->activity_url ? ctx->activity_url : ctx->txn_url;
const svn_commit_info_t *commit_info;
- int response_code;
svn_error_t *err = NULL;
/* MERGE our activity */
- SVN_ERR(svn_ra_serf__run_merge(&commit_info, &response_code,
+ SVN_ERR(svn_ra_serf__run_merge(&commit_info,
ctx->session,
- ctx->session->conns[0],
merge_target,
ctx->lock_tokens,
ctx->keep_locks,
pool, pool));
- if (response_code != 200)
- {
- return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
- _("MERGE request failed: returned %d "
- "(during commit)"),
- response_code);
- }
-
ctx->txn_url = NULL; /* If HTTPv2, the txn is now done */
/* Inform the WC that we did a commit. */
@@ -2264,12 +2058,10 @@ close_edit(void *edit_baton,
{
svn_ra_serf__handler_t *handler;
- handler = apr_pcalloc(pool, sizeof(*handler));
- handler->handler_pool = pool;
+ handler = svn_ra_serf__create_handler(ctx->session, pool);
+
handler->method = "DELETE";
handler->path = ctx->activity_url;
- handler->conn = ctx->conn;
- handler->session = ctx->session;
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
@@ -2280,7 +2072,8 @@ close_edit(void *edit_baton,
err,
svn_ra_serf__context_run_one(handler, pool)));
- SVN_ERR_ASSERT(handler->sline.code == 204);
+ if (handler->sline.code != 204)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
}
SVN_ERR(err);
@@ -2305,14 +2098,13 @@ abort_edit(void *edit_baton,
serf_connection_reset(ctx->session->conns[0]->conn);
/* DELETE our aborted activity */
- handler = apr_pcalloc(pool, sizeof(*handler));
- handler->handler_pool = pool;
+ handler = svn_ra_serf__create_handler(ctx->session, pool);
+
handler->method = "DELETE";
- handler->conn = ctx->session->conns[0];
- handler->session = ctx->session;
handler->response_handler = svn_ra_serf__expect_empty_body;
handler->response_baton = handler;
+ handler->no_fail_on_http_failure_status = TRUE;
if (USING_HTTPV2_COMMIT_SUPPORT(ctx)) /* HTTP v2 */
handler->path = ctx->txn_url;
@@ -2326,14 +2118,15 @@ abort_edit(void *edit_baton,
404 if the activity wasn't found. */
if (handler->sline.code != 204
&& handler->sline.code != 403
- && handler->sline.code != 404
- )
+ && handler->sline.code != 404)
{
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("DELETE returned unexpected status: %d"),
- handler->sline.code);
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
}
+ /* Don't delete again if somebody aborts twice */
+ ctx->activity_url = NULL;
+ ctx->txn_url = NULL;
+
return SVN_NO_ERROR;
}
@@ -2360,7 +2153,6 @@ svn_ra_serf__get_commit_editor(svn_ra_session_t *ra_session,
ctx->pool = pool;
ctx->session = session;
- ctx->conn = session->conns[0];
ctx->revprop_table = svn_prop_hash_dup(revprop_table, pool);
@@ -2427,28 +2219,43 @@ svn_ra_serf__change_rev_prop(svn_ra_session_t *ra_session,
{
svn_ra_serf__session_t *session = ra_session->priv;
proppatch_context_t *proppatch_ctx;
- commit_context_t *commit;
const char *proppatch_target;
- const char *ns;
+ const svn_string_t *tmp_old_value;
+ svn_boolean_t atomic_capable = FALSE;
+ svn_prop_t *prop;
svn_error_t *err;
+ if (old_value_p || !value)
+ SVN_ERR(svn_ra_serf__has_capability(ra_session, &atomic_capable,
+ SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
+ pool));
+
if (old_value_p)
{
- svn_boolean_t capable;
- SVN_ERR(svn_ra_serf__has_capability(ra_session, &capable,
- SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
- pool));
-
/* How did you get past the same check in svn_ra_change_rev_prop2()? */
- SVN_ERR_ASSERT(capable);
+ SVN_ERR_ASSERT(atomic_capable);
}
+ else if (! value && atomic_capable)
+ {
+ svn_string_t *old_value;
+ /* mod_dav_svn doesn't report a failure when a property delete fails. The
+ atomic revprop change behavior is a nice workaround, to allow getting
+ access to the error anyway.
- commit = apr_pcalloc(pool, sizeof(*commit));
+ Somehow the mod_dav maintainers think that returning an error from
+ mod_dav's property delete is an RFC violation.
+ See https://issues.apache.org/bugzilla/show_bug.cgi?id=53525 */
- commit->pool = pool;
+ SVN_ERR(svn_ra_serf__rev_prop(ra_session, rev, name, &old_value,
+ pool));
- commit->session = session;
- commit->conn = session->conns[0];
+ if (!old_value)
+ return SVN_NO_ERROR; /* Nothing to delete */
+
+ /* The api expects a double const pointer. Let's make one */
+ tmp_old_value = old_value;
+ old_value_p = &tmp_old_value;
+ }
if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
{
@@ -2458,74 +2265,52 @@ svn_ra_serf__change_rev_prop(svn_ra_session_t *ra_session,
{
const char *vcc_url;
- SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, commit->session,
- commit->conn, pool));
+ SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, pool));
SVN_ERR(svn_ra_serf__fetch_dav_prop(&proppatch_target,
- commit->conn, vcc_url, rev,
- "href",
+ session, vcc_url, rev, "href",
pool, pool));
}
- if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
- {
- ns = SVN_DAV_PROP_NS_SVN;
- name += sizeof(SVN_PROP_PREFIX) - 1;
- }
- else
- {
- ns = SVN_DAV_PROP_NS_CUSTOM;
- }
-
/* PROPPATCH our log message and pass it along. */
proppatch_ctx = apr_pcalloc(pool, sizeof(*proppatch_ctx));
proppatch_ctx->pool = pool;
- proppatch_ctx->commit = commit;
+ proppatch_ctx->commit_ctx = NULL; /* No lock headers */
proppatch_ctx->path = proppatch_target;
- proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
- proppatch_ctx->removed_props = apr_hash_make(proppatch_ctx->pool);
- if (old_value_p)
- {
- proppatch_ctx->previous_changed_props = apr_hash_make(proppatch_ctx->pool);
- proppatch_ctx->previous_removed_props = apr_hash_make(proppatch_ctx->pool);
- }
+ proppatch_ctx->prop_changes = apr_hash_make(pool);
proppatch_ctx->base_revision = SVN_INVALID_REVNUM;
- if (old_value_p && *old_value_p)
- {
- svn_ra_serf__set_prop(proppatch_ctx->previous_changed_props,
- proppatch_ctx->path,
- ns, name, *old_value_p, proppatch_ctx->pool);
- }
- else if (old_value_p)
+ if (old_value_p)
{
- svn_string_t *dummy_value = svn_string_create_empty(proppatch_ctx->pool);
+ prop = apr_palloc(pool, sizeof (*prop));
- svn_ra_serf__set_prop(proppatch_ctx->previous_removed_props,
- proppatch_ctx->path,
- ns, name, dummy_value, proppatch_ctx->pool);
- }
+ prop->name = name;
+ prop->value = *old_value_p;
- if (value)
- {
- svn_ra_serf__set_prop(proppatch_ctx->changed_props, proppatch_ctx->path,
- ns, name, value, proppatch_ctx->pool);
+ proppatch_ctx->old_props = apr_hash_make(pool);
+ svn_hash_sets(proppatch_ctx->old_props, prop->name, prop);
}
- else
+
+ prop = apr_palloc(pool, sizeof (*prop));
+
+ prop->name = name;
+ prop->value = value;
+ svn_hash_sets(proppatch_ctx->prop_changes, prop->name, prop);
+
+ err = proppatch_resource(session, proppatch_ctx, pool);
+
+ /* Use specific error code for old property value mismatch.
+ Use loop to provide the right result with tracing */
+ if (err && err->apr_err == SVN_ERR_RA_DAV_PRECONDITION_FAILED)
{
- value = svn_string_create_empty(proppatch_ctx->pool);
+ svn_error_t *e = err;
- svn_ra_serf__set_prop(proppatch_ctx->removed_props, proppatch_ctx->path,
- ns, name, value, proppatch_ctx->pool);
+ while (e && e->apr_err == SVN_ERR_RA_DAV_PRECONDITION_FAILED)
+ {
+ e->apr_err = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH;
+ e = e->child;
+ }
}
- err = proppatch_resource(proppatch_ctx, commit, proppatch_ctx->pool);
- if (err)
- return
- svn_error_create
- (SVN_ERR_RA_DAV_REQUEST_FAILED, err,
- _("DAV request failed; it's possible that the repository's "
- "pre-revprop-change hook either failed or is non-existent"));
-
- return SVN_NO_ERROR;
+ return svn_error_trace(err);
}
diff --git a/subversion/libsvn_ra_serf/eagain_bucket.c b/subversion/libsvn_ra_serf/eagain_bucket.c
new file mode 100644
index 0000000..16387be
--- /dev/null
+++ b/subversion/libsvn_ra_serf/eagain_bucket.c
@@ -0,0 +1,122 @@
+/*
+ * eagain_bucket.c : a serf bucket that handles slowing down data
+ * for specific readers that would have unwanted
+ * behavior if they read everything too fast
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include <serf.h>
+#include <serf_bucket_util.h>
+
+#include "svn_private_config.h"
+
+#include "ra_serf.h"
+
+typedef struct eagain_baton_t
+{
+ const char *data;
+ apr_size_t remaining;
+} eagain_baton_t;
+
+static apr_status_t
+eagain_bucket_read(serf_bucket_t *bucket,
+ apr_size_t requested,
+ const char **data,
+ apr_size_t *len)
+{
+ eagain_baton_t *eab = bucket->data;
+
+ if (eab->remaining > 0)
+ {
+ *data = eab->data;
+ if (requested > eab->remaining || requested == SERF_READ_ALL_AVAIL)
+ {
+ *len = eab->remaining;
+ eab->data = NULL;
+ eab->remaining = 0;
+ }
+ else
+ {
+ *len = requested;
+ eab->data += requested;
+ eab->remaining -= requested;
+ }
+
+ if (eab->remaining)
+ return APR_SUCCESS;
+ }
+
+ return APR_EAGAIN;
+}
+
+
+static apr_status_t
+eagain_bucket_readline(serf_bucket_t *bucket,
+ int acceptable,
+ int *found,
+ const char **data,
+ apr_size_t *len)
+{
+ /* ### for now, we know callers won't use this function. */
+ svn_error_clear(svn_error__malfunction(TRUE, __FILE__, __LINE__,
+ "Not implemented."));
+ return APR_ENOTIMPL;
+}
+
+
+static apr_status_t
+eagain_bucket_peek(serf_bucket_t *bucket,
+ const char **data,
+ apr_size_t *len)
+{
+ const eagain_baton_t *eab = bucket->data;
+
+ *data = eab->data ? eab->data : "";
+ *len = eab->remaining;
+
+ return APR_SUCCESS;
+}
+
+
+static const serf_bucket_type_t delay_bucket_vtable = {
+ "BUF-EAGAIN",
+ eagain_bucket_read,
+ eagain_bucket_readline,
+ serf_default_read_iovec,
+ serf_default_read_for_sendfile,
+ serf_default_read_bucket,
+ eagain_bucket_peek,
+ serf_default_destroy_and_data,
+};
+
+
+serf_bucket_t *
+svn_ra_serf__create_bucket_with_eagain(const char *data,
+ apr_size_t len,
+ serf_bucket_alloc_t *allocator)
+{
+ eagain_baton_t *eab;
+
+ eab = serf_bucket_mem_alloc(allocator, sizeof(*eab));
+ eab->data = data;
+ eab->remaining = len;
+
+ return serf_bucket_create(&delay_bucket_vtable, allocator, eab);
+}
diff --git a/subversion/libsvn_ra_serf/get_deleted_rev.c b/subversion/libsvn_ra_serf/get_deleted_rev.c
index 40f6b1d..624854d 100644
--- a/subversion/libsvn_ra_serf/get_deleted_rev.c
+++ b/subversion/libsvn_ra_serf/get_deleted_rev.c
@@ -36,7 +36,7 @@
* This enum represents the current state of our XML parsing for a REPORT.
*/
enum drev_state_e {
- INITIAL = 0,
+ INITIAL = XML_STATE_INITIAL,
REPORT,
VERSION_NAME
};
@@ -75,11 +75,13 @@ getdrev_closed(svn_ra_serf__xml_estate_t *xes,
apr_pool_t *scratch_pool)
{
drev_context_t *drev_ctx = baton;
+ apr_int64_t rev;
SVN_ERR_ASSERT(leaving_state == VERSION_NAME);
SVN_ERR_ASSERT(cdata != NULL);
- *drev_ctx->revision_deleted = SVN_STR_TO_REV(cdata->data);
+ SVN_ERR(svn_cstring_atoi64(&rev, cdata->data));
+ *drev_ctx->revision_deleted = (svn_revnum_t)rev;
return SVN_NO_ERROR;
}
@@ -90,7 +92,8 @@ static svn_error_t *
create_getdrev_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
serf_bucket_t *buckets;
drev_context_t *drev_ctx = baton;
@@ -101,7 +104,7 @@ create_getdrev_body(serf_bucket_t **body_bkt,
"S:get-deleted-rev-report",
"xmlns:S", SVN_XML_NAMESPACE,
"xmlns:D", "DAV:",
- NULL, NULL);
+ SVN_VA_NULL);
svn_ra_serf__add_tag_buckets(buckets,
"S:path", drev_ctx->path,
@@ -146,23 +149,20 @@ svn_ra_serf__get_deleted_rev(svn_ra_session_t *session,
drev_ctx->revision_deleted = revision_deleted;
SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
- ras, NULL /* conn */,
- NULL /* url */, peg_revision,
+ ras, NULL /* url */, peg_revision,
pool, pool));
xmlctx = svn_ra_serf__xml_context_create(getdrev_ttable,
NULL, getdrev_closed, NULL,
drev_ctx,
pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
+ handler = svn_ra_serf__create_expat_handler(ras, xmlctx, NULL, pool);
handler->method = "REPORT";
handler->path = req_url;
handler->body_type = "text/xml";
handler->body_delegate = create_getdrev_body;
handler->body_delegate_baton = drev_ctx;
- handler->conn = ras->conns[0];
- handler->session = ras;
err = svn_ra_serf__context_run_one(handler, pool);
diff --git a/subversion/libsvn_ra_serf/get_file.c b/subversion/libsvn_ra_serf/get_file.c
new file mode 100644
index 0000000..cb63b7d
--- /dev/null
+++ b/subversion/libsvn_ra_serf/get_file.c
@@ -0,0 +1,425 @@
+/*
+ * get_file.c : entry point for update RA functions for ra_serf
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+
+
+#define APR_WANT_STRFUNC
+#include <apr_version.h>
+#include <apr_want.h>
+
+#include <apr_uri.h>
+
+#include <serf.h>
+
+#include "svn_private_config.h"
+#include "svn_hash.h"
+#include "svn_pools.h"
+#include "svn_ra.h"
+#include "svn_delta.h"
+#include "svn_path.h"
+#include "svn_props.h"
+
+#include "private/svn_dep_compat.h"
+#include "private/svn_string_private.h"
+
+#include "ra_serf.h"
+#include "../libsvn_ra/ra_loader.h"
+
+
+
+
+/*
+ * This structure represents a single request to GET (fetch) a file with
+ * its associated Serf session/connection.
+ */
+typedef struct stream_ctx_t {
+
+ /* The handler representing this particular fetch. */
+ svn_ra_serf__handler_t *handler;
+
+ /* Have we read our response headers yet? */
+ svn_boolean_t read_headers;
+
+ svn_boolean_t using_compression;
+
+ /* This flag is set when our response is aborted before we reach the
+ * end and we decide to requeue this request.
+ */
+ svn_boolean_t aborted_read;
+ apr_off_t aborted_read_size;
+
+ /* This is the amount of data that we have read so far. */
+ apr_off_t read_size;
+
+ /* If we're writing this file to a stream, this will be non-NULL. */
+ svn_stream_t *result_stream;
+
+} stream_ctx_t;
+
+
+
+/** Routines called when we are fetching a file */
+
+static svn_error_t *
+headers_fetch(serf_bucket_t *headers,
+ void *baton,
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
+{
+ stream_ctx_t *fetch_ctx = baton;
+
+ if (fetch_ctx->using_compression)
+ {
+ serf_bucket_headers_setn(headers, "Accept-Encoding", "gzip");
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+cancel_fetch(serf_request_t *request,
+ serf_bucket_t *response,
+ int status_code,
+ void *baton)
+{
+ stream_ctx_t *fetch_ctx = baton;
+
+ /* Uh-oh. Our connection died on us.
+ *
+ * The core ra_serf layer will requeue our request - we just need to note
+ * that we got cut off in the middle of our song.
+ */
+ if (!response)
+ {
+ /* If we already started the fetch and opened the file handle, we need
+ * to hold subsequent read() ops until we get back to where we were
+ * before the close and we can then resume the textdelta() calls.
+ */
+ if (fetch_ctx->read_headers)
+ {
+ if (!fetch_ctx->aborted_read && fetch_ctx->read_size)
+ {
+ fetch_ctx->aborted_read = TRUE;
+ fetch_ctx->aborted_read_size = fetch_ctx->read_size;
+ }
+ fetch_ctx->read_size = 0;
+ }
+
+ return SVN_NO_ERROR;
+ }
+
+ /* We have no idea what went wrong. */
+ SVN_ERR_MALFUNCTION();
+}
+
+
+/* Helper svn_ra_serf__get_file(). Attempts to fetch file contents
+ * using SESSION->wc_callbacks->get_wc_contents() if sha1 property is
+ * present in PROPS.
+ *
+ * Sets *FOUND_P to TRUE if file contents was successfuly fetched.
+ *
+ * Performs all temporary allocations in POOL.
+ */
+static svn_error_t *
+try_get_wc_contents(svn_boolean_t *found_p,
+ svn_ra_serf__session_t *session,
+ const char *sha1_checksum_prop,
+ svn_stream_t *dst_stream,
+ apr_pool_t *pool)
+{
+ svn_checksum_t *checksum;
+ svn_stream_t *wc_stream;
+ svn_error_t *err;
+
+ /* No contents found by default. */
+ *found_p = FALSE;
+
+ if (!session->wc_callbacks->get_wc_contents
+ || sha1_checksum_prop == NULL)
+ {
+ /* Nothing to do. */
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1,
+ sha1_checksum_prop, pool));
+
+ err = session->wc_callbacks->get_wc_contents(
+ session->wc_callback_baton, &wc_stream, checksum, pool);
+
+ if (err)
+ {
+ svn_error_clear(err);
+
+ /* Ignore errors for now. */
+ return SVN_NO_ERROR;
+ }
+
+ if (wc_stream)
+ {
+ SVN_ERR(svn_stream_copy3(wc_stream,
+ svn_stream_disown(dst_stream, pool),
+ NULL, NULL, pool));
+ *found_p = TRUE;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* -----------------------------------------------------------------------
+ svn_ra_get_file() specific */
+
+/* Implements svn_ra_serf__response_handler_t */
+static svn_error_t *
+handle_stream(serf_request_t *request,
+ serf_bucket_t *response,
+ void *handler_baton,
+ apr_pool_t *pool)
+{
+ stream_ctx_t *fetch_ctx = handler_baton;
+ apr_status_t status;
+
+ if (fetch_ctx->handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(fetch_ctx->handler));
+
+ while (1)
+ {
+ const char *data;
+ apr_size_t len;
+
+ status = serf_bucket_read(response, 8000, &data, &len);
+ if (SERF_BUCKET_READ_ERROR(status))
+ {
+ return svn_ra_serf__wrap_err(status, NULL);
+ }
+
+ fetch_ctx->read_size += len;
+
+ if (fetch_ctx->aborted_read)
+ {
+ apr_off_t skip;
+
+ /* We haven't caught up to where we were before. */
+ if (fetch_ctx->read_size < fetch_ctx->aborted_read_size)
+ {
+ /* Eek. What did the file shrink or something? */
+ if (APR_STATUS_IS_EOF(status))
+ {
+ SVN_ERR_MALFUNCTION();
+ }
+
+ /* Skip on to the next iteration of this loop. */
+ if (APR_STATUS_IS_EAGAIN(status))
+ {
+ return svn_ra_serf__wrap_err(status, NULL);
+ }
+ continue;
+ }
+
+ /* Woo-hoo. We're back. */
+ fetch_ctx->aborted_read = FALSE;
+
+ /* Increment data and len by the difference. */
+ skip = len - (fetch_ctx->read_size - fetch_ctx->aborted_read_size);
+ data += skip;
+ len -= (apr_size_t)skip;
+ }
+
+ if (len)
+ {
+ apr_size_t written_len;
+
+ written_len = len;
+
+ SVN_ERR(svn_stream_write(fetch_ctx->result_stream, data,
+ &written_len));
+ }
+
+ if (status)
+ {
+ return svn_ra_serf__wrap_err(status, NULL);
+ }
+ }
+ /* not reached */
+}
+
+/* Baton for get_file_prop_cb */
+struct file_prop_baton_t
+{
+ apr_pool_t *result_pool;
+ svn_node_kind_t kind;
+ apr_hash_t *props;
+ const char *sha1_checksum;
+};
+
+/* Implements svn_ra_serf__prop_func_t for svn_ra_serf__get_file */
+static svn_error_t *
+get_file_prop_cb(void *baton,
+ const char *path,
+ const char *ns,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *scratch_pool)
+{
+ struct file_prop_baton_t *fb = baton;
+ const char *svn_name;
+
+ if (strcmp(ns, "DAV:") == 0 && strcmp(name, "resourcetype") == 0)
+ {
+ const char *val = value->data;
+
+ if (strcmp(val, "collection") == 0)
+ fb->kind = svn_node_dir;
+ else
+ fb->kind = svn_node_file;
+
+ return SVN_NO_ERROR;
+ }
+ else if (strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0
+ && strcmp(name, "sha1-checksum") == 0)
+ {
+ fb->sha1_checksum = apr_pstrdup(fb->result_pool, value->data);
+ }
+
+ if (!fb->props)
+ return SVN_NO_ERROR;
+
+ svn_name = svn_ra_serf__svnname_from_wirename(ns, name, fb->result_pool);
+ if (svn_name)
+ {
+ svn_hash_sets(fb->props, svn_name,
+ svn_string_dup(value, fb->result_pool));
+ }
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra_serf__get_file(svn_ra_session_t *ra_session,
+ const char *path,
+ svn_revnum_t revision,
+ svn_stream_t *stream,
+ svn_revnum_t *fetched_rev,
+ apr_hash_t **props,
+ apr_pool_t *pool)
+{
+ svn_ra_serf__session_t *session = ra_session->priv;
+ const char *fetch_url;
+ const svn_ra_serf__dav_props_t *which_props;
+ svn_ra_serf__handler_t *propfind_handler;
+ struct file_prop_baton_t fb;
+
+ /* Fetch properties. */
+
+ fetch_url = svn_path_url_add_component2(session->session_url.path, path, pool);
+
+ /* The simple case is if we want HEAD - then a GET on the fetch_url is fine.
+ *
+ * Otherwise, we need to get the baseline version for this particular
+ * revision and then fetch that file.
+ */
+ if (SVN_IS_VALID_REVNUM(revision) || fetched_rev)
+ {
+ SVN_ERR(svn_ra_serf__get_stable_url(&fetch_url, fetched_rev,
+ session,
+ fetch_url, revision,
+ pool, pool));
+ revision = SVN_INVALID_REVNUM;
+ }
+ /* REVISION is always SVN_INVALID_REVNUM */
+ SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision));
+
+ if (props)
+ which_props = all_props;
+ else if (stream && session->wc_callbacks->get_wc_contents)
+ which_props = type_and_checksum_props;
+ else
+ which_props = check_path_props;
+
+ fb.result_pool = pool;
+ fb.props = props ? apr_hash_make(pool) : NULL;
+ fb.kind = svn_node_unknown;
+ fb.sha1_checksum = NULL;
+
+ SVN_ERR(svn_ra_serf__create_propfind_handler(&propfind_handler, session,
+ fetch_url, SVN_INVALID_REVNUM,
+ "0", which_props,
+ get_file_prop_cb, &fb,
+ pool));
+
+ SVN_ERR(svn_ra_serf__context_run_one(propfind_handler, pool));
+
+ /* Verify that resource type is not collection. */
+ if (fb.kind != svn_node_file)
+ {
+ return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL,
+ _("Can't get text contents of a directory"));
+ }
+
+ if (props)
+ *props = fb.props;
+
+ if (stream)
+ {
+ svn_boolean_t found;
+ SVN_ERR(try_get_wc_contents(&found, session, fb.sha1_checksum, stream, pool));
+
+ /* No contents found in the WC, let's fetch from server. */
+ if (!found)
+ {
+ stream_ctx_t *stream_ctx;
+ svn_ra_serf__handler_t *handler;
+
+ /* Create the fetch context. */
+ stream_ctx = apr_pcalloc(pool, sizeof(*stream_ctx));
+ stream_ctx->result_stream = stream;
+ stream_ctx->using_compression = session->using_compression;
+
+ handler = svn_ra_serf__create_handler(session, pool);
+
+ handler->method = "GET";
+ handler->path = fetch_url;
+
+ handler->custom_accept_encoding = TRUE;
+ handler->no_dav_headers = TRUE;
+
+ handler->header_delegate = headers_fetch;
+ handler->header_delegate_baton = stream_ctx;
+
+ handler->response_handler = handle_stream;
+ handler->response_baton = stream_ctx;
+
+ handler->response_error = cancel_fetch;
+ handler->response_error_baton = stream_ctx;
+
+ stream_ctx->handler = handler;
+
+ SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
+
+ if (handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/libsvn_ra_serf/get_lock.c b/subversion/libsvn_ra_serf/get_lock.c
new file mode 100644
index 0000000..24d7100
--- /dev/null
+++ b/subversion/libsvn_ra_serf/get_lock.c
@@ -0,0 +1,337 @@
+/*
+ * get_lock.c : obtain single lock information functions for ra_serf
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+
+
+#include <apr_uri.h>
+#include <serf.h>
+
+#include "svn_dav.h"
+#include "svn_pools.h"
+#include "svn_ra.h"
+
+#include "../libsvn_ra/ra_loader.h"
+#include "svn_config.h"
+#include "svn_path.h"
+#include "svn_time.h"
+#include "svn_private_config.h"
+
+#include "ra_serf.h"
+
+
+/*
+ * This enum represents the current state of our XML parsing for a REPORT.
+ */
+enum {
+ INITIAL = 0,
+ MULTISTATUS,
+ RESPONSE,
+ PROPSTAT,
+ PROP,
+ LOCK_DISCOVERY,
+ ACTIVE_LOCK,
+ LOCK_TYPE,
+ LOCK_SCOPE,
+ DEPTH,
+ TIMEOUT,
+ LOCK_TOKEN,
+ OWNER,
+ HREF
+};
+
+typedef struct lock_info_t {
+ apr_pool_t *pool;
+
+ const char *path;
+
+ svn_lock_t *lock;
+
+ svn_boolean_t read_headers;
+
+ svn_ra_serf__handler_t *handler;
+
+ /* The expat handler. We wrap this to do a bit more work. */
+ svn_ra_serf__response_handler_t inner_handler;
+ void *inner_baton;
+
+} lock_info_t;
+
+#define D_ "DAV:"
+#define S_ SVN_XML_NAMESPACE
+static const svn_ra_serf__xml_transition_t locks_ttable[] = {
+ /* The INITIAL state can transition into D:prop (LOCK) or
+ to D:multistatus (PROPFIND) */
+ { INITIAL, D_, "multistatus", MULTISTATUS,
+ FALSE, { NULL }, FALSE },
+
+ { MULTISTATUS, D_, "response", RESPONSE,
+ FALSE, { NULL }, FALSE },
+
+ { RESPONSE, D_, "propstat", PROPSTAT,
+ FALSE, { NULL }, FALSE },
+
+ { PROPSTAT, D_, "prop", PROP,
+ FALSE, { NULL }, FALSE },
+
+ { PROP, D_, "lockdiscovery", LOCK_DISCOVERY,
+ FALSE, { NULL }, FALSE },
+
+ { LOCK_DISCOVERY, D_, "activelock", ACTIVE_LOCK,
+ FALSE, { NULL }, FALSE },
+
+#if 0
+ /* ### we don't really need to parse locktype/lockscope. we know what
+ ### the values are going to be. we *could* validate that the only
+ ### possible children are D:write and D:exclusive. we'd need to
+ ### modify the state transition to tell us about all children
+ ### (ie. maybe support "*" for the name) and then validate. but it
+ ### just isn't important to validate, so disable this for now... */
+
+ { ACTIVE_LOCK, D_, "locktype", LOCK_TYPE,
+ FALSE, { NULL }, FALSE },
+
+ { LOCK_TYPE, D_, "write", WRITE,
+ FALSE, { NULL }, TRUE },
+
+ { ACTIVE_LOCK, D_, "lockscope", LOCK_SCOPE,
+ FALSE, { NULL }, FALSE },
+
+ { LOCK_SCOPE, D_, "exclusive", EXCLUSIVE,
+ FALSE, { NULL }, TRUE },
+#endif /* 0 */
+
+ { ACTIVE_LOCK, D_, "timeout", TIMEOUT,
+ TRUE, { NULL }, TRUE },
+
+ { ACTIVE_LOCK, D_, "locktoken", LOCK_TOKEN,
+ FALSE, { NULL }, FALSE },
+
+ { LOCK_TOKEN, D_, "href", HREF,
+ TRUE, { NULL }, TRUE },
+
+ { ACTIVE_LOCK, D_, "owner", OWNER,
+ TRUE, { NULL }, TRUE },
+
+ /* ACTIVE_LOCK has a D:depth child, but we can ignore that. */
+
+ { 0 }
+};
+
+static const int locks_expected_status[] = {
+ 207,
+ 0
+};
+
+/* Conforms to svn_ra_serf__xml_closed_t */
+static svn_error_t *
+locks_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)
+{
+ lock_info_t *lock_ctx = baton;
+
+ if (leaving_state == TIMEOUT)
+ {
+ if (strcasecmp(cdata->data, "Infinite") == 0)
+ lock_ctx->lock->expiration_date = 0;
+ else if (strncasecmp(cdata->data, "Second-", 7) == 0)
+ {
+ unsigned n;
+ SVN_ERR(svn_cstring_atoui(&n, cdata->data+7));
+
+ lock_ctx->lock->expiration_date = apr_time_now() +
+ apr_time_from_sec(n);
+ }
+ else
+ return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+ _("Invalid LOCK timeout value '%s'"),
+ cdata->data);
+ }
+ else if (leaving_state == HREF)
+ {
+ if (cdata->len)
+ {
+ char *buf = apr_pstrmemdup(lock_ctx->pool, cdata->data, cdata->len);
+
+ apr_collapse_spaces(buf, buf);
+ lock_ctx->lock->token = buf;
+ }
+ }
+ else if (leaving_state == OWNER)
+ {
+ if (cdata->len)
+ {
+ lock_ctx->lock->comment = apr_pstrmemdup(lock_ctx->pool,
+ cdata->data, cdata->len);
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Implements svn_ra_serf__response_handler_t */
+static svn_error_t *
+handle_lock(serf_request_t *request,
+ serf_bucket_t *response,
+ void *handler_baton,
+ apr_pool_t *pool)
+{
+ lock_info_t *ctx = handler_baton;
+
+ if (!ctx->read_headers)
+ {
+ serf_bucket_t *headers;
+ const char *val;
+
+ headers = serf_bucket_response_get_headers(response);
+
+ val = serf_bucket_headers_get(headers, SVN_DAV_LOCK_OWNER_HEADER);
+ if (val)
+ {
+ ctx->lock->owner = apr_pstrdup(ctx->pool, val);
+ }
+
+ val = serf_bucket_headers_get(headers, SVN_DAV_CREATIONDATE_HEADER);
+ if (val)
+ {
+ SVN_ERR(svn_time_from_cstring(&ctx->lock->creation_date, val,
+ ctx->pool));
+ }
+
+ ctx->read_headers = TRUE;
+ }
+
+ return ctx->inner_handler(request, response, ctx->inner_baton, pool);
+}
+
+/* Implements svn_ra_serf__request_body_delegate_t */
+static svn_error_t *
+create_getlock_body(serf_bucket_t **body_bkt,
+ void *baton,
+ serf_bucket_alloc_t *alloc,
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
+{
+ serf_bucket_t *buckets;
+
+ buckets = serf_bucket_aggregate_create(alloc);
+
+ svn_ra_serf__add_xml_header_buckets(buckets, alloc);
+ svn_ra_serf__add_open_tag_buckets(buckets, alloc, "propfind",
+ "xmlns", "DAV:",
+ SVN_VA_NULL);
+ svn_ra_serf__add_open_tag_buckets(buckets, alloc, "prop", SVN_VA_NULL);
+ svn_ra_serf__add_empty_tag_buckets(buckets, alloc,
+ "lockdiscovery", SVN_VA_NULL);
+ svn_ra_serf__add_close_tag_buckets(buckets, alloc, "prop");
+ svn_ra_serf__add_close_tag_buckets(buckets, alloc, "propfind");
+
+ *body_bkt = buckets;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t*
+setup_getlock_headers(serf_bucket_t *headers,
+ void *baton,
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
+{
+ serf_bucket_headers_setn(headers, "Depth", "0");
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra_serf__get_lock(svn_ra_session_t *ra_session,
+ svn_lock_t **lock,
+ const char *path,
+ apr_pool_t *result_pool)
+{
+ svn_ra_serf__session_t *session = ra_session->priv;
+ svn_ra_serf__handler_t *handler;
+ svn_ra_serf__xml_context_t *xmlctx;
+ apr_pool_t *scratch_pool = svn_pool_create(result_pool);
+ lock_info_t *lock_ctx;
+ const char *req_url;
+ svn_error_t *err;
+
+ req_url = svn_path_url_add_component2(session->session_url.path, path,
+ scratch_pool);
+
+ lock_ctx = apr_pcalloc(scratch_pool, sizeof(*lock_ctx));
+ lock_ctx->pool = result_pool;
+ lock_ctx->path = req_url;
+ lock_ctx->lock = svn_lock_create(result_pool);
+ lock_ctx->lock->path = apr_pstrdup(result_pool, path);
+
+ xmlctx = svn_ra_serf__xml_context_create(locks_ttable,
+ NULL, locks_closed, NULL,
+ lock_ctx,
+ scratch_pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx,
+ locks_expected_status,
+ scratch_pool);
+
+ handler->method = "PROPFIND";
+ handler->path = req_url;
+ handler->body_type = "text/xml";
+
+ handler->body_delegate = create_getlock_body;
+ handler->body_delegate_baton = lock_ctx;
+
+ handler->header_delegate = setup_getlock_headers;
+ handler->header_delegate_baton = lock_ctx;
+
+ handler->no_dav_headers = TRUE;
+
+ lock_ctx->inner_handler = handler->response_handler;
+ lock_ctx->inner_baton = handler->response_baton;
+ handler->response_handler = handle_lock;
+ handler->response_baton = lock_ctx;
+
+ lock_ctx->handler = handler;
+
+ err = svn_ra_serf__context_run_one(handler, scratch_pool);
+
+ if ((err && (handler->sline.code == 500 || handler->sline.code == 501))
+ || svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE))
+ return svn_error_trace(
+ svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err,
+ _("Server does not support locking features")));
+ else if (svn_error_find_cause(err, SVN_ERR_FS_NOT_FOUND))
+ svn_error_clear(err); /* Behave like the other RA layers */
+ else if (handler->sline.code != 207)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
+
+ if (lock_ctx->lock && lock_ctx->lock->token)
+ *lock = lock_ctx->lock;
+ else
+ *lock = NULL;
+
+ svn_pool_destroy(scratch_pool);
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/libsvn_ra_serf/getdate.c b/subversion/libsvn_ra_serf/getdate.c
index cc1014e..30d57f3 100644
--- a/subversion/libsvn_ra_serf/getdate.c
+++ b/subversion/libsvn_ra_serf/getdate.c
@@ -44,7 +44,7 @@
* This enum represents the current state of our XML parsing for a REPORT.
*/
enum date_state_e {
- INITIAL = 0,
+ INITIAL = XML_STATE_INITIAL,
REPORT,
VERSION_NAME
};
@@ -82,11 +82,14 @@ date_closed(svn_ra_serf__xml_estate_t *xes,
apr_pool_t *scratch_pool)
{
date_context_t *date_ctx = baton;
+ apr_int64_t rev;
SVN_ERR_ASSERT(leaving_state == VERSION_NAME);
SVN_ERR_ASSERT(cdata != NULL);
- *date_ctx->revision = SVN_STR_TO_REV(cdata->data);
+ SVN_ERR(svn_cstring_atoi64(&rev, cdata->data));
+
+ *date_ctx->revision = (svn_revnum_t)rev;
return SVN_NO_ERROR;
}
@@ -97,7 +100,8 @@ static svn_error_t *
create_getdate_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
serf_bucket_t *buckets;
date_context_t *date_ctx = baton;
@@ -107,7 +111,7 @@ create_getdate_body(serf_bucket_t **body_bkt,
svn_ra_serf__add_open_tag_buckets(buckets, alloc, "S:dated-rev-report",
"xmlns:S", SVN_XML_NAMESPACE,
"xmlns:D", "DAV:",
- NULL);
+ SVN_VA_NULL);
svn_ra_serf__add_tag_buckets(buckets,
"D:" SVN_DAV__CREATIONDATE,
@@ -131,40 +135,37 @@ svn_ra_serf__get_dated_revision(svn_ra_session_t *ra_session,
svn_ra_serf__handler_t *handler;
svn_ra_serf__xml_context_t *xmlctx;
const char *report_target;
- svn_error_t *err;
date_ctx = apr_palloc(pool, sizeof(*date_ctx));
date_ctx->time = tm;
date_ctx->revision = revision;
- SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
+ SVN_ERR(svn_ra_serf__report_resource(&report_target, session, pool));
xmlctx = svn_ra_serf__xml_context_create(date_ttable,
NULL, date_closed, NULL,
date_ctx,
pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool);
handler->method = "REPORT";
handler->path = report_target;
handler->body_type = "text/xml";
- handler->conn = session->conns[0];
- handler->session = session;
handler->body_delegate = create_getdate_body;
handler->body_delegate_baton = date_ctx;
*date_ctx->revision = SVN_INVALID_REVNUM;
- err = svn_ra_serf__context_run_one(handler, pool);
+ SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
- SVN_ERR(svn_error_compose_create(
- svn_ra_serf__error_on_status(handler->sline,
- report_target,
- handler->location),
- err));
+ if (handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
- SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(*revision));
+ if (!SVN_IS_VALID_REVNUM(*revision))
+ return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
+ _("The REPORT response did not include "
+ "the requested properties"));
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_ra_serf/getlocations.c b/subversion/libsvn_ra_serf/getlocations.c
index 1ae4f82..063272e 100644
--- a/subversion/libsvn_ra_serf/getlocations.c
+++ b/subversion/libsvn_ra_serf/getlocations.c
@@ -43,7 +43,7 @@
* This enum represents the current state of our XML parsing for a REPORT.
*/
enum loc_state_e {
- INITIAL = 0,
+ INITIAL = XML_STATE_INITIAL,
REPORT,
LOCATION
};
@@ -94,7 +94,12 @@ getloc_closed(svn_ra_serf__xml_estate_t *xes,
path = svn_hash_gets(attrs, "path");
if (revstr != NULL && path != NULL)
{
- svn_revnum_t rev = SVN_STR_TO_REV(revstr);
+ apr_int64_t rev_val;
+ svn_revnum_t rev;
+
+ SVN_ERR(svn_cstring_atoi64(&rev_val, revstr));
+ rev = (svn_revnum_t)rev_val;
+
apr_hash_set(loc_ctx->paths,
apr_pmemdup(loc_ctx->pool, &rev, sizeof(rev)), sizeof(rev),
apr_pstrdup(loc_ctx->pool, path));
@@ -109,7 +114,8 @@ static svn_error_t *
create_get_locations_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
serf_bucket_t *buckets;
loc_context_t *loc_ctx = baton;
@@ -121,7 +127,7 @@ create_get_locations_body(serf_bucket_t **body_bkt,
"S:get-locations",
"xmlns:S", SVN_XML_NAMESPACE,
"xmlns:D", "DAV:",
- NULL);
+ SVN_VA_NULL);
svn_ra_serf__add_tag_buckets(buckets,
"S:path", loc_ctx->path,
@@ -159,7 +165,6 @@ svn_ra_serf__get_locations(svn_ra_session_t *ra_session,
svn_ra_serf__handler_t *handler;
svn_ra_serf__xml_context_t *xmlctx;
const char *req_url;
- svn_error_t *err;
loc_ctx = apr_pcalloc(pool, sizeof(*loc_ctx));
loc_ctx->pool = pool;
@@ -171,31 +176,26 @@ svn_ra_serf__get_locations(svn_ra_session_t *ra_session,
*locations = loc_ctx->paths;
SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
- session, NULL /* conn */,
- NULL /* url */, peg_revision,
+ session, NULL /* url */, peg_revision,
pool, pool));
xmlctx = svn_ra_serf__xml_context_create(getloc_ttable,
NULL, getloc_closed, NULL,
loc_ctx,
pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool);
handler->method = "REPORT";
handler->path = req_url;
handler->body_delegate = create_get_locations_body;
handler->body_delegate_baton = loc_ctx;
handler->body_type = "text/xml";
- handler->conn = session->conns[0];
- handler->session = session;
- err = svn_ra_serf__context_run_one(handler, pool);
+ SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
- SVN_ERR(svn_error_compose_create(
- svn_ra_serf__error_on_status(handler->sline,
- req_url,
- handler->location),
- err));
+ SVN_ERR(svn_ra_serf__error_on_status(handler->sline,
+ handler->path,
+ handler->location));
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_ra_serf/getlocationsegments.c b/subversion/libsvn_ra_serf/getlocationsegments.c
index e5c58a9..884ab5d 100644
--- a/subversion/libsvn_ra_serf/getlocationsegments.c
+++ b/subversion/libsvn_ra_serf/getlocationsegments.c
@@ -52,8 +52,8 @@ typedef struct gls_context_t {
} gls_context_t;
-enum {
- INITIAL = 0,
+enum locseg_state_e {
+ INITIAL = XML_STATE_INITIAL,
REPORT,
SEGMENT
};
@@ -84,6 +84,8 @@ gls_closed(svn_ra_serf__xml_estate_t *xes,
const char *path;
const char *start_str;
const char *end_str;
+ apr_int64_t start_val;
+ apr_int64_t end_val;
svn_location_segment_t segment;
SVN_ERR_ASSERT(leaving_state == SEGMENT);
@@ -95,9 +97,12 @@ gls_closed(svn_ra_serf__xml_estate_t *xes,
/* The transition table said these must exist. */
SVN_ERR_ASSERT(start_str && end_str);
+ SVN_ERR(svn_cstring_atoi64(&start_val, start_str));
+ SVN_ERR(svn_cstring_atoi64(&end_val, end_str));
+
segment.path = path; /* may be NULL */
- segment.range_start = SVN_STR_TO_REV(start_str);
- segment.range_end = SVN_STR_TO_REV(end_str);
+ segment.range_start = (svn_revnum_t)start_val;
+ segment.range_end = (svn_revnum_t)end_val;
SVN_ERR(gls_ctx->receiver(&segment, gls_ctx->receiver_baton, scratch_pool));
return SVN_NO_ERROR;
@@ -109,7 +114,8 @@ static svn_error_t *
create_gls_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
serf_bucket_t *buckets;
gls_context_t *gls_ctx = baton;
@@ -119,7 +125,7 @@ create_gls_body(serf_bucket_t **body_bkt,
svn_ra_serf__add_open_tag_buckets(buckets, alloc,
"S:get-location-segments",
"xmlns:S", SVN_XML_NAMESPACE,
- NULL);
+ SVN_VA_NULL);
svn_ra_serf__add_tag_buckets(buckets,
"S:path", gls_ctx->path,
@@ -173,31 +179,29 @@ svn_ra_serf__get_location_segments(svn_ra_session_t *ra_session,
gls_ctx->receiver_baton = receiver_baton;
SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
- session, NULL /* conn */,
- NULL /* url */, peg_revision,
+ session, NULL /* url */, peg_revision,
pool, pool));
xmlctx = svn_ra_serf__xml_context_create(gls_ttable,
NULL, gls_closed, NULL,
gls_ctx,
pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool);
handler->method = "REPORT";
handler->path = req_url;
handler->body_delegate = create_gls_body;
handler->body_delegate_baton = gls_ctx;
handler->body_type = "text/xml";
- handler->conn = session->conns[0];
- handler->session = session;
err = svn_ra_serf__context_run_one(handler, pool);
- err = svn_error_compose_create(
- svn_ra_serf__error_on_status(handler->sline,
- handler->path,
- handler->location),
- err);
+ if (!err)
+ {
+ err = svn_ra_serf__error_on_status(handler->sline,
+ handler->path,
+ handler->location);
+ }
if (err && (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE))
return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, NULL);
diff --git a/subversion/libsvn_ra_serf/getlocks.c b/subversion/libsvn_ra_serf/getlocks.c
index df201a7..518d2aa 100644
--- a/subversion/libsvn_ra_serf/getlocks.c
+++ b/subversion/libsvn_ra_serf/getlocks.c
@@ -47,8 +47,8 @@
/*
* This enum represents the current state of our XML parsing for a REPORT.
*/
-enum {
- INITIAL = 0,
+enum getlocks_state_e {
+ INITIAL = XML_STATE_INITIAL,
REPORT,
LOCK,
PATH,
@@ -213,7 +213,8 @@ static svn_error_t *
create_getlocks_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
lock_context_t *lock_ctx = baton;
serf_bucket_t *buckets;
@@ -222,7 +223,7 @@ create_getlocks_body(serf_bucket_t **body_bkt,
svn_ra_serf__add_open_tag_buckets(
buckets, alloc, "S:get-locks-report", "xmlns:S", SVN_XML_NAMESPACE,
- "depth", svn_depth_to_word(lock_ctx->requested_depth), NULL);
+ "depth", svn_depth_to_word(lock_ctx->requested_depth), SVN_VA_NULL);
svn_ra_serf__add_close_tag_buckets(buckets, alloc, "S:get-locks-report");
*body_bkt = buckets;
@@ -244,12 +245,11 @@ svn_ra_serf__get_locks(svn_ra_session_t *ra_session,
svn_error_t *err;
req_url = svn_path_url_add_component2(session->session_url.path, path, pool);
- SVN_ERR(svn_ra_serf__get_relative_path(&rel_path, req_url, session,
- NULL, pool));
+ SVN_ERR(svn_ra_serf__get_relative_path(&rel_path, req_url, session, pool));
lock_ctx = apr_pcalloc(pool, sizeof(*lock_ctx));
lock_ctx->pool = pool;
- lock_ctx->path = apr_pstrcat(pool, "/", rel_path, (char *)NULL);
+ lock_ctx->path = apr_pstrcat(pool, "/", rel_path, SVN_VA_NULL);
lock_ctx->requested_depth = depth;
lock_ctx->hash = apr_hash_make(pool);
@@ -257,33 +257,41 @@ svn_ra_serf__get_locks(svn_ra_session_t *ra_session,
NULL, getlocks_closed, NULL,
lock_ctx,
pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool);
handler->method = "REPORT";
handler->path = req_url;
handler->body_type = "text/xml";
- handler->conn = session->conns[0];
- handler->session = session;
handler->body_delegate = create_getlocks_body;
handler->body_delegate_baton = lock_ctx;
err = svn_ra_serf__context_run_one(handler, pool);
-
- /* Wrap the server generated error for an unsupported report with the
- documented error for this ra function. */
- if (svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE))
- err = svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, NULL);
-
- SVN_ERR(err);
+
+ if (err)
+ {
+ if (svn_error_find_cause(err, SVN_ERR_UNSUPPORTED_FEATURE))
+ {
+ /* The server told us that it doesn't support this report type.
+ We return the documented error for svn_ra_get_locks(), but
+ with the original error report */
+ return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, NULL);
+ }
+ else if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ /* File doesn't exist in HEAD: Not an error */
+ svn_error_clear(err);
+ }
+ else
+ return svn_error_trace(err);
+ }
/* We get a 404 when a path doesn't exist in HEAD, but it might
have existed earlier (E.g. 'svn ls http://s/svn/trunk/file@1' */
- if (handler->sline.code != 404)
+ if (handler->sline.code != 200
+ && handler->sline.code != 404)
{
- SVN_ERR(svn_ra_serf__error_on_status(handler->sline,
- handler->path,
- handler->location));
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
}
*locks = lock_ctx->hash;
diff --git a/subversion/libsvn_ra_serf/inherited_props.c b/subversion/libsvn_ra_serf/inherited_props.c
index f1172e7..6edafb1 100644
--- a/subversion/libsvn_ra_serf/inherited_props.c
+++ b/subversion/libsvn_ra_serf/inherited_props.c
@@ -28,12 +28,14 @@
#include "svn_hash.h"
#include "svn_path.h"
#include "svn_ra.h"
+#include "svn_sorts.h"
#include "svn_string.h"
#include "svn_xml.h"
#include "svn_props.h"
#include "svn_base64.h"
#include "private/svn_dav_protocol.h"
+#include "private/svn_sorts_private.h"
#include "../libsvn_ra/ra_loader.h"
#include "svn_private_config.h"
#include "ra_serf.h"
@@ -41,7 +43,7 @@
/* The current state of our XML parsing. */
typedef enum iprops_state_e {
- INITIAL = 0,
+ INITIAL = XML_STATE_INITIAL,
IPROPS_REPORT,
IPROPS_ITEM,
IPROPS_PATH,
@@ -141,9 +143,7 @@ iprops_closed(svn_ra_serf__xml_estate_t *xes,
return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
iprops_ctx->curr_iprop->path_or_url =
- svn_path_url_add_component2(iprops_ctx->repos_root_url,
- cdata->data,
- iprops_ctx->pool);
+ apr_pstrdup(iprops_ctx->pool, cdata->data);
}
else if (leaving_state == IPROPS_PROPNAME)
{
@@ -197,7 +197,8 @@ static svn_error_t *
create_iprops_body(serf_bucket_t **bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
iprops_context_t *iprops_ctx = baton;
serf_bucket_t *body_bkt;
@@ -207,7 +208,7 @@ create_iprops_body(serf_bucket_t **bkt,
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
"S:" SVN_DAV__INHERITED_PROPS_REPORT,
"xmlns:S", SVN_XML_NAMESPACE,
- NULL);
+ SVN_VA_NULL);
svn_ra_serf__add_tag_buckets(body_bkt,
"S:" SVN_DAV__REVISION,
apr_ltoa(pool, iprops_ctx->revision),
@@ -220,6 +221,131 @@ create_iprops_body(serf_bucket_t **bkt,
return SVN_NO_ERROR;
}
+/* Per request information for get_iprops_via_more_requests */
+typedef struct iprop_rq_info_t
+{
+ const char *relpath;
+ const char *urlpath;
+ apr_hash_t *props;
+ svn_ra_serf__handler_t *handler;
+} iprop_rq_info_t;
+
+
+/* Assumes session reparented to the repository root. The old session
+ root is passed as session_url */
+static svn_error_t *
+get_iprops_via_more_requests(svn_ra_session_t *ra_session,
+ apr_array_header_t **iprops,
+ const char *session_url,
+ const char *path,
+ svn_revnum_t revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_serf__session_t *session = ra_session->priv;
+ const char *url;
+ const char *relpath;
+ apr_array_header_t *rq_info;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_interval_time_t waittime_left = session->timeout;
+ const svn_revnum_t rev_marker = SVN_INVALID_REVNUM;
+ int i;
+
+ rq_info = apr_array_make(scratch_pool, 16, sizeof(iprop_rq_info_t *));
+
+ if (!svn_path_is_empty(path))
+ url = svn_path_url_add_component2(session_url, path, scratch_pool);
+ else
+ url = session_url;
+
+ relpath = svn_uri_skip_ancestor(session->repos_root_str, url, scratch_pool);
+
+ /* Create all requests */
+ while (relpath[0] != '\0')
+ {
+ iprop_rq_info_t *rq = apr_pcalloc(scratch_pool, sizeof(*rq));
+
+ relpath = svn_relpath_dirname(relpath, scratch_pool);
+
+ rq->relpath = relpath;
+ rq->props = apr_hash_make(scratch_pool);
+
+ SVN_ERR(svn_ra_serf__get_stable_url(&rq->urlpath, NULL, session,
+ svn_path_url_add_component2(
+ session->repos_root.path,
+ relpath, scratch_pool),
+ revision,
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_ra_serf__create_propfind_handler(
+ &rq->handler, session,
+ rq->urlpath,
+ rev_marker, "0", all_props,
+ svn_ra_serf__deliver_svn_props,
+ rq->props,
+ scratch_pool));
+
+ /* Allow ignoring authz problems */
+ rq->handler->no_fail_on_http_failure_status = TRUE;
+
+ svn_ra_serf__request_create(rq->handler);
+
+ APR_ARRAY_PUSH(rq_info, iprop_rq_info_t *) = rq;
+ }
+
+ while (TRUE)
+ {
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_ra_serf__context_run(session, &waittime_left, iterpool));
+
+ for (i = 0; i < rq_info->nelts; i++)
+ {
+ iprop_rq_info_t *rq = APR_ARRAY_IDX(rq_info, i, iprop_rq_info_t *);
+
+ if (!rq->handler->done)
+ break;
+ }
+
+ if (i >= rq_info->nelts)
+ break; /* All requests done */
+ }
+
+ *iprops = apr_array_make(result_pool, rq_info->nelts,
+ sizeof(svn_prop_inherited_item_t *));
+
+ /* And now create the result set */
+ for (i = 0; i < rq_info->nelts; i++)
+ {
+ iprop_rq_info_t *rq = APR_ARRAY_IDX(rq_info, i, iprop_rq_info_t *);
+ apr_hash_t *node_props;
+ svn_prop_inherited_item_t *new_iprop;
+
+ if (rq->handler->sline.code != 207 && rq->handler->sline.code != 403)
+ {
+ if (rq->handler->server_error)
+ SVN_ERR(svn_ra_serf__server_error_create(rq->handler,
+ scratch_pool));
+
+ return svn_error_trace(svn_ra_serf__unexpected_status(rq->handler));
+ }
+
+ node_props = rq->props;
+
+ svn_ra_serf__keep_only_regular_props(node_props, scratch_pool);
+
+ if (!apr_hash_count(node_props))
+ continue;
+
+ new_iprop = apr_palloc(result_pool, sizeof(*new_iprop));
+ new_iprop->path_or_url = apr_pstrdup(result_pool, rq->relpath);
+ new_iprop->prop_hash = svn_prop_hash_dup(node_props, result_pool);
+ svn_sort__array_insert(*iprops, &new_iprop, 0);
+ }
+
+ return SVN_NO_ERROR;
+}
+
/* Request a inherited-props-report from the URL attached to RA_SESSION,
and fill the IPROPS array hash with the results. */
svn_error_t *
@@ -230,20 +356,53 @@ svn_ra_serf__get_inherited_props(svn_ra_session_t *ra_session,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- svn_error_t *err;
iprops_context_t *iprops_ctx;
svn_ra_serf__session_t *session = ra_session->priv;
svn_ra_serf__handler_t *handler;
svn_ra_serf__xml_context_t *xmlctx;
const char *req_url;
+ svn_boolean_t iprop_capable;
+
+ SVN_ERR(svn_ra_serf__has_capability(ra_session, &iprop_capable,
+ SVN_RA_CAPABILITY_INHERITED_PROPS,
+ scratch_pool));
+
+ if (!iprop_capable)
+ {
+ svn_error_t *err;
+ const char *reparent_uri = NULL;
+ const char *session_uri;
+ const char *repos_root_url;
+
+ SVN_ERR(svn_ra_serf__get_repos_root(ra_session, &repos_root_url,
+ scratch_pool));
+
+ session_uri = apr_pstrdup(scratch_pool, session->session_url_str);
+ if (strcmp(repos_root_url, session->session_url_str) != 0)
+ {
+ reparent_uri = session_uri;
+ SVN_ERR(svn_ra_serf__reparent(ra_session, repos_root_url,
+ scratch_pool));
+ }
+
+ err = get_iprops_via_more_requests(ra_session, iprops, session_uri, path,
+ revision, result_pool, scratch_pool);
+
+ if (reparent_uri)
+ err = svn_error_compose_create(err,
+ svn_ra_serf__reparent(ra_session,
+ reparent_uri ,
+ scratch_pool));
+
+ return svn_error_trace(err);
+ }
SVN_ERR(svn_ra_serf__get_stable_url(&req_url,
NULL /* latest_revnum */,
session,
- NULL /* conn */,
NULL /* url */,
revision,
- result_pool, scratch_pool));
+ scratch_pool, scratch_pool));
SVN_ERR_ASSERT(session->repos_root_str);
@@ -258,26 +417,24 @@ svn_ra_serf__get_inherited_props(svn_ra_session_t *ra_session,
iprops_ctx->revision = revision;
xmlctx = svn_ra_serf__xml_context_create(iprops_table,
- iprops_opened, iprops_closed, NULL,
+ iprops_opened, iprops_closed,
+ NULL,
iprops_ctx,
scratch_pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, scratch_pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL,
+ scratch_pool);
handler->method = "REPORT";
handler->path = req_url;
- handler->conn = session->conns[0];
- handler->session = session;
+
handler->body_delegate = create_iprops_body;
handler->body_delegate_baton = iprops_ctx;
handler->body_type = "text/xml";
- handler->handler_pool = scratch_pool;
-
- err = svn_ra_serf__context_run_one(handler, scratch_pool);
- SVN_ERR(svn_error_compose_create(
- svn_ra_serf__error_on_status(handler->sline,
- handler->path,
- handler->location),
- err));
+
+ SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
+
+ if (handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
*iprops = iprops_ctx->iprops;
diff --git a/subversion/libsvn_ra_serf/libsvn_ra_serf.pc.in b/subversion/libsvn_ra_serf/libsvn_ra_serf.pc.in
new file mode 100644
index 0000000..e02ef12
--- /dev/null
+++ b/subversion/libsvn_ra_serf/libsvn_ra_serf.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libsvn_ra_serf
+Description: Subversion HTTP/WebDAV Protocol Repository Access Library
+Version: @PACKAGE_VERSION@
+Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@
+Requires.private: libsvn_delta libsvn_subr serf-1
+Libs: -L${libdir} -lsvn_ra_serf @SVN_XML_LIBS@ @SVN_ZLIB_LIBS@
+Cflags: -I${includedir}
diff --git a/subversion/libsvn_ra_serf/lock.c b/subversion/libsvn_ra_serf/lock.c
new file mode 100644
index 0000000..dd045e3
--- /dev/null
+++ b/subversion/libsvn_ra_serf/lock.c
@@ -0,0 +1,679 @@
+/*
+ * lock.c : entry point for locking RA functions for ra_serf
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+
+
+#include <apr_uri.h>
+#include <serf.h>
+#include <assert.h>
+
+#include "svn_dav.h"
+#include "svn_hash.h"
+#include "svn_pools.h"
+#include "svn_ra.h"
+
+#include "../libsvn_ra/ra_loader.h"
+#include "svn_config.h"
+#include "svn_path.h"
+#include "svn_sorts.h"
+#include "svn_time.h"
+#include "svn_private_config.h"
+#include "private/svn_sorts_private.h"
+
+#include "ra_serf.h"
+
+
+/*
+ * This enum represents the current state of our XML parsing for a REPORT.
+ */
+enum {
+ INITIAL = 0,
+ PROP,
+ LOCK_DISCOVERY,
+ ACTIVE_LOCK,
+ LOCK_TYPE,
+ LOCK_SCOPE,
+ DEPTH,
+ TIMEOUT,
+ LOCK_TOKEN,
+ OWNER,
+ HREF
+};
+
+
+typedef struct lock_ctx_t {
+ apr_pool_t *pool;
+
+ const char *path;
+
+ const char *token; /* For unlock */
+ svn_lock_t *lock; /* For lock */
+
+ svn_boolean_t force;
+ svn_revnum_t revision;
+
+ svn_boolean_t read_headers;
+
+ svn_ra_serf__handler_t *handler;
+
+ /* The expat handler. We wrap this to do a bit more work. */
+ svn_ra_serf__response_handler_t inner_handler;
+ void *inner_baton;
+
+} lock_ctx_t;
+
+
+#define D_ "DAV:"
+#define S_ SVN_XML_NAMESPACE
+static const svn_ra_serf__xml_transition_t locks_ttable[] = {
+ /* The INITIAL state can transition into D:prop (LOCK) or
+ to D:multistatus (PROPFIND) */
+ { INITIAL, D_, "prop", PROP,
+ FALSE, { NULL }, FALSE },
+
+ { PROP, D_, "lockdiscovery", LOCK_DISCOVERY,
+ FALSE, { NULL }, FALSE },
+
+ { LOCK_DISCOVERY, D_, "activelock", ACTIVE_LOCK,
+ FALSE, { NULL }, FALSE },
+
+#if 0
+ /* ### we don't really need to parse locktype/lockscope. we know what
+ ### the values are going to be. we *could* validate that the only
+ ### possible children are D:write and D:exclusive. we'd need to
+ ### modify the state transition to tell us about all children
+ ### (ie. maybe support "*" for the name) and then validate. but it
+ ### just isn't important to validate, so disable this for now... */
+
+ { ACTIVE_LOCK, D_, "locktype", LOCK_TYPE,
+ FALSE, { NULL }, FALSE },
+
+ { LOCK_TYPE, D_, "write", WRITE,
+ FALSE, { NULL }, TRUE },
+
+ { ACTIVE_LOCK, D_, "lockscope", LOCK_SCOPE,
+ FALSE, { NULL }, FALSE },
+
+ { LOCK_SCOPE, D_, "exclusive", EXCLUSIVE,
+ FALSE, { NULL }, TRUE },
+#endif /* 0 */
+
+ { ACTIVE_LOCK, D_, "timeout", TIMEOUT,
+ TRUE, { NULL }, TRUE },
+
+ { ACTIVE_LOCK, D_, "locktoken", LOCK_TOKEN,
+ FALSE, { NULL }, FALSE },
+
+ { LOCK_TOKEN, D_, "href", HREF,
+ TRUE, { NULL }, TRUE },
+
+ { ACTIVE_LOCK, D_, "owner", OWNER,
+ TRUE, { NULL }, TRUE },
+
+ /* ACTIVE_LOCK has a D:depth child, but we can ignore that. */
+
+ { 0 }
+};
+
+/* Conforms to svn_ra_serf__xml_closed_t */
+static svn_error_t *
+locks_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)
+{
+ lock_ctx_t *lock_ctx = baton;
+
+ if (leaving_state == TIMEOUT)
+ {
+ /* This function just parses the result of our own lock request,
+ so on a normal server we will only encounter 'Infinite' here. */
+ if (strcasecmp(cdata->data, "Infinite") == 0)
+ lock_ctx->lock->expiration_date = 0;
+ else if (strncasecmp(cdata->data, "Second-", 7) == 0)
+ {
+ unsigned n;
+ SVN_ERR(svn_cstring_atoui(&n, cdata->data+7));
+
+ lock_ctx->lock->expiration_date = apr_time_now() +
+ apr_time_from_sec(n);
+ }
+ else
+ return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+ _("Invalid LOCK timeout value '%s'"),
+ cdata->data);
+ }
+ else if (leaving_state == HREF)
+ {
+ if (cdata->len)
+ {
+ char *buf = apr_pstrmemdup(lock_ctx->pool, cdata->data, cdata->len);
+
+ apr_collapse_spaces(buf, buf);
+ lock_ctx->lock->token = buf;
+ }
+ }
+ else if (leaving_state == OWNER)
+ {
+ if (cdata->len)
+ {
+ lock_ctx->lock->comment = apr_pstrmemdup(lock_ctx->pool,
+ cdata->data, cdata->len);
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+set_lock_headers(serf_bucket_t *headers,
+ void *baton,
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
+{
+ lock_ctx_t *lock_ctx = baton;
+
+ if (lock_ctx->force)
+ {
+ serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER,
+ SVN_DAV_OPTION_LOCK_STEAL);
+ }
+
+ if (SVN_IS_VALID_REVNUM(lock_ctx->revision))
+ {
+ serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER,
+ apr_ltoa(pool, lock_ctx->revision));
+ }
+
+ return APR_SUCCESS;
+}
+
+/* Helper function for svn_ra_serf__lock and svn_ra_serf__unlock */
+static svn_error_t *
+run_locks(svn_ra_serf__session_t *sess,
+ apr_array_header_t *lock_ctxs,
+ svn_boolean_t locking,
+ svn_ra_lock_callback_t lock_func,
+ void *lock_baton,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool;
+ apr_interval_time_t waittime_left = sess->timeout;
+
+ assert(sess->pending_error == SVN_NO_ERROR);
+
+ iterpool = svn_pool_create(scratch_pool);
+ while (lock_ctxs->nelts)
+ {
+ int i;
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_ra_serf__context_run(sess, &waittime_left, iterpool));
+
+ for (i = 0; i < lock_ctxs->nelts; i++)
+ {
+ lock_ctx_t *ctx = APR_ARRAY_IDX(lock_ctxs, i, lock_ctx_t *);
+
+ if (ctx->handler->done)
+ {
+ svn_error_t *server_err = NULL;
+ svn_error_t *cb_err = NULL;
+ svn_error_t *err;
+
+ if (ctx->handler->server_error)
+ server_err = svn_ra_serf__server_error_create(ctx->handler, iterpool);
+
+ /* Api users expect specific error code to detect failures,
+ pass the rest to svn_ra_serf__error_on_status */
+ switch (ctx->handler->sline.code)
+ {
+ case 200:
+ case 204:
+ err = NULL; /* (un)lock succeeded */
+ break;
+
+ case 400:
+ err = svn_error_createf(SVN_ERR_FS_NO_SUCH_LOCK, NULL,
+ _("No lock on path '%s' (%d %s)"),
+ ctx->path,
+ ctx->handler->sline.code,
+ ctx->handler->sline.reason);
+ break;
+ case 403:
+ /* ### Authz can also lead to 403. */
+ err = svn_error_createf(SVN_ERR_FS_LOCK_OWNER_MISMATCH,
+ NULL,
+ _("Unlock of '%s' failed (%d %s)"),
+ ctx->path,
+ ctx->handler->sline.code,
+ ctx->handler->sline.reason);
+ break;
+ case 405:
+ err = svn_error_createf(SVN_ERR_FS_OUT_OF_DATE,
+ NULL,
+ _("Path '%s' doesn't exist in "
+ "HEAD revision (%d %s)"),
+ ctx->path,
+ ctx->handler->sline.code,
+ ctx->handler->sline.reason);
+ break;
+ case 423:
+ err = svn_error_createf(SVN_ERR_FS_PATH_ALREADY_LOCKED,
+ NULL,
+ _("Path '%s' already locked "
+ "(%d %s)"),
+ ctx->path,
+ ctx->handler->sline.code,
+ ctx->handler->sline.reason);
+ break;
+
+ case 404:
+ case 409:
+ case 500:
+ if (server_err)
+ {
+ /* Handle out of date, etc by just passing the server
+ error */
+ err = NULL;
+ break;
+ }
+
+ /* Fall through */
+ default:
+ err = svn_ra_serf__unexpected_status(ctx->handler);
+ break;
+ }
+
+ if (server_err && err && server_err->apr_err == err->apr_err)
+ err = svn_error_compose_create(server_err, err);
+ else
+ err = svn_error_compose_create(err, server_err);
+
+ if (err
+ && !SVN_ERR_IS_UNLOCK_ERROR(err)
+ && !SVN_ERR_IS_LOCK_ERROR(err))
+ {
+ /* If the error that we are going to report is just about the
+ POST unlock hook, we should first report that the operation
+ succeeded, or the repository and working copy will be
+ out of sync... */
+
+ if (lock_func &&
+ err->apr_err == SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED)
+ {
+ err = svn_error_compose_create(
+ err, lock_func(lock_baton, ctx->path, locking,
+ NULL, NULL, ctx->pool));
+ }
+
+ return svn_error_trace(err); /* Don't go through callbacks */
+ }
+
+ if (lock_func)
+ {
+ svn_lock_t *report_lock = NULL;
+
+ if (locking && ctx->lock->token)
+ report_lock = ctx->lock;
+
+ cb_err = lock_func(lock_baton, ctx->path, locking,
+ report_lock, err, ctx->pool);
+ }
+ svn_error_clear(err);
+
+ SVN_ERR(cb_err);
+
+ waittime_left = sess->timeout;
+ svn_sort__array_delete(lock_ctxs, i, 1);
+ i--;
+
+ svn_pool_destroy(ctx->pool);
+ continue;
+ }
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Implements svn_ra_serf__response_handler_t */
+static svn_error_t *
+handle_lock(serf_request_t *request,
+ serf_bucket_t *response,
+ void *handler_baton,
+ apr_pool_t *pool)
+{
+ lock_ctx_t *ctx = handler_baton;
+
+ if (!ctx->read_headers)
+ {
+ serf_bucket_t *headers;
+ const char *val;
+
+ headers = serf_bucket_response_get_headers(response);
+
+ val = serf_bucket_headers_get(headers, SVN_DAV_LOCK_OWNER_HEADER);
+ if (val)
+ {
+ ctx->lock->owner = apr_pstrdup(ctx->pool, val);
+ }
+
+ val = serf_bucket_headers_get(headers, SVN_DAV_CREATIONDATE_HEADER);
+ if (val)
+ {
+ SVN_ERR(svn_time_from_cstring(&ctx->lock->creation_date, val,
+ ctx->pool));
+ }
+
+ ctx->read_headers = TRUE;
+ }
+
+ return ctx->inner_handler(request, response, ctx->inner_baton, pool);
+}
+
+/* Implements svn_ra_serf__request_body_delegate_t */
+static svn_error_t *
+create_lock_body(serf_bucket_t **body_bkt,
+ void *baton,
+ serf_bucket_alloc_t *alloc,
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
+{
+ lock_ctx_t *ctx = baton;
+ serf_bucket_t *buckets;
+
+ buckets = serf_bucket_aggregate_create(alloc);
+
+ svn_ra_serf__add_xml_header_buckets(buckets, alloc);
+ svn_ra_serf__add_open_tag_buckets(buckets, alloc, "lockinfo",
+ "xmlns", "DAV:",
+ SVN_VA_NULL);
+
+ svn_ra_serf__add_open_tag_buckets(buckets, alloc, "lockscope", SVN_VA_NULL);
+ svn_ra_serf__add_empty_tag_buckets(buckets, alloc, "exclusive", SVN_VA_NULL);
+ svn_ra_serf__add_close_tag_buckets(buckets, alloc, "lockscope");
+
+ svn_ra_serf__add_open_tag_buckets(buckets, alloc, "locktype", SVN_VA_NULL);
+ svn_ra_serf__add_empty_tag_buckets(buckets, alloc, "write", SVN_VA_NULL);
+ svn_ra_serf__add_close_tag_buckets(buckets, alloc, "locktype");
+
+ if (ctx->lock->comment)
+ {
+ svn_ra_serf__add_tag_buckets(buckets, "owner", ctx->lock->comment,
+ alloc);
+ }
+
+ svn_ra_serf__add_close_tag_buckets(buckets, alloc, "lockinfo");
+
+ *body_bkt = buckets;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra_serf__lock(svn_ra_session_t *ra_session,
+ apr_hash_t *path_revs,
+ const char *comment,
+ svn_boolean_t force,
+ svn_ra_lock_callback_t lock_func,
+ void *lock_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_serf__session_t *session = ra_session->priv;
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool;
+ apr_array_header_t *lock_requests;
+
+ lock_requests = apr_array_make(scratch_pool, apr_hash_count(path_revs),
+ sizeof(lock_ctx_t*));
+
+ /* ### Perhaps we should open more connections than just one? See update.c */
+
+ iterpool = svn_pool_create(scratch_pool);
+
+ for (hi = apr_hash_first(scratch_pool, path_revs);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ svn_ra_serf__handler_t *handler;
+ svn_ra_serf__xml_context_t *xmlctx;
+ const char *req_url;
+ lock_ctx_t *lock_ctx;
+ apr_pool_t *lock_pool;
+
+ svn_pool_clear(iterpool);
+
+ lock_pool = svn_pool_create(scratch_pool);
+ lock_ctx = apr_pcalloc(scratch_pool, sizeof(*lock_ctx));
+
+ lock_ctx->pool = lock_pool;
+ lock_ctx->path = apr_hash_this_key(hi);
+ lock_ctx->revision = *((svn_revnum_t*)apr_hash_this_val(hi));
+ lock_ctx->lock = svn_lock_create(lock_pool);
+ lock_ctx->lock->path = lock_ctx->path;
+ lock_ctx->lock->comment = comment;
+
+ lock_ctx->force = force;
+ req_url = svn_path_url_add_component2(session->session_url.path,
+ lock_ctx->path, lock_pool);
+
+ xmlctx = svn_ra_serf__xml_context_create(locks_ttable,
+ NULL, locks_closed, NULL,
+ lock_ctx,
+ lock_pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL,
+ lock_pool);
+
+ handler->method = "LOCK";
+ handler->path = req_url;
+ handler->body_type = "text/xml";
+
+ /* Same stupid algorithm from get_best_connection() in update.c */
+ handler->conn = session->conns[session->cur_conn];
+ session->cur_conn++;
+
+ if (session->cur_conn >= session->num_conns)
+ session->cur_conn = 0;
+
+ handler->header_delegate = set_lock_headers;
+ handler->header_delegate_baton = lock_ctx;
+
+ handler->body_delegate = create_lock_body;
+ handler->body_delegate_baton = lock_ctx;
+
+ lock_ctx->inner_handler = handler->response_handler;
+ lock_ctx->inner_baton = handler->response_baton;
+ handler->response_handler = handle_lock;
+ handler->response_baton = lock_ctx;
+
+ handler->no_fail_on_http_failure_status = TRUE;
+
+ lock_ctx->handler = handler;
+
+ APR_ARRAY_PUSH(lock_requests, lock_ctx_t *) = lock_ctx;
+
+ svn_ra_serf__request_create(handler);
+ }
+
+ SVN_ERR(run_locks(session, lock_requests, TRUE, lock_func, lock_baton,
+ iterpool));
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+set_unlock_headers(serf_bucket_t *headers,
+ void *baton,
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
+{
+ lock_ctx_t *ctx = baton;
+
+ serf_bucket_headers_set(headers, "Lock-Token", ctx->token);
+ if (ctx->force)
+ {
+ serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER,
+ SVN_DAV_OPTION_LOCK_BREAK);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra_serf__unlock(svn_ra_session_t *ra_session,
+ apr_hash_t *path_tokens,
+ svn_boolean_t force,
+ svn_ra_lock_callback_t lock_func,
+ void *lock_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_serf__session_t *session = ra_session->priv;
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool;
+ apr_array_header_t *lock_requests;
+
+ iterpool = svn_pool_create(scratch_pool);
+
+ /* If we are stealing locks we need the lock tokens */
+ if (force)
+ {
+ /* Theoretically this part can be improved (for performance) by using
+ svn_ra_get_locks() to obtain all the locks in a single request, but
+ do we really want to improve the performance of
+ $ svn unlock --force *
+ */
+
+ for (hi = apr_hash_first(scratch_pool, path_tokens);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *path;
+ const char *token;
+ svn_lock_t *existing_lock;
+ svn_error_t *err;
+
+ svn_pool_clear(iterpool);
+
+ path = apr_hash_this_key(hi);
+ token = apr_hash_this_val(hi);
+
+ if (token && token[0])
+ continue;
+
+ if (session->cancel_func)
+ SVN_ERR(session->cancel_func(session->cancel_baton));
+
+ err = svn_ra_serf__get_lock(ra_session, &existing_lock, path,
+ iterpool);
+
+ if (!err && existing_lock)
+ {
+ svn_hash_sets(path_tokens, path,
+ apr_pstrdup(scratch_pool, existing_lock->token));
+ continue;
+ }
+
+ err = svn_error_createf(SVN_ERR_RA_NOT_LOCKED, err,
+ _("'%s' is not locked in the repository"),
+ path);
+
+ if (lock_func)
+ {
+ svn_error_t *err2;
+ err2 = lock_func(lock_baton, path, FALSE, NULL, err, iterpool);
+ svn_error_clear(err);
+
+ SVN_ERR(err2);
+ }
+ else
+ {
+ svn_error_clear(err);
+ }
+
+ svn_hash_sets(path_tokens, path, NULL);
+ }
+ }
+
+ /* ### Perhaps we should open more connections than just one? See update.c */
+
+ lock_requests = apr_array_make(scratch_pool, apr_hash_count(path_tokens),
+ sizeof(lock_ctx_t*));
+
+ for (hi = apr_hash_first(scratch_pool, path_tokens);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ svn_ra_serf__handler_t *handler;
+ const char *req_url, *token;
+ lock_ctx_t *lock_ctx;
+ apr_pool_t *lock_pool;
+
+ svn_pool_clear(iterpool);
+
+ lock_pool = svn_pool_create(scratch_pool);
+ lock_ctx = apr_pcalloc(lock_pool, sizeof(*lock_ctx));
+
+ lock_ctx->pool = lock_pool;
+
+ lock_ctx->path = apr_hash_this_key(hi);
+ token = apr_hash_this_val(hi);
+
+ lock_ctx->force = force;
+ lock_ctx->token = apr_pstrcat(lock_pool, "<", token, ">", SVN_VA_NULL);
+
+ req_url = svn_path_url_add_component2(session->session_url.path, lock_ctx->path,
+ lock_pool);
+
+ handler = svn_ra_serf__create_handler(session, lock_pool);
+
+ handler->method = "UNLOCK";
+ handler->path = req_url;
+
+ handler->header_delegate = set_unlock_headers;
+ handler->header_delegate_baton = lock_ctx;
+
+ handler->response_handler = svn_ra_serf__expect_empty_body;
+ handler->response_baton = handler;
+
+ handler->no_fail_on_http_failure_status = TRUE;
+
+ lock_ctx->handler = handler;
+
+ APR_ARRAY_PUSH(lock_requests, lock_ctx_t *) = lock_ctx;
+
+ svn_ra_serf__request_create(handler);
+ }
+
+ SVN_ERR(run_locks(session, lock_requests, FALSE, lock_func, lock_baton,
+ iterpool));
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/libsvn_ra_serf/locks.c b/subversion/libsvn_ra_serf/locks.c
deleted file mode 100644
index 252c301..0000000
--- a/subversion/libsvn_ra_serf/locks.c
+++ /dev/null
@@ -1,669 +0,0 @@
-/*
- * locks.c : entry point for locking RA functions for ra_serf
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- */
-
-
-
-#include <apr_uri.h>
-#include <serf.h>
-
-#include "svn_dav.h"
-#include "svn_pools.h"
-#include "svn_ra.h"
-
-#include "../libsvn_ra/ra_loader.h"
-#include "svn_config.h"
-#include "svn_path.h"
-#include "svn_time.h"
-#include "svn_private_config.h"
-
-#include "ra_serf.h"
-
-
-/*
- * This enum represents the current state of our XML parsing for a REPORT.
- */
-enum {
- INITIAL = 0,
- MULTISTATUS,
- RESPONSE,
- PROPSTAT,
- PROP,
- LOCK_DISCOVERY,
- ACTIVE_LOCK,
- LOCK_TYPE,
- LOCK_SCOPE,
- DEPTH,
- TIMEOUT,
- LOCK_TOKEN,
- OWNER,
- HREF
-};
-
-typedef struct lock_info_t {
- apr_pool_t *pool;
-
- const char *path;
-
- svn_lock_t *lock;
-
- svn_boolean_t force;
- svn_revnum_t revision;
-
- svn_boolean_t read_headers;
-
- svn_ra_serf__handler_t *handler;
-
- /* The expat handler. We wrap this to do a bit more work. */
- svn_ra_serf__response_handler_t inner_handler;
- void *inner_baton;
-
-} lock_info_t;
-
-#define D_ "DAV:"
-#define S_ SVN_XML_NAMESPACE
-static const svn_ra_serf__xml_transition_t locks_ttable[] = {
- /* The INITIAL state can transition into D:prop (LOCK) or
- to D:multistatus (PROPFIND) */
- { INITIAL, D_, "prop", PROP,
- FALSE, { NULL }, FALSE },
- { INITIAL, D_, "multistatus", MULTISTATUS,
- FALSE, { NULL }, FALSE },
-
- { MULTISTATUS, D_, "response", RESPONSE,
- FALSE, { NULL }, FALSE },
-
- { RESPONSE, D_, "propstat", PROPSTAT,
- FALSE, { NULL }, FALSE },
-
- { PROPSTAT, D_, "prop", PROP,
- FALSE, { NULL }, FALSE },
-
- { PROP, D_, "lockdiscovery", LOCK_DISCOVERY,
- FALSE, { NULL }, FALSE },
-
- { LOCK_DISCOVERY, D_, "activelock", ACTIVE_LOCK,
- FALSE, { NULL }, FALSE },
-
-#if 0
- /* ### we don't really need to parse locktype/lockscope. we know what
- ### the values are going to be. we *could* validate that the only
- ### possible children are D:write and D:exclusive. we'd need to
- ### modify the state transition to tell us about all children
- ### (ie. maybe support "*" for the name) and then validate. but it
- ### just isn't important to validate, so disable this for now... */
-
- { ACTIVE_LOCK, D_, "locktype", LOCK_TYPE,
- FALSE, { NULL }, FALSE },
-
- { LOCK_TYPE, D_, "write", WRITE,
- FALSE, { NULL }, TRUE },
-
- { ACTIVE_LOCK, D_, "lockscope", LOCK_SCOPE,
- FALSE, { NULL }, FALSE },
-
- { LOCK_SCOPE, D_, "exclusive", EXCLUSIVE,
- FALSE, { NULL }, TRUE },
-#endif /* 0 */
-
- { ACTIVE_LOCK, D_, "timeout", TIMEOUT,
- TRUE, { NULL }, TRUE },
-
- { ACTIVE_LOCK, D_, "locktoken", LOCK_TOKEN,
- FALSE, { NULL }, FALSE },
-
- { LOCK_TOKEN, D_, "href", HREF,
- TRUE, { NULL }, TRUE },
-
- { ACTIVE_LOCK, D_, "owner", OWNER,
- TRUE, { NULL }, TRUE },
-
- /* ACTIVE_LOCK has a D:depth child, but we can ignore that. */
-
- { 0 }
-};
-
-
-/* Conforms to svn_ra_serf__xml_closed_t */
-static svn_error_t *
-locks_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)
-{
- lock_info_t *lock_ctx = baton;
-
- if (leaving_state == TIMEOUT)
- {
- if (strcasecmp(cdata->data, "Infinite") == 0)
- lock_ctx->lock->expiration_date = 0;
- else if (strncasecmp(cdata->data, "Second-", 7) == 0)
- {
- unsigned n;
- SVN_ERR(svn_cstring_atoui(&n, cdata->data+7));
-
- lock_ctx->lock->expiration_date = apr_time_now() +
- apr_time_from_sec(n);
- }
- else
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Invalid LOCK timeout value '%s'"),
- cdata->data);
- }
- else if (leaving_state == HREF)
- {
- if (cdata->len)
- {
- char *buf = apr_pstrmemdup(lock_ctx->pool, cdata->data, cdata->len);
-
- apr_collapse_spaces(buf, buf);
- lock_ctx->lock->token = buf;
- }
- }
- else if (leaving_state == OWNER)
- {
- if (cdata->len)
- {
- lock_ctx->lock->comment = apr_pstrmemdup(lock_ctx->pool,
- cdata->data, cdata->len);
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *
-set_lock_headers(serf_bucket_t *headers,
- void *baton,
- apr_pool_t *pool)
-{
- lock_info_t *lock_ctx = baton;
-
- if (lock_ctx->force)
- {
- serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER,
- SVN_DAV_OPTION_LOCK_STEAL);
- }
-
- if (SVN_IS_VALID_REVNUM(lock_ctx->revision))
- {
- serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER,
- apr_ltoa(pool, lock_ctx->revision));
- }
-
- return APR_SUCCESS;
-}
-
-
-/* Register an error within the session. If something is already there,
- then it will take precedence. */
-static svn_error_t *
-determine_error(svn_ra_serf__handler_t *handler,
- svn_error_t *err)
-{
- {
- apr_status_t errcode;
-
- if (handler->sline.code == 423)
- errcode = SVN_ERR_FS_PATH_ALREADY_LOCKED;
- else if (handler->sline.code == 403)
- errcode = SVN_ERR_RA_DAV_FORBIDDEN;
- else
- return err;
-
- /* Client-side or server-side error already. Return it. */
- if (err != NULL)
- return err;
-
- /* The server did not send us a detailed human-readable error.
- Provide a generic error. */
- err = svn_error_createf(errcode, NULL,
- _("Lock request failed: %d %s"),
- handler->sline.code,
- handler->sline.reason);
- }
-
- return err;
-}
-
-
-/* Implements svn_ra_serf__response_handler_t */
-static svn_error_t *
-handle_lock(serf_request_t *request,
- serf_bucket_t *response,
- void *handler_baton,
- apr_pool_t *pool)
-{
- lock_info_t *ctx = handler_baton;
-
- /* 403 (Forbidden) when a lock doesn't exist.
- 423 (Locked) when a lock already exists. */
- if (ctx->handler->sline.code == 403
- || ctx->handler->sline.code == 423)
- {
- /* Go look in the body for a server-provided error. This will
- reset flags for the core handler to Do The Right Thing. We
- won't be back to this handler again. */
- return svn_error_trace(svn_ra_serf__expect_empty_body(
- request, response, ctx->handler, pool));
- }
-
- if (!ctx->read_headers)
- {
- serf_bucket_t *headers;
- const char *val;
-
- headers = serf_bucket_response_get_headers(response);
-
- val = serf_bucket_headers_get(headers, SVN_DAV_LOCK_OWNER_HEADER);
- if (val)
- {
- ctx->lock->owner = apr_pstrdup(ctx->pool, val);
- }
-
- val = serf_bucket_headers_get(headers, SVN_DAV_CREATIONDATE_HEADER);
- if (val)
- {
- SVN_ERR(svn_time_from_cstring(&ctx->lock->creation_date, val,
- ctx->pool));
- }
-
- ctx->read_headers = TRUE;
- }
-
- return ctx->inner_handler(request, response, ctx->inner_baton, pool);
-}
-
-/* Implements svn_ra_serf__request_body_delegate_t */
-static svn_error_t *
-create_getlock_body(serf_bucket_t **body_bkt,
- void *baton,
- serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
-{
- serf_bucket_t *buckets;
-
- buckets = serf_bucket_aggregate_create(alloc);
-
- svn_ra_serf__add_xml_header_buckets(buckets, alloc);
- svn_ra_serf__add_open_tag_buckets(buckets, alloc, "propfind",
- "xmlns", "DAV:",
- NULL);
- svn_ra_serf__add_open_tag_buckets(buckets, alloc, "prop", NULL);
- svn_ra_serf__add_tag_buckets(buckets, "lockdiscovery", NULL, alloc);
- svn_ra_serf__add_close_tag_buckets(buckets, alloc, "prop");
- svn_ra_serf__add_close_tag_buckets(buckets, alloc, "propfind");
-
- *body_bkt = buckets;
- return SVN_NO_ERROR;
-}
-
-static svn_error_t*
-setup_getlock_headers(serf_bucket_t *headers,
- void *baton,
- apr_pool_t *pool)
-{
- serf_bucket_headers_setn(headers, "Depth", "0");
-
- return SVN_NO_ERROR;
-}
-
-/* Implements svn_ra_serf__request_body_delegate_t */
-static svn_error_t *
-create_lock_body(serf_bucket_t **body_bkt,
- void *baton,
- serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
-{
- lock_info_t *ctx = baton;
- serf_bucket_t *buckets;
-
- buckets = serf_bucket_aggregate_create(alloc);
-
- svn_ra_serf__add_xml_header_buckets(buckets, alloc);
- svn_ra_serf__add_open_tag_buckets(buckets, alloc, "lockinfo",
- "xmlns", "DAV:",
- NULL);
-
- svn_ra_serf__add_open_tag_buckets(buckets, alloc, "lockscope", NULL);
- svn_ra_serf__add_tag_buckets(buckets, "exclusive", NULL, alloc);
- svn_ra_serf__add_close_tag_buckets(buckets, alloc, "lockscope");
-
- svn_ra_serf__add_open_tag_buckets(buckets, alloc, "locktype", NULL);
- svn_ra_serf__add_tag_buckets(buckets, "write", NULL, alloc);
- svn_ra_serf__add_close_tag_buckets(buckets, alloc, "locktype");
-
- if (ctx->lock->comment)
- {
- svn_ra_serf__add_tag_buckets(buckets, "owner", ctx->lock->comment,
- alloc);
- }
-
- svn_ra_serf__add_close_tag_buckets(buckets, alloc, "lockinfo");
-
- *body_bkt = buckets;
- return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_ra_serf__get_lock(svn_ra_session_t *ra_session,
- svn_lock_t **lock,
- const char *path,
- apr_pool_t *result_pool)
-{
- svn_ra_serf__session_t *session = ra_session->priv;
- svn_ra_serf__handler_t *handler;
- svn_ra_serf__xml_context_t *xmlctx;
- apr_pool_t *scratch_pool = svn_pool_create(result_pool);
- lock_info_t *lock_ctx;
- const char *req_url;
- svn_error_t *err;
-
- req_url = svn_path_url_add_component2(session->session_url.path, path,
- scratch_pool);
-
- lock_ctx = apr_pcalloc(scratch_pool, sizeof(*lock_ctx));
- lock_ctx->pool = result_pool;
- lock_ctx->path = req_url;
- lock_ctx->lock = svn_lock_create(result_pool);
- lock_ctx->lock->path = apr_pstrdup(result_pool, path);
-
- xmlctx = svn_ra_serf__xml_context_create(locks_ttable,
- NULL, locks_closed, NULL,
- lock_ctx,
- scratch_pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, scratch_pool);
-
- handler->method = "PROPFIND";
- handler->path = req_url;
- handler->body_type = "text/xml";
- handler->conn = session->conns[0];
- handler->session = session;
-
- handler->body_delegate = create_getlock_body;
- handler->body_delegate_baton = lock_ctx;
-
- handler->header_delegate = setup_getlock_headers;
- handler->header_delegate_baton = lock_ctx;
-
- lock_ctx->inner_handler = handler->response_handler;
- lock_ctx->inner_baton = handler->response_baton;
- handler->response_handler = handle_lock;
- handler->response_baton = lock_ctx;
-
- lock_ctx->handler = handler;
-
- err = svn_ra_serf__context_run_one(handler, scratch_pool);
- err = determine_error(handler, err);
-
- if (handler->sline.code == 404)
- {
- return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, err,
- _("Malformed URL for repository"));
- }
- if (err)
- {
- /* TODO Shh. We're telling a white lie for now. */
- return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err,
- _("Server does not support locking features"));
- }
-
- if (lock_ctx->lock && lock_ctx->lock->token)
- *lock = lock_ctx->lock;
- else
- *lock = NULL;
-
- svn_pool_destroy(scratch_pool);
-
- return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_ra_serf__lock(svn_ra_session_t *ra_session,
- apr_hash_t *path_revs,
- const char *comment,
- svn_boolean_t force,
- svn_ra_lock_callback_t lock_func,
- void *lock_baton,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__session_t *session = ra_session->priv;
- apr_hash_index_t *hi;
- apr_pool_t *iterpool;
-
- iterpool = svn_pool_create(scratch_pool);
-
- /* ### TODO for issue 2263: Send all the locks over the wire at once. This
- ### loop is just a temporary shim.
- ### an alternative, which is backwards-compat with all servers is to
- ### pipeline these requests. ie. stop using run_wait/run_one. */
-
- for (hi = apr_hash_first(scratch_pool, path_revs);
- hi;
- hi = apr_hash_next(hi))
- {
- svn_ra_serf__handler_t *handler;
- svn_ra_serf__xml_context_t *xmlctx;
- const char *req_url;
- lock_info_t *lock_ctx;
- svn_error_t *err;
- svn_error_t *new_err = NULL;
-
- svn_pool_clear(iterpool);
-
- lock_ctx = apr_pcalloc(iterpool, sizeof(*lock_ctx));
-
- lock_ctx->pool = iterpool;
- lock_ctx->path = svn__apr_hash_index_key(hi);
- lock_ctx->revision = *((svn_revnum_t*)svn__apr_hash_index_val(hi));
- lock_ctx->lock = svn_lock_create(iterpool);
- lock_ctx->lock->path = lock_ctx->path;
- lock_ctx->lock->comment = comment;
-
- lock_ctx->force = force;
- req_url = svn_path_url_add_component2(session->session_url.path,
- lock_ctx->path, iterpool);
-
- xmlctx = svn_ra_serf__xml_context_create(locks_ttable,
- NULL, locks_closed, NULL,
- lock_ctx,
- iterpool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, iterpool);
-
- handler->method = "LOCK";
- handler->path = req_url;
- handler->body_type = "text/xml";
- handler->conn = session->conns[0];
- handler->session = session;
-
- handler->header_delegate = set_lock_headers;
- handler->header_delegate_baton = lock_ctx;
-
- handler->body_delegate = create_lock_body;
- handler->body_delegate_baton = lock_ctx;
-
- lock_ctx->inner_handler = handler->response_handler;
- lock_ctx->inner_baton = handler->response_baton;
- handler->response_handler = handle_lock;
- handler->response_baton = lock_ctx;
-
- lock_ctx->handler = handler;
-
- err = svn_ra_serf__context_run_one(handler, iterpool);
- err = determine_error(handler, err);
-
- if (lock_func)
- new_err = lock_func(lock_baton, lock_ctx->path, TRUE, lock_ctx->lock,
- err, iterpool);
- svn_error_clear(err);
-
- SVN_ERR(new_err);
- }
-
- svn_pool_destroy(iterpool);
-
- return SVN_NO_ERROR;
-}
-
-struct unlock_context_t {
- const char *token;
- svn_boolean_t force;
-};
-
-static svn_error_t *
-set_unlock_headers(serf_bucket_t *headers,
- void *baton,
- apr_pool_t *pool)
-{
- struct unlock_context_t *ctx = baton;
-
- serf_bucket_headers_set(headers, "Lock-Token", ctx->token);
- if (ctx->force)
- {
- serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER,
- SVN_DAV_OPTION_LOCK_BREAK);
- }
-
- return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_ra_serf__unlock(svn_ra_session_t *ra_session,
- apr_hash_t *path_tokens,
- svn_boolean_t force,
- svn_ra_lock_callback_t lock_func,
- void *lock_baton,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__session_t *session = ra_session->priv;
- apr_hash_index_t *hi;
- apr_pool_t *iterpool;
-
- iterpool = svn_pool_create(scratch_pool);
-
- /* ### TODO for issue 2263: Send all the locks over the wire at once. This
- ### loop is just a temporary shim.
- ### an alternative, which is backwards-compat with all servers is to
- ### pipeline these requests. ie. stop using run_wait/run_one. */
-
- for (hi = apr_hash_first(scratch_pool, path_tokens);
- hi;
- hi = apr_hash_next(hi))
- {
- svn_ra_serf__handler_t *handler;
- const char *req_url, *path, *token;
- svn_lock_t *existing_lock = NULL;
- struct unlock_context_t unlock_ctx;
- svn_error_t *err = NULL;
- svn_error_t *new_err = NULL;
-
-
- svn_pool_clear(iterpool);
-
- path = svn__apr_hash_index_key(hi);
- token = svn__apr_hash_index_val(hi);
-
- if (force && (!token || token[0] == '\0'))
- {
- SVN_ERR(svn_ra_serf__get_lock(ra_session, &existing_lock, path,
- iterpool));
- token = existing_lock ? existing_lock->token : NULL;
- if (!token)
- {
- err = svn_error_createf(SVN_ERR_RA_NOT_LOCKED, NULL,
- _("'%s' is not locked in the repository"),
- path);
-
- if (lock_func)
- {
- svn_error_t *err2;
- err2 = lock_func(lock_baton, path, FALSE, NULL, err,
- iterpool);
- svn_error_clear(err);
- err = NULL;
- if (err2)
- return svn_error_trace(err2);
- }
- else
- {
- svn_error_clear(err);
- err = NULL;
- }
- continue;
- }
- }
-
- unlock_ctx.force = force;
- unlock_ctx.token = apr_pstrcat(iterpool, "<", token, ">", (char *)NULL);
-
- req_url = svn_path_url_add_component2(session->session_url.path, path,
- iterpool);
-
- handler = apr_pcalloc(iterpool, sizeof(*handler));
-
- handler->handler_pool = iterpool;
- handler->method = "UNLOCK";
- handler->path = req_url;
- handler->conn = session->conns[0];
- handler->session = session;
-
- handler->header_delegate = set_unlock_headers;
- handler->header_delegate_baton = &unlock_ctx;
-
- handler->response_handler = svn_ra_serf__expect_empty_body;
- handler->response_baton = handler;
-
- SVN_ERR(svn_ra_serf__context_run_one(handler, iterpool));
-
- switch (handler->sline.code)
- {
- case 204:
- break; /* OK */
- case 403:
- /* Api users expect this specific error code to detect failures */
- err = svn_error_createf(SVN_ERR_FS_LOCK_OWNER_MISMATCH, NULL,
- _("Unlock request failed: %d %s"),
- handler->sline.code,
- handler->sline.reason);
- break;
- default:
- err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
- _("Unlock request failed: %d %s"),
- handler->sline.code,
- handler->sline.reason);
- }
-
- if (lock_func)
- new_err = lock_func(lock_baton, path, FALSE, existing_lock, err,
- iterpool);
-
- svn_error_clear(err);
- SVN_ERR(new_err);
- }
-
- svn_pool_destroy(iterpool);
-
- return SVN_NO_ERROR;
-}
diff --git a/subversion/libsvn_ra_serf/log.c b/subversion/libsvn_ra_serf/log.c
index 02f2f29..773ae5c 100644
--- a/subversion/libsvn_ra_serf/log.c
+++ b/subversion/libsvn_ra_serf/log.c
@@ -22,7 +22,8 @@
*/
-
+
+
#include <apr_uri.h>
#include <serf.h>
@@ -44,12 +45,13 @@
#include "ra_serf.h"
#include "../libsvn_ra/ra_loader.h"
-
+
+
/*
* This enum represents the current state of our XML parsing for a REPORT.
*/
-enum {
- INITIAL = 0,
+enum log_state_e {
+ INITIAL = XML_STATE_INITIAL,
REPORT,
ITEM,
VERSION,
@@ -145,7 +147,8 @@ static const svn_ra_serf__xml_transition_t log_ttable[] = {
{ 0 }
};
-
+
+
/* Store CDATA into REVPROPS, associated with PROPNAME. If ENCODING is not
NULL, then it must base "base64" and CDATA will be decoded first.
@@ -207,12 +210,14 @@ collect_path(apr_hash_t *paths,
copyfrom_rev = svn_hash_gets(attrs, "copyfrom-rev");
if (copyfrom_path && copyfrom_rev)
{
- svn_revnum_t rev = SVN_STR_TO_REV(copyfrom_rev);
+ apr_int64_t rev;
+
+ SVN_ERR(svn_cstring_atoi64(&rev, copyfrom_rev));
- if (SVN_IS_VALID_REVNUM(rev))
+ if (SVN_IS_VALID_REVNUM((svn_revnum_t)rev))
{
lcp->copyfrom_path = apr_pstrdup(result_pool, copyfrom_path);
- lcp->copyfrom_rev = rev;
+ lcp->copyfrom_rev = (svn_revnum_t)rev;
}
}
@@ -296,7 +301,12 @@ log_closed(svn_ra_serf__xml_estate_t *xes,
rev_str = svn_hash_gets(attrs, "revision");
if (rev_str)
- log_entry->revision = SVN_STR_TO_REV(rev_str);
+ {
+ apr_int64_t rev;
+
+ SVN_ERR(svn_cstring_atoi64(&rev, rev_str));
+ log_entry->revision = (svn_revnum_t)rev;
+ }
else
log_entry->revision = SVN_INVALID_REVNUM;
@@ -397,12 +407,13 @@ log_closed(svn_ra_serf__xml_estate_t *xes,
return SVN_NO_ERROR;
}
-
+/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_log_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
serf_bucket_t *buckets;
log_context_t *log_ctx = baton;
@@ -412,7 +423,7 @@ create_log_body(serf_bucket_t **body_bkt,
svn_ra_serf__add_open_tag_buckets(buckets, alloc,
"S:log-report",
"xmlns:S", SVN_XML_NAMESPACE,
- NULL);
+ SVN_VA_NULL);
svn_ra_serf__add_tag_buckets(buckets,
"S:start-revision",
@@ -432,23 +443,22 @@ create_log_body(serf_bucket_t **body_bkt,
if (log_ctx->changed_paths)
{
- svn_ra_serf__add_tag_buckets(buckets,
- "S:discover-changed-paths", NULL,
- alloc);
+ svn_ra_serf__add_empty_tag_buckets(buckets, alloc,
+ "S:discover-changed-paths",
+ SVN_VA_NULL);
}
if (log_ctx->strict_node_history)
{
- svn_ra_serf__add_tag_buckets(buckets,
- "S:strict-node-history", NULL,
- alloc);
+ svn_ra_serf__add_empty_tag_buckets(buckets, alloc,
+ "S:strict-node-history", SVN_VA_NULL);
}
if (log_ctx->include_merged_revisions)
{
- svn_ra_serf__add_tag_buckets(buckets,
- "S:include-merged-revisions", NULL,
- alloc);
+ svn_ra_serf__add_empty_tag_buckets(buckets, alloc,
+ "S:include-merged-revisions",
+ SVN_VA_NULL);
}
if (log_ctx->revprops)
@@ -463,16 +473,14 @@ create_log_body(serf_bucket_t **body_bkt,
}
if (log_ctx->revprops->nelts == 0)
{
- svn_ra_serf__add_tag_buckets(buckets,
- "S:no-revprops", NULL,
- alloc);
+ svn_ra_serf__add_empty_tag_buckets(buckets, alloc,
+ "S:no-revprops", SVN_VA_NULL);
}
}
else
{
- svn_ra_serf__add_tag_buckets(buckets,
- "S:all-revprops", NULL,
- alloc);
+ svn_ra_serf__add_empty_tag_buckets(buckets, alloc,
+ "S:all-revprops", SVN_VA_NULL);
}
if (log_ctx->paths)
@@ -487,9 +495,8 @@ create_log_body(serf_bucket_t **body_bkt,
}
}
- svn_ra_serf__add_tag_buckets(buckets,
- "S:encode-binary-props", NULL,
- alloc);
+ svn_ra_serf__add_empty_tag_buckets(buckets, alloc,
+ "S:encode-binary-props", SVN_VA_NULL);
svn_ra_serf__add_close_tag_buckets(buckets, alloc,
"S:log-report");
@@ -518,7 +525,6 @@ svn_ra_serf__get_log(svn_ra_session_t *ra_session,
svn_ra_serf__xml_context_t *xmlctx;
svn_boolean_t want_custom_revprops;
svn_revnum_t peg_rev;
- svn_error_t *err;
const char *req_url;
log_ctx = apr_pcalloc(pool, sizeof(*log_ctx));
@@ -574,7 +580,7 @@ svn_ra_serf__get_log(svn_ra_session_t *ra_session,
peg_rev = (start == SVN_INVALID_REVNUM || start > end) ? start : end;
SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
- session, NULL /* conn */,
+ session,
NULL /* url */, peg_rev,
pool, pool));
@@ -582,23 +588,18 @@ svn_ra_serf__get_log(svn_ra_session_t *ra_session,
log_opened, log_closed, NULL,
log_ctx,
pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool);
handler->method = "REPORT";
handler->path = req_url;
handler->body_delegate = create_log_body;
handler->body_delegate_baton = log_ctx;
handler->body_type = "text/xml";
- handler->conn = session->conns[0];
- handler->session = session;
- err = svn_ra_serf__context_run_one(handler, pool);
+ SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
- SVN_ERR(svn_error_compose_create(
+ return svn_error_trace(
svn_ra_serf__error_on_status(handler->sline,
req_url,
- handler->location),
- err));
-
- return SVN_NO_ERROR;
+ handler->location));
}
diff --git a/subversion/libsvn_ra_serf/merge.c b/subversion/libsvn_ra_serf/merge.c
index 670e421..0a2fd54 100644
--- a/subversion/libsvn_ra_serf/merge.c
+++ b/subversion/libsvn_ra_serf/merge.c
@@ -48,7 +48,7 @@
* This enum represents the current state of our XML parsing for a MERGE.
*/
typedef enum merge_state_e {
- INITIAL = 0,
+ INITIAL = XML_STATE_INITIAL,
MERGE_RESPONSE,
UPDATED_SET,
RESPONSE,
@@ -65,7 +65,7 @@ typedef enum merge_state_e {
AUTHOR,
POST_COMMIT_ERR,
- PROP_VAL
+ STATUS
} merge_state_e;
@@ -171,7 +171,12 @@ merge_closed(svn_ra_serf__xml_estate_t *xes,
rev_str = svn_hash_gets(attrs, "revision");
if (rev_str)
- merge_ctx->commit_info->revision = SVN_STR_TO_REV(rev_str);
+ {
+ apr_int64_t rev;
+
+ SVN_ERR(svn_cstring_atoi64(&rev, rev_str));
+ merge_ctx->commit_info->revision = (svn_revnum_t)rev;
+ }
else
merge_ctx->commit_info->revision = SVN_INVALID_REVNUM;
@@ -266,7 +271,8 @@ merge_closed(svn_ra_serf__xml_estate_t *xes,
static svn_error_t *
setup_merge_headers(serf_bucket_t *headers,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
merge_context_t *ctx = baton;
@@ -294,7 +300,7 @@ svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens,
svn_ra_serf__add_open_tag_buckets(body, alloc,
"S:lock-token-list",
"xmlns:S", SVN_XML_NAMESPACE,
- NULL);
+ SVN_VA_NULL);
for (hi = apr_hash_first(pool, lock_tokens);
hi;
@@ -313,9 +319,9 @@ svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens,
if (parent && !svn_relpath_skip_ancestor(parent, key))
continue;
- svn_ra_serf__add_open_tag_buckets(body, alloc, "S:lock", NULL);
+ svn_ra_serf__add_open_tag_buckets(body, alloc, "S:lock", SVN_VA_NULL);
- svn_ra_serf__add_open_tag_buckets(body, alloc, "lock-path", NULL);
+ svn_ra_serf__add_open_tag_buckets(body, alloc, "lock-path", SVN_VA_NULL);
svn_ra_serf__add_cdata_len_buckets(body, alloc, path.data, path.len);
svn_ra_serf__add_close_tag_buckets(body, alloc, "lock-path");
@@ -327,11 +333,13 @@ svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens,
svn_ra_serf__add_close_tag_buckets(body, alloc, "S:lock-token-list");
}
+/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t*
create_merge_body(serf_bucket_t **bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
merge_context_t *ctx = baton;
serf_bucket_t *body_bkt;
@@ -341,9 +349,9 @@ create_merge_body(serf_bucket_t **bkt,
svn_ra_serf__add_xml_header_buckets(body_bkt, alloc);
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:merge",
"xmlns:D", "DAV:",
- NULL);
- svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:source", NULL);
- svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:href", NULL);
+ SVN_VA_NULL);
+ svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:source", SVN_VA_NULL);
+ svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:href", SVN_VA_NULL);
svn_ra_serf__add_cdata_len_buckets(body_bkt, alloc,
ctx->merge_resource_url,
@@ -352,19 +360,26 @@ create_merge_body(serf_bucket_t **bkt,
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:href");
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:source");
- svn_ra_serf__add_tag_buckets(body_bkt, "D:no-auto-merge", NULL, alloc);
- svn_ra_serf__add_tag_buckets(body_bkt, "D:no-checkout", NULL, alloc);
-
- svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL);
- svn_ra_serf__add_tag_buckets(body_bkt, "D:checked-in", NULL, alloc);
- svn_ra_serf__add_tag_buckets(body_bkt, "D:" SVN_DAV__VERSION_NAME, NULL, alloc);
- svn_ra_serf__add_tag_buckets(body_bkt, "D:resourcetype", NULL, alloc);
- svn_ra_serf__add_tag_buckets(body_bkt, "D:" SVN_DAV__CREATIONDATE, NULL, alloc);
- svn_ra_serf__add_tag_buckets(body_bkt, "D:creator-displayname", NULL, alloc);
+ svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc,
+ "D:no-auto-merge", SVN_VA_NULL);
+ svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc,
+ "D:no-checkout", SVN_VA_NULL);
+
+ svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", SVN_VA_NULL);
+ svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc,
+ "D:checked-in", SVN_VA_NULL);
+ svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc,
+ "D:" SVN_DAV__VERSION_NAME, SVN_VA_NULL);
+ svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc,
+ "D:resourcetype", SVN_VA_NULL);
+ svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc,
+ "D:" SVN_DAV__CREATIONDATE, SVN_VA_NULL);
+ svn_ra_serf__add_empty_tag_buckets(body_bkt, alloc,
+ "D:creator-displayname", SVN_VA_NULL);
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop");
- svn_ra_serf__merge_lock_token_list(ctx->lock_tokens, NULL, body_bkt, alloc,
- pool);
+ svn_ra_serf__merge_lock_token_list(ctx->lock_tokens, NULL, body_bkt,
+ alloc, pool);
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:merge");
@@ -376,9 +391,7 @@ create_merge_body(serf_bucket_t **bkt,
svn_error_t *
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,
@@ -407,14 +420,14 @@ svn_ra_serf__run_merge(const svn_commit_info_t **commit_info,
NULL, merge_closed, NULL,
merge_ctx,
scratch_pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, scratch_pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL,
+ scratch_pool);
handler->method = "MERGE";
handler->path = merge_ctx->merge_url;
handler->body_delegate = create_merge_body;
handler->body_delegate_baton = merge_ctx;
- handler->conn = conn;
- handler->session = session;
+ handler->body_type = "text/xml";
handler->header_delegate = setup_merge_headers;
handler->header_delegate_baton = merge_ctx;
@@ -423,8 +436,21 @@ svn_ra_serf__run_merge(const svn_commit_info_t **commit_info,
SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
+ if (handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
+
*commit_info = merge_ctx->commit_info;
- *response_code = handler->sline.code;
+
+ /* Sanity check (Reported to be triggered by CodePlex's svnbridge) */
+ if (! SVN_IS_VALID_REVNUM(merge_ctx->commit_info->revision))
+ {
+ return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL,
+ _("The MERGE response did not include "
+ "a new revision"));
+ }
+
+ merge_ctx->commit_info->repos_root = apr_pstrdup(result_pool,
+ session->repos_root_str);
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_ra_serf/mergeinfo.c b/subversion/libsvn_ra_serf/mergeinfo.c
index bd4fcba..589ff07 100644
--- a/subversion/libsvn_ra_serf/mergeinfo.c
+++ b/subversion/libsvn_ra_serf/mergeinfo.c
@@ -41,7 +41,7 @@
/* The current state of our XML parsing. */
typedef enum mergeinfo_state_e {
- INITIAL = 0,
+ INITIAL = XML_STATE_INITIAL,
MERGEINFO_REPORT,
MERGEINFO_ITEM,
MERGEINFO_PATH,
@@ -130,12 +130,13 @@ mergeinfo_closed(svn_ra_serf__xml_estate_t *xes,
return SVN_NO_ERROR;
}
-
+/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_mergeinfo_body(serf_bucket_t **bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
mergeinfo_context_t *mergeinfo_ctx = baton;
serf_bucket_t *body_bkt;
@@ -145,7 +146,7 @@ create_mergeinfo_body(serf_bucket_t **bkt,
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
"S:" SVN_DAV__MERGEINFO_REPORT,
"xmlns:S", SVN_XML_NAMESPACE,
- NULL);
+ SVN_VA_NULL);
svn_ra_serf__add_tag_buckets(body_bkt,
"S:" SVN_DAV__REVISION,
@@ -191,7 +192,6 @@ svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session,
svn_boolean_t include_descendants,
apr_pool_t *pool)
{
- svn_error_t *err;
mergeinfo_context_t *mergeinfo_ctx;
svn_ra_serf__session_t *session = ra_session->priv;
svn_ra_serf__handler_t *handler;
@@ -201,7 +201,7 @@ svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session,
*catalog = NULL;
SVN_ERR(svn_ra_serf__get_stable_url(&path, NULL /* latest_revnum */,
- session, NULL /* conn */,
+ session,
NULL /* url */, revision,
pool, pool));
@@ -217,24 +217,21 @@ svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session,
NULL, mergeinfo_closed, NULL,
mergeinfo_ctx,
pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool);
handler->method = "REPORT";
handler->path = path;
- handler->conn = session->conns[0];
- handler->session = session;
+
handler->body_delegate = create_mergeinfo_body;
handler->body_delegate_baton = mergeinfo_ctx;
handler->body_type = "text/xml";
- err = svn_ra_serf__context_run_one(handler, pool);
+ SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
- SVN_ERR(svn_error_compose_create(
- svn_ra_serf__error_on_status(handler->sline, handler->path,
- handler->location),
- err));
+ SVN_ERR(svn_ra_serf__error_on_status(handler->sline, handler->path,
+ handler->location));
- if (handler->done && apr_hash_count(mergeinfo_ctx->result_catalog))
+ if (apr_hash_count(mergeinfo_ctx->result_catalog))
*catalog = mergeinfo_ctx->result_catalog;
return SVN_NO_ERROR;
diff --git a/subversion/libsvn_ra_serf/multistatus.c b/subversion/libsvn_ra_serf/multistatus.c
new file mode 100644
index 0000000..9c269c3
--- /dev/null
+++ b/subversion/libsvn_ra_serf/multistatus.c
@@ -0,0 +1,750 @@
+/*
+ * multistatus.c : parse multistatus (error) responses.
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+
+
+#include <assert.h>
+
+#include <apr.h>
+
+#include <serf.h>
+#include <serf_bucket_types.h>
+
+#include "svn_private_config.h"
+#include "svn_hash.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_string.h"
+#include "svn_xml.h"
+#include "svn_props.h"
+#include "svn_dirent_uri.h"
+
+#include "private/svn_dep_compat.h"
+#include "private/svn_fspath.h"
+
+#include "ra_serf.h"
+
+/* The current state of our XML parsing. */
+typedef enum iprops_state_e {
+ INITIAL = XML_STATE_INITIAL,
+ MS_MULTISTATUS,
+
+ MS_RESPONSE,
+ MS_RESPONSE_HREF,
+
+ MS_PROPSTAT,
+ MS_PROPSTAT_PROP,
+ MS_PROPSTAT_PROP_NAME,
+ MS_PROPSTAT_STATUS,
+ MS_PROPSTAT_RESPONSEDESCRIPTION,
+ MS_PROPSTAT_ERROR,
+ MS_PROPSTAT_ERROR_HUMANREADABLE,
+
+ MS_RESPONSE_STATUS,
+ MS_RESPONSE_RESPONSEDESCRIPTION,
+ MS_RESPONSE_ERROR,
+ MS_RESPONSE_ERROR_HUMANREADABLE,
+
+ MS_MULTISTATUS_RESPONSEDESCRIPTION,
+
+ D_ERROR,
+ S_ERROR,
+ M_ERROR_HUMANREADABLE
+} iprops_state_e;
+
+/*
+ <D:multistatus xmlns:D="DAV:">
+ <D:response>
+ <D:href>http://something</D:href>
+ <!-- Possibly multiple D:href elements -->
+ <D:status>HTTP/1.1 500 failed</D:status>
+ <D:error>
+ <S:human-readable code="12345">
+ Some Subversion error
+ </S:human-readable>
+ </D:error>
+ <D:responsedescription>
+ Human readable description
+ </D:responsedescription>
+ <D:location>http://redirected</D:location>
+ </D:response>
+ ...
+ </D:multistatus>
+
+ Or for property operations:
+
+ <D:multistatus xmlns:D="DAV:">
+ <D:response>
+ <D:href>http://somewhere-else</D:href>
+ <D:propstat>
+ <D:propname><C:myprop /></D:propname>
+ <D:status>HTTP/1.1 499 failed</D:status>
+ <D:error>
+ <S:human-readable code="12345">
+ Some Subversion error
+ </S:human-readable>
+ </D:error>
+ <D:responsedescription>
+ Human readable description
+ </D:responsedescription>
+ </D:propstat>
+ <D:status>HTTP/1.1 499 failed</D:status>
+ <D:error>
+ <S:human-readable code="12345">
+ Some Subversion error
+ </S:human-readable>
+ </D:error>
+ <D:responsedescription>
+ Human readable description
+ </D:responsedescription>
+ <D:location>http://redirected</D:location>
+ <D:responsedescription>
+ Global description
+ <D:responsedescription>
+ </D:multistatus>
+
+ Or on request failures
+ <D:error>
+ <X:some-error xmlns="QQ" />
+ <D:human-readable code="12345">
+ Some Subversion error
+ </D:human-readable>
+ </D:error>
+ */
+
+#define D_ "DAV:"
+#define S_ SVN_XML_NAMESPACE
+#define M_ "http://apache.org/dav/xmlns"
+static const svn_ra_serf__xml_transition_t multistatus_ttable[] = {
+ { INITIAL, D_, "multistatus", MS_MULTISTATUS,
+ FALSE, { NULL }, FALSE },
+
+ { MS_MULTISTATUS, D_, "responsedescription", MS_MULTISTATUS_RESPONSEDESCRIPTION,
+ TRUE, { NULL }, TRUE },
+
+ /* <response> */
+ { MS_MULTISTATUS, D_, "response", MS_RESPONSE,
+ FALSE, { NULL }, TRUE },
+
+ { MS_RESPONSE, D_, "href", MS_RESPONSE_HREF,
+ TRUE, { NULL }, TRUE },
+
+ /* <propstat> */
+ { MS_RESPONSE, D_, "propstat", MS_PROPSTAT,
+ FALSE, { NULL }, TRUE },
+
+ { MS_PROPSTAT, D_, "prop", MS_PROPSTAT_PROP,
+ FALSE, { NULL }, FALSE },
+
+ { MS_PROPSTAT_PROP, "", "*", MS_PROPSTAT_PROP_NAME,
+ FALSE, { NULL }, FALSE },
+
+ { MS_PROPSTAT, D_, "status", MS_PROPSTAT_STATUS,
+ TRUE, { NULL }, TRUE },
+
+ { MS_PROPSTAT, D_, "responsedescription", MS_PROPSTAT_RESPONSEDESCRIPTION,
+ TRUE, { NULL }, TRUE },
+
+ { MS_PROPSTAT, D_, "error", MS_PROPSTAT_ERROR,
+ FALSE, { NULL }, FALSE },
+
+ { MS_PROPSTAT_ERROR, M_, "human-readable", MS_PROPSTAT_ERROR_HUMANREADABLE,
+ TRUE, { "?errcode", NULL }, TRUE },
+ /* </propstat> */
+
+
+ { MS_RESPONSE, D_, "status", MS_RESPONSE_STATUS,
+ TRUE, { NULL }, TRUE },
+
+ { MS_RESPONSE, D_, "responsedescription", MS_RESPONSE_RESPONSEDESCRIPTION,
+ TRUE, { NULL }, TRUE },
+
+ { MS_RESPONSE, D_, "error", MS_RESPONSE_ERROR,
+ FALSE, { NULL }, TRUE },
+
+ { MS_RESPONSE_ERROR, M_, "human-readable", MS_RESPONSE_ERROR_HUMANREADABLE,
+ TRUE, { "?errcode", NULL }, TRUE },
+
+ /* </response> */
+
+ { MS_MULTISTATUS, D_, "responsedescription", MS_MULTISTATUS_RESPONSEDESCRIPTION,
+ TRUE, { NULL }, TRUE },
+
+
+ { INITIAL, D_, "error", D_ERROR,
+ FALSE, { NULL }, TRUE },
+
+ { D_ERROR, S_, "error", S_ERROR,
+ FALSE, { NULL }, FALSE },
+
+ { D_ERROR, M_, "human-readable", M_ERROR_HUMANREADABLE,
+ TRUE, { "?errcode", NULL }, TRUE },
+
+ { 0 }
+};
+
+/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric
+ status code into *STATUS_CODE_OUT. Ignores leading whitespace. */
+static svn_error_t *
+parse_status_line(int *status_code_out,
+ const char **reason,
+ const char *status_line,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+ const char *token;
+ char *tok_status;
+ svn_stringbuf_t *temp_buf = svn_stringbuf_create(status_line, scratch_pool);
+
+ svn_stringbuf_strip_whitespace(temp_buf);
+ token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status);
+ if (token)
+ token = apr_strtok(NULL, " \t\r\n", &tok_status);
+ if (!token)
+ return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+ _("Malformed DAV:status '%s'"),
+ status_line);
+ err = svn_cstring_atoi(status_code_out, token);
+ if (err)
+ return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
+ _("Malformed DAV:status '%s'"),
+ status_line);
+
+ token = apr_strtok(NULL, " \t\r\n", &tok_status);
+
+ *reason = apr_pstrdup(result_pool, token);
+
+ return SVN_NO_ERROR;
+}
+
+
+typedef struct error_item_t
+{
+ const char *path;
+ const char *propname;
+
+ int http_status;
+ const char *http_reason;
+ apr_status_t apr_err;
+
+ const char *message;
+} error_item_t;
+
+static svn_error_t *
+multistatus_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)
+{
+ /*struct svn_ra_serf__server_error_t *server_error = baton;*/
+ const char *propname;
+
+ switch (entered_state)
+ {
+ case MS_PROPSTAT_PROP_NAME:
+ if (strcmp(tag->xmlns, SVN_DAV_PROP_NS_SVN) == 0)
+ propname = apr_pstrcat(scratch_pool, SVN_PROP_PREFIX, tag->name,
+ SVN_VA_NULL);
+ else
+ propname = tag->name;
+ svn_ra_serf__xml_note(xes, MS_PROPSTAT, "propname", propname);
+ break;
+ case S_ERROR:
+ /* This toggles an has error boolean in libsvn_ra_neon in 1.7 */
+ break;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+multistatus_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)
+{
+ struct svn_ra_serf__server_error_t *server_error = baton;
+ const char *errcode;
+ const char *status;
+
+ switch (leaving_state)
+ {
+ case MS_RESPONSE_HREF:
+ {
+ apr_status_t result;
+ apr_uri_t uri;
+
+ result = apr_uri_parse(scratch_pool, cdata->data, &uri);
+ if (result)
+ return svn_ra_serf__wrap_err(result, NULL);
+ svn_ra_serf__xml_note(xes, MS_RESPONSE, "path",
+ svn_urlpath__canonicalize(uri.path, scratch_pool));
+ }
+ break;
+ case MS_RESPONSE_STATUS:
+ svn_ra_serf__xml_note(xes, MS_RESPONSE, "status", cdata->data);
+ break;
+ case MS_RESPONSE_ERROR_HUMANREADABLE:
+ svn_ra_serf__xml_note(xes, MS_RESPONSE, "human-readable", cdata->data);
+ errcode = svn_hash_gets(attrs, "errcode");
+ if (errcode)
+ svn_ra_serf__xml_note(xes, MS_RESPONSE, "errcode", errcode);
+ break;
+ case MS_RESPONSE:
+ if ((status = svn_hash__get_cstring(attrs, "status", NULL)) != NULL)
+ {
+ error_item_t *item;
+
+ item = apr_pcalloc(server_error->pool, sizeof(*item));
+
+ item->path = apr_pstrdup(server_error->pool,
+ svn_hash_gets(attrs, "path"));
+
+ SVN_ERR(parse_status_line(&item->http_status,
+ &item->http_reason,
+ status,
+ server_error->pool,
+ scratch_pool));
+
+ /* Do we have a mod_dav specific message? */
+ item->message = svn_hash_gets(attrs, "human-readable");
+
+ if (item->message)
+ {
+ if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL)
+ {
+ apr_int64_t val;
+
+ SVN_ERR(svn_cstring_atoi64(&val, errcode));
+ item->apr_err = (apr_status_t)val;
+ }
+
+ item->message = apr_pstrdup(server_error->pool, item->message);
+ }
+ else
+ item->message = apr_pstrdup(server_error->pool,
+ svn_hash_gets(attrs, "description"));
+
+ APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
+ }
+ break;
+
+
+ case MS_PROPSTAT_STATUS:
+ svn_ra_serf__xml_note(xes, MS_PROPSTAT, "status", cdata->data);
+ break;
+ case MS_PROPSTAT_ERROR_HUMANREADABLE:
+ svn_ra_serf__xml_note(xes, MS_PROPSTAT, "human-readable", cdata->data);
+ errcode = svn_hash_gets(attrs, "errcode");
+ if (errcode)
+ svn_ra_serf__xml_note(xes, MS_PROPSTAT, "errcode", errcode);
+ break;
+ case MS_PROPSTAT_RESPONSEDESCRIPTION:
+ svn_ra_serf__xml_note(xes, MS_PROPSTAT, "description",
+ cdata->data);
+ break;
+
+ case MS_PROPSTAT:
+ if ((status = svn_hash__get_cstring(attrs, "status", NULL)) != NULL)
+ {
+ apr_hash_t *response_attrs;
+ error_item_t *item;
+
+ response_attrs = svn_ra_serf__xml_gather_since(xes, MS_RESPONSE);
+ item = apr_pcalloc(server_error->pool, sizeof(*item));
+
+ item->path = apr_pstrdup(server_error->pool,
+ svn_hash_gets(response_attrs, "path"));
+ item->propname = apr_pstrdup(server_error->pool,
+ svn_hash_gets(attrs, "propname"));
+
+ SVN_ERR(parse_status_line(&item->http_status,
+ &item->http_reason,
+ status,
+ server_error->pool,
+ scratch_pool));
+
+ /* Do we have a mod_dav specific message? */
+ item->message = svn_hash_gets(attrs, "human-readable");
+
+ if (item->message)
+ {
+ if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL)
+ {
+ apr_int64_t val;
+
+ SVN_ERR(svn_cstring_atoi64(&val, errcode));
+ item->apr_err = (apr_status_t)val;
+ }
+
+ item->message = apr_pstrdup(server_error->pool, item->message);
+ }
+ else
+ item->message = apr_pstrdup(server_error->pool,
+ svn_hash_gets(attrs, "description"));
+
+
+ APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
+ }
+ break;
+
+ case M_ERROR_HUMANREADABLE:
+ svn_ra_serf__xml_note(xes, D_ERROR, "human-readable", cdata->data);
+ errcode = svn_hash_gets(attrs, "errcode");
+ if (errcode)
+ svn_ra_serf__xml_note(xes, D_ERROR, "errcode", errcode);
+ break;
+
+ case D_ERROR:
+ {
+ error_item_t *item;
+
+ item = apr_pcalloc(server_error->pool, sizeof(*item));
+
+ item->http_status = server_error->handler->sline.code;
+
+ /* Do we have a mod_dav specific message? */
+ item->message = svn_hash__get_cstring(attrs, "human-readable",
+ NULL);
+
+ if (item->message)
+ {
+ if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL)
+ {
+ apr_int64_t val;
+
+ SVN_ERR(svn_cstring_atoi64(&val, errcode));
+ item->apr_err = (apr_status_t)val;
+ }
+
+ item->message = apr_pstrdup(server_error->pool, item->message);
+ }
+
+
+ APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
+ }
+ }
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra_serf__server_error_create(svn_ra_serf__handler_t *handler,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_serf__server_error_t *server_error = handler->server_error;
+ svn_error_t *err = NULL;
+ int i;
+
+ for (i = 0; i < server_error->items->nelts; i++)
+ {
+ const error_item_t *item;
+ apr_status_t status;
+ const char *message;
+ svn_error_t *new_err;
+
+ item = APR_ARRAY_IDX(server_error->items, i, error_item_t *);
+
+ if (!item->apr_err && item->http_status == 200)
+ {
+ continue; /* Success code */
+ }
+ else if (!item->apr_err && item->http_status == 424 && item->propname)
+ {
+ continue; /* Failed because other PROPPATCH operations failed */
+ }
+
+ if (item->apr_err)
+ status = item->apr_err;
+ else
+ switch (item->http_status)
+ {
+ case 0:
+ continue; /* Not an error */
+ case 301:
+ case 302:
+ case 303:
+ case 307:
+ case 308:
+ status = SVN_ERR_RA_DAV_RELOCATED;
+ break;
+ case 403:
+ status = SVN_ERR_RA_DAV_FORBIDDEN;
+ break;
+ case 404:
+ status = SVN_ERR_FS_NOT_FOUND;
+ break;
+ case 409:
+ status = SVN_ERR_FS_CONFLICT;
+ break;
+ case 412:
+ status = SVN_ERR_RA_DAV_PRECONDITION_FAILED;
+ break;
+ case 423:
+ status = SVN_ERR_FS_NO_LOCK_TOKEN;
+ break;
+ case 500:
+ status = SVN_ERR_RA_DAV_REQUEST_FAILED;
+ break;
+ case 501:
+ status = SVN_ERR_UNSUPPORTED_FEATURE;
+ break;
+ default:
+ if (err)
+ status = err->apr_err; /* Just use previous */
+ else
+ status = SVN_ERR_RA_DAV_REQUEST_FAILED;
+ break;
+ }
+
+ if (item->message && *item->message)
+ {
+ svn_stringbuf_t *sb = svn_stringbuf_create(item->message,
+ scratch_pool);
+
+ svn_stringbuf_strip_whitespace(sb);
+ message = sb->data;
+ }
+ else if (item->propname)
+ {
+ message = apr_psprintf(scratch_pool,
+ _("Property operation on '%s' failed"),
+ item->propname);
+ }
+ else
+ {
+ /* Yuck: Older servers sometimes assume that we get convertable
+ apr error codes, while mod_dav_svn just produces a blank
+ text error, because err->message is NULL. */
+ serf_status_line sline;
+ svn_error_t *tmp_err;
+
+ memset(&sline, 0, sizeof(sline));
+ sline.code = item->http_status;
+ sline.reason = item->http_reason;
+
+ tmp_err = svn_ra_serf__error_on_status(sline, item->path, NULL);
+
+ message = (tmp_err && tmp_err->message)
+ ? apr_pstrdup(scratch_pool, tmp_err->message)
+ : _("<blank error>");
+ svn_error_clear(tmp_err);
+ }
+
+ SVN_ERR_ASSERT(status > 0);
+ new_err = svn_error_create(status, NULL, message);
+
+ if (item->propname)
+ new_err = svn_error_createf(new_err->apr_err, new_err,
+ _("While handling the '%s' property on '%s':"),
+ item->propname, item->path);
+ else if (item->path)
+ new_err = svn_error_createf(new_err->apr_err, new_err,
+ _("While handling the '%s' path:"),
+ item->path);
+
+ err = svn_error_compose_create(
+ err,
+ new_err);
+ }
+
+ /* Theoretically a 207 status can have a 'global' description without a
+ global STATUS that summarizes the final result of property/href
+ operations.
+
+ We should wrap that around the existing errors if there is one.
+
+ But currently I don't see how mod_dav ever sets that value */
+
+ if (!err)
+ {
+ /* We should fail.... but why... Who installed us? */
+ err = svn_error_trace(svn_ra_serf__unexpected_status(handler));
+ }
+
+ return err;
+}
+
+
+svn_error_t *
+svn_ra_serf__setup_error_parsing(svn_ra_serf__server_error_t **server_err,
+ svn_ra_serf__handler_t *handler,
+ svn_boolean_t expect_207_only,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_serf__server_error_t *ms_baton;
+ svn_ra_serf__handler_t *tmp_handler;
+
+ int *expected_status = apr_pcalloc(result_pool,
+ 2 * sizeof(expected_status[0]));
+
+ expected_status[0] = handler->sline.code;
+
+ ms_baton = apr_pcalloc(result_pool, sizeof(*ms_baton));
+ ms_baton->pool = result_pool;
+
+ ms_baton->items = apr_array_make(result_pool, 4, sizeof(error_item_t *));
+ ms_baton->handler = handler;
+
+ ms_baton->xmlctx = svn_ra_serf__xml_context_create(multistatus_ttable,
+ multistatus_opened,
+ multistatus_closed,
+ NULL,
+ ms_baton,
+ ms_baton->pool);
+
+ tmp_handler = svn_ra_serf__create_expat_handler(handler->session,
+ ms_baton->xmlctx,
+ expected_status,
+ result_pool);
+
+ /* Ugly way to obtain expat_handler() */
+ tmp_handler->sline = handler->sline;
+ ms_baton->response_handler = tmp_handler->response_handler;
+ ms_baton->response_baton = tmp_handler->response_baton;
+
+ *server_err = ms_baton;
+ return SVN_NO_ERROR;
+}
+
+
+
+/* Implements svn_ra_serf__response_handler_t */
+svn_error_t *
+svn_ra_serf__handle_multistatus_only(serf_request_t *request,
+ serf_bucket_t *response,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_serf__handler_t *handler = baton;
+
+ /* This function is just like expect_empty_body() except for the
+ XML parsing callbacks. We are looking for very limited pieces of
+ the multistatus response. */
+
+ /* We should see this just once, in order to initialize SERVER_ERROR.
+ At that point, the core error processing will take over. If we choose
+ not to parse an error, then we'll never return here (because we
+ change the response handler). */
+ SVN_ERR_ASSERT(handler->server_error == NULL);
+
+ {
+ serf_bucket_t *hdrs;
+ const char *val;
+
+ hdrs = serf_bucket_response_get_headers(response);
+ val = serf_bucket_headers_get(hdrs, "Content-Type");
+ if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
+ {
+ svn_ra_serf__server_error_t *server_err;
+
+ SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err,
+ handler,
+ TRUE,
+ handler->handler_pool,
+ handler->handler_pool));
+
+ handler->server_error = server_err;
+ }
+ else
+ {
+ /* The body was not text/xml, so we don't know what to do with it.
+ Toss anything that arrives. */
+ handler->discard_body = TRUE;
+ }
+ }
+
+ /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it
+ to call the response handler again. That will start up the XML parsing,
+ or it will be dropped on the floor (per the decision above). */
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra_serf__handle_server_error(svn_ra_serf__server_error_t *server_error,
+ svn_ra_serf__handler_t *handler,
+ serf_request_t *request,
+ serf_bucket_t *response,
+ apr_status_t *serf_status,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+
+ err = server_error->response_handler(request, response,
+ server_error->response_baton,
+ scratch_pool);
+ /* If we do not receive an error or it is a non-transient error, return
+ immediately.
+
+ 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))
+ {
+ *serf_status = err->apr_err;
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+
+ /* Clear the EOF. We don't need it as subversion error. */
+ svn_error_clear(err);
+ *serf_status = APR_EOF;
+
+ /* On PROPPATCH we always get status 207, which may or may not imply an
+ error status, but let's keep it generic and just do the check for
+ any multistatus */
+ if (handler->sline.code == 207 /* MULTISTATUS */)
+ {
+ svn_boolean_t have_error = FALSE;
+ int i;
+
+ for (i = 0; i < server_error->items->nelts; i++)
+ {
+ const error_item_t *item;
+ item = APR_ARRAY_IDX(server_error->items, i, error_item_t *);
+
+ if (!item->apr_err && item->http_status == 200)
+ {
+ continue; /* Success code */
+ }
+
+ have_error = TRUE;
+ break;
+ }
+
+ if (! have_error)
+ handler->server_error = NULL; /* We didn't have a server error */
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/libsvn_ra_serf/options.c b/subversion/libsvn_ra_serf/options.c
index 5389b04..a52977b 100644
--- a/subversion/libsvn_ra_serf/options.c
+++ b/subversion/libsvn_ra_serf/options.c
@@ -30,6 +30,7 @@
#include "svn_dirent_uri.h"
#include "svn_hash.h"
#include "svn_pools.h"
+#include "svn_path.h"
#include "svn_ra.h"
#include "svn_dav.h"
#include "svn_xml.h"
@@ -51,7 +52,7 @@
* This enum represents the current state of our XML parsing for an OPTIONS.
*/
enum options_state_e {
- INITIAL = 0,
+ INITIAL = XML_STATE_INITIAL,
OPTIONS,
ACTIVITY_COLLECTION,
HREF
@@ -65,7 +66,6 @@ typedef struct options_context_t {
svn_boolean_t headers_processed;
svn_ra_serf__session_t *session;
- svn_ra_serf__connection_t *conn;
svn_ra_serf__handler_t *handler;
svn_ra_serf__response_handler_t inner_handler;
@@ -112,19 +112,20 @@ options_closed(svn_ra_serf__xml_estate_t *xes,
return SVN_NO_ERROR;
}
-
+/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_options_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
serf_bucket_t *body;
body = serf_bucket_aggregate_create(alloc);
svn_ra_serf__add_xml_header_buckets(body, alloc);
svn_ra_serf__add_open_tag_buckets(body, alloc, "D:options",
"xmlns:D", "DAV:",
- NULL);
+ SVN_VA_NULL);
svn_ra_serf__add_tag_buckets(body, "D:activity-collection-set", NULL, alloc);
svn_ra_serf__add_close_tag_buckets(body, alloc, "D:options");
@@ -372,7 +373,7 @@ options_response_handler(serf_request_t *request,
serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback,
opt_ctx);
- /* Assume mergeinfo capability unsupported, if didn't recieve information
+ /* Assume mergeinfo capability unsupported, if didn't receive information
about server or repository mergeinfo capability. */
if (!svn_hash_gets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO))
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
@@ -389,7 +390,6 @@ options_response_handler(serf_request_t *request,
static svn_error_t *
create_options_req(options_context_t **opt_ctx,
svn_ra_serf__session_t *session,
- svn_ra_serf__connection_t *conn,
apr_pool_t *pool)
{
options_context_t *new_ctx;
@@ -399,7 +399,6 @@ create_options_req(options_context_t **opt_ctx,
new_ctx = apr_pcalloc(pool, sizeof(*new_ctx));
new_ctx->pool = pool;
new_ctx->session = session;
- new_ctx->conn = conn;
new_ctx->youngest_rev = SVN_INVALID_REVNUM;
@@ -407,14 +406,12 @@ create_options_req(options_context_t **opt_ctx,
NULL, options_closed, NULL,
new_ctx,
pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool);
handler->method = "OPTIONS";
handler->path = session->session_url.path;
handler->body_delegate = create_options_body;
handler->body_type = "text/xml";
- handler->conn = conn;
- handler->session = session;
new_ctx->handler = handler;
@@ -431,22 +428,25 @@ create_options_req(options_context_t **opt_ctx,
svn_error_t *
svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest,
- svn_ra_serf__connection_t *conn,
+ svn_ra_serf__session_t *session,
apr_pool_t *scratch_pool)
{
- svn_ra_serf__session_t *session = conn->session;
options_context_t *opt_ctx;
SVN_ERR_ASSERT(SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session));
- SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool));
+ SVN_ERR(create_options_req(&opt_ctx, session, scratch_pool));
SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool));
- SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline,
- opt_ctx->handler->path,
- opt_ctx->handler->location));
+
+ if (opt_ctx->handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(opt_ctx->handler));
+
+ if (! SVN_IS_VALID_REVNUM(opt_ctx->youngest_rev))
+ return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
+ _("The OPTIONS response did not include "
+ "the youngest revision"));
*youngest = opt_ctx->youngest_rev;
- SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(*youngest));
return SVN_NO_ERROR;
}
@@ -454,21 +454,39 @@ svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest,
svn_error_t *
svn_ra_serf__v1_get_activity_collection(const char **activity_url,
- svn_ra_serf__connection_t *conn,
+ svn_ra_serf__session_t *session,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- svn_ra_serf__session_t *session = conn->session;
options_context_t *opt_ctx;
SVN_ERR_ASSERT(!SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session));
- SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool));
+ if (session->activity_collection_url)
+ {
+ *activity_url = apr_pstrdup(result_pool,
+ session->activity_collection_url);
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(create_options_req(&opt_ctx, session, scratch_pool));
SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool));
- SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline,
- opt_ctx->handler->path,
- opt_ctx->handler->location));
+ if (opt_ctx->handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(opt_ctx->handler));
+
+ /* Cache the result. */
+ if (opt_ctx->activity_collection)
+ {
+ session->activity_collection_url =
+ apr_pstrdup(session->pool, opt_ctx->activity_collection);
+ }
+ else
+ {
+ return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
+ _("The OPTIONS response did not include the "
+ "requested activity-collection-set value"));
+ }
*activity_url = apr_pstrdup(result_pool, opt_ctx->activity_collection);
@@ -483,15 +501,20 @@ svn_ra_serf__v1_get_activity_collection(const char **activity_url,
svn_error_t *
svn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess,
const char **corrected_url,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
options_context_t *opt_ctx;
- svn_error_t *err;
+
+ if (corrected_url)
+ *corrected_url = NULL;
/* This routine automatically fills in serf_sess->capabilities */
- SVN_ERR(create_options_req(&opt_ctx, serf_sess, serf_sess->conns[0], pool));
+ SVN_ERR(create_options_req(&opt_ctx, serf_sess, scratch_pool));
- err = svn_ra_serf__context_run_one(opt_ctx->handler, pool);
+ opt_ctx->handler->no_fail_on_http_redirect_status = TRUE;
+
+ SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool));
/* If our caller cares about server redirections, and our response
carries such a thing, report as much. We'll disregard ERR --
@@ -499,16 +522,47 @@ svn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess,
successfully parsing as XML or somesuch. */
if (corrected_url && (opt_ctx->handler->sline.code == 301))
{
- svn_error_clear(err);
- *corrected_url = opt_ctx->handler->location;
+ if (!opt_ctx->handler->location || !*opt_ctx->handler->location)
+ {
+ return svn_error_create(
+ SVN_ERR_RA_DAV_RESPONSE_HEADER_BADNESS, NULL,
+ _("Location header not set on redirect response"));
+ }
+ else if (svn_path_is_url(opt_ctx->handler->location))
+ {
+ *corrected_url = svn_uri_canonicalize(opt_ctx->handler->location,
+ result_pool);
+ }
+ else
+ {
+ /* RFC1945 and RFC2616 state that the Location header's value
+ (from whence this CORRECTED_URL comes), if present, must be an
+ absolute URI. But some Apache versions (those older than 2.2.11,
+ it seems) transmit only the path portion of the URI.
+ See issue #3775 for details. */
+
+ apr_uri_t corrected_URI = serf_sess->session_url;
+
+ corrected_URI.path = (char *)corrected_url;
+ *corrected_url = svn_uri_canonicalize(
+ apr_uri_unparse(scratch_pool, &corrected_URI, 0),
+ result_pool);
+ }
+
return SVN_NO_ERROR;
}
+ else if (opt_ctx->handler->sline.code >= 300
+ && opt_ctx->handler->sline.code < 399)
+ {
+ return svn_error_createf(SVN_ERR_RA_SESSION_URL_MISMATCH, NULL,
+ (opt_ctx->handler->sline.code == 301
+ ? _("Repository moved permanently to '%s'")
+ : _("Repository moved temporarily to '%s'")),
+ opt_ctx->handler->location);
+ }
- SVN_ERR(svn_error_compose_create(
- svn_ra_serf__error_on_status(opt_ctx->handler->sline,
- serf_sess->session_url.path,
- opt_ctx->handler->location),
- err));
+ if (opt_ctx->handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(opt_ctx->handler));
/* Opportunistically cache any reported activity URL. (We don't
want to have to ask for this again later, potentially against an
@@ -522,12 +576,13 @@ svn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess,
return SVN_NO_ERROR;
}
-
+/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_simple_options_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
serf_bucket_t *body;
serf_bucket_t *s;
@@ -549,18 +604,16 @@ svn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess,
{
svn_ra_serf__handler_t *handler;
- handler = apr_pcalloc(scratch_pool, sizeof(*handler));
- handler->handler_pool = scratch_pool;
+ handler = svn_ra_serf__create_handler(serf_sess, scratch_pool);
handler->method = "OPTIONS";
handler->path = serf_sess->session_url.path;
- handler->conn = serf_sess->conns[0];
- handler->session = serf_sess;
/* We don't care about the response body, so discard it. */
handler->response_handler = svn_ra_serf__handle_discard_body;
/* We need a simple body, in order to send it in chunked format. */
handler->body_delegate = create_simple_options_body;
+ handler->no_fail_on_http_failure_status = TRUE;
/* No special headers. */
@@ -574,9 +627,8 @@ svn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess,
return SVN_NO_ERROR;
}
- SVN_ERR(svn_ra_serf__error_on_status(handler->sline,
- handler->path,
- handler->location));
+ if (handler->sline.code != 200)
+ SVN_ERR(svn_ra_serf__unexpected_status(handler));
return SVN_NO_ERROR;
}
@@ -602,7 +654,7 @@ svn_ra_serf__has_capability(svn_ra_session_t *ra_session,
/* If any capability is unknown, they're all unknown, so ask. */
if (cap_result == NULL)
- SVN_ERR(svn_ra_serf__exchange_capabilities(serf_sess, NULL, pool));
+ SVN_ERR(svn_ra_serf__exchange_capabilities(serf_sess, NULL, pool, pool));
/* Try again, now that we've fetched the capabilities. */
cap_result = svn_hash_gets(serf_sess->capabilities, capability);
@@ -628,7 +680,9 @@ svn_ra_serf__has_capability(svn_ra_session_t *ra_session,
APR_ARRAY_PUSH(paths, const char *) = "";
err = svn_ra_serf__get_mergeinfo(ra_session, &ignored, paths, 0,
- FALSE, FALSE, pool);
+ svn_mergeinfo_explicit,
+ FALSE /* include_descendants */,
+ pool);
if (err)
{
@@ -646,7 +700,7 @@ svn_ra_serf__has_capability(svn_ra_session_t *ra_session,
cap_result = capability_yes;
}
else
- return err;
+ return svn_error_trace(err);
}
else
cap_result = capability_yes;
diff --git a/subversion/libsvn_ra_serf/property.c b/subversion/libsvn_ra_serf/property.c
index 586d38f..b7e0318 100644
--- a/subversion/libsvn_ra_serf/property.c
+++ b/subversion/libsvn_ra_serf/property.c
@@ -42,7 +42,7 @@
/* Our current parsing state we're in for the PROPFIND response. */
typedef enum prop_state_e {
- INITIAL = 0,
+ INITIAL = XML_STATE_INITIAL,
MULTISTATUS,
RESPONSE,
HREF,
@@ -59,20 +59,12 @@ typedef enum prop_state_e {
* This structure represents a pending PROPFIND response.
*/
typedef struct propfind_context_t {
- /* pool to issue allocations from */
- apr_pool_t *pool;
-
svn_ra_serf__handler_t *handler;
- /* associated serf session */
- svn_ra_serf__session_t *sess;
- svn_ra_serf__connection_t *conn;
-
/* the requested path */
const char *path;
- /* the requested version (number and string form) */
- svn_revnum_t rev;
+ /* the requested version (in string form) */
const char *label;
/* the request depth */
@@ -81,12 +73,8 @@ typedef struct propfind_context_t {
/* the list of requested properties */
const svn_ra_serf__dav_props_t *find_props;
- /* hash table that will be updated with the properties
- *
- * This can be shared between multiple propfind_context_t
- * structures
- */
- apr_hash_t *ret_props;
+ svn_ra_serf__prop_func_t prop_func;
+ void *prop_func_baton;
/* hash table containing all the properties associated with the
* "current" <propstat> tag. These will get copied into RET_PROPS
@@ -94,12 +82,6 @@ typedef struct propfind_context_t {
* "good"; otherwise, they'll get discarded.
*/
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;
@@ -136,10 +118,14 @@ static const svn_ra_serf__xml_transition_t propfind_ttable[] = {
{ 0 }
};
+static const int propfind_expected_status[] = {
+ 207,
+ 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)
+static apr_int64_t parse_status_code(const char *status_line)
{
/* STATUS_LINE should be of form: "HTTP/1.1 200 OK" */
if (status_line[0] == 'H' &&
@@ -159,24 +145,6 @@ static int parse_status_code(const char *status_line)
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,
@@ -189,17 +157,40 @@ propfind_opened(svn_ra_serf__xml_estate_t *xes,
if (entered_state == PROPVAL)
{
- svn_ra_serf__xml_note(xes, PROPVAL, "ns", tag->namespace);
+ svn_ra_serf__xml_note(xes, PROPVAL, "ns", tag->xmlns);
svn_ra_serf__xml_note(xes, PROPVAL, "name", tag->name);
}
else if (entered_state == PROPSTAT)
{
- ctx->ps_props = apr_hash_make(ctx->pool);
+ ctx->ps_props = apr_hash_make(svn_ra_serf__xml_state_pool(xes));
}
return SVN_NO_ERROR;
}
+/* Set PROPS for NS:NAME VAL. Helper for propfind_closed */
+static void
+set_ns_prop(apr_hash_t *ns_props,
+ const char *ns, const char *name,
+ const svn_string_t *val, apr_pool_t *result_pool)
+{
+ apr_hash_t *props = svn_hash_gets(ns_props, ns);
+
+ if (!props)
+ {
+ props = apr_hash_make(result_pool);
+ ns = apr_pstrdup(result_pool, ns);
+ svn_hash_sets(ns_props, ns, props);
+ }
+
+ if (val)
+ {
+ name = apr_pstrdup(result_pool, name);
+ val = svn_string_dup(val, result_pool);
+ }
+
+ svn_hash_sets(props, name, val);
+}
/* Conforms to svn_ra_serf__xml_closed_t */
static svn_error_t *
@@ -218,17 +209,10 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes,
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);
@@ -237,11 +221,10 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes,
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);
+ SVN_ERR(ctx->prop_func(ctx->prop_func_baton,
+ path,
+ D_, "href",
+ cdata, scratch_pool));
}
else if (leaving_state == COLLECTION)
{
@@ -257,21 +240,23 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes,
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);
+ apr_int64_t 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 = svn_hash_gets(attrs, "V:encoding");
+ const char *encoding;
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 ((altvalue = svn_hash_gets(attrs, "altvalue")) != NULL)
+ {
+ val_str = svn_string_create(altvalue, scratch_pool);
+ }
+ else if ((encoding = svn_hash_gets(attrs, "V:encoding")) != NULL)
{
if (strcmp(encoding, "base64") != 0)
return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
@@ -280,17 +265,15 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes,
encoding);
/* Decode into the right pool. */
- val_str = svn_base64_decode_string(cdata, ctx->pool);
+ val_str = svn_base64_decode_string(cdata, scratch_pool);
}
else
{
/* Copy into the right pool. */
- val_str = svn_string_dup(cdata, ctx->pool);
+ val_str = cdata;
}
- /* 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.
+ /* The current path sits on the RESPONSE state.
Now, it would be nice if we could, at this point, know that
the status code for this property indicated a problem -- then
@@ -300,24 +283,12 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes,
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 = svn_hash_gets(gathered, "path");
- if (path == NULL)
- path = ctx->path;
ns = svn_hash_gets(attrs, "ns");
- name = apr_pstrdup(ctx->pool,
- svn_hash_gets(attrs, "name"));
-
- altvalue = svn_hash_gets(attrs, "altvalue");
- if (altvalue != NULL)
- val_str = svn_string_create(altvalue, ctx->pool);
+ name = svn_hash_gets(attrs, "name");
- svn_ra_serf__set_ver_prop(ctx->ps_props,
- path, ctx->rev, ns, name, val_str,
- ctx->pool);
+ set_ns_prop(ctx->ps_props, ns, name, val_str,
+ apr_hash_pool_get(ctx->ps_props));
}
else
{
@@ -325,146 +296,60 @@ propfind_closed(svn_ra_serf__xml_estate_t *xes,
SVN_ERR_ASSERT(leaving_state == PROPSTAT);
- gathered = svn_ra_serf__xml_gather_since(xes, PROPSTAT);
+ gathered = svn_ra_serf__xml_gather_since(xes, RESPONSE);
/* 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 (! svn_hash_gets(gathered, "ignore-prop"))
{
- SVN_ERR(svn_ra_serf__walk_all_paths(ctx->ps_props, ctx->rev,
- copy_into_ret_props, ctx,
- scratch_pool));
- }
+ apr_hash_index_t *hi_ns;
+ const char *path;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- ctx->ps_props = NULL;
- }
-
- return SVN_NO_ERROR;
-}
+ path = svn_hash_gets(gathered, "path");
+ if (!path)
+ path = ctx->path;
-const svn_string_t *
-svn_ra_serf__get_ver_prop_string(apr_hash_t *props,
- const char *path,
- svn_revnum_t rev,
- const char *ns,
- const char *name)
-{
- apr_hash_t *ver_props, *path_props, *ns_props;
- void *val = NULL;
-
- ver_props = apr_hash_get(props, &rev, sizeof(rev));
- if (ver_props)
- {
- path_props = svn_hash_gets(ver_props, path);
-
- if (path_props)
- {
- ns_props = svn_hash_gets(path_props, ns);
- if (ns_props)
+ for (hi_ns = apr_hash_first(scratch_pool, ctx->ps_props);
+ hi_ns;
+ hi_ns = apr_hash_next(hi_ns))
{
- val = svn_hash_gets(ns_props, name);
+ const char *ns = apr_hash_this_key(hi_ns);
+ apr_hash_t *props = apr_hash_this_val(hi_ns);
+ apr_hash_index_t *hi_prop;
+
+ svn_pool_clear(iterpool);
+
+ for (hi_prop = apr_hash_first(iterpool, props);
+ hi_prop;
+ hi_prop = apr_hash_next(hi_prop))
+ {
+ const char *name = apr_hash_this_key(hi_prop);
+ const svn_string_t *value = apr_hash_this_val(hi_prop);
+
+ SVN_ERR(ctx->prop_func(ctx->prop_func_baton, path,
+ ns, name, value, iterpool));
+ }
}
- }
- }
-
- return val;
-}
-
-const char *
-svn_ra_serf__get_ver_prop(apr_hash_t *props,
- const char *path,
- svn_revnum_t rev,
- const char *ns,
- const char *name)
-{
- const svn_string_t *val;
- val = svn_ra_serf__get_ver_prop_string(props, path, rev, ns, name);
-
- if (val)
- {
- return val->data;
- }
-
- return NULL;
-}
-
-const svn_string_t *
-svn_ra_serf__get_prop_string(apr_hash_t *props,
- const char *path,
- const char *ns,
- const char *name)
-{
- return svn_ra_serf__get_ver_prop_string(props, path, SVN_INVALID_REVNUM,
- ns, name);
-}
-
-const char *
-svn_ra_serf__get_prop(apr_hash_t *props,
- const char *path,
- const char *ns,
- const char *name)
-{
- return svn_ra_serf__get_ver_prop(props, path, SVN_INVALID_REVNUM, ns, name);
-}
-
-void
-svn_ra_serf__set_ver_prop(apr_hash_t *props,
- const char *path, svn_revnum_t rev,
- const char *ns, const char *name,
- const svn_string_t *val, apr_pool_t *pool)
-{
- apr_hash_t *ver_props, *path_props, *ns_props;
-
- ver_props = apr_hash_get(props, &rev, sizeof(rev));
- if (!ver_props)
- {
- ver_props = apr_hash_make(pool);
- apr_hash_set(props, apr_pmemdup(pool, &rev, sizeof(rev)), sizeof(rev),
- ver_props);
- }
-
- path_props = svn_hash_gets(ver_props, path);
-
- if (!path_props)
- {
- path_props = apr_hash_make(pool);
- path = apr_pstrdup(pool, path);
- svn_hash_sets(ver_props, path, path_props);
-
- /* todo: we know that we'll fail the next check, but fall through
- * for now for simplicity's sake.
- */
- }
+ svn_pool_destroy(iterpool);
+ }
- ns_props = svn_hash_gets(path_props, ns);
- if (!ns_props)
- {
- ns_props = apr_hash_make(pool);
- ns = apr_pstrdup(pool, ns);
- svn_hash_sets(path_props, ns, ns_props);
+ ctx->ps_props = NULL; /* Allocated in PROPSTAT state pool */
}
- svn_hash_sets(ns_props, name, val);
+ return SVN_NO_ERROR;
}
-void
-svn_ra_serf__set_prop(apr_hash_t *props,
- const char *path,
- const char *ns, const char *name,
- const svn_string_t *val, apr_pool_t *pool)
-{
- svn_ra_serf__set_ver_prop(props, path, SVN_INVALID_REVNUM, ns, name,
- val, pool);
-}
static svn_error_t *
setup_propfind_headers(serf_bucket_t *headers,
- void *setup_baton,
- apr_pool_t *pool)
+ void *setup_baton,
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
propfind_context_t *ctx = setup_baton;
@@ -480,11 +365,13 @@ setup_propfind_headers(serf_bucket_t *headers,
#define PROPFIND_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind xmlns=\"DAV:\">"
#define PROPFIND_TRAILER "</propfind>"
+/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_propfind_body(serf_bucket_t **bkt,
void *setup_baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
propfind_context_t *ctx = setup_baton;
@@ -495,7 +382,7 @@ create_propfind_body(serf_bucket_t **bkt,
body_bkt = serf_bucket_aggregate_create(alloc);
prop = ctx->find_props;
- while (prop && prop->namespace)
+ while (prop && prop->xmlns)
{
/* special case the allprop case. */
if (strcmp(prop->name, "allprop") == 0)
@@ -515,7 +402,7 @@ create_propfind_body(serf_bucket_t **bkt,
alloc);
serf_bucket_aggregate_append(body_bkt, tmp);
- tmp = SERF_BUCKET_SIMPLE_STRING(prop->namespace, alloc);
+ tmp = SERF_BUCKET_SIMPLE_STRING(prop->xmlns, alloc);
serf_bucket_aggregate_append(body_bkt, tmp);
tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"/>", sizeof("\"/>")-1,
@@ -559,16 +446,15 @@ create_propfind_body(serf_bucket_t **bkt,
svn_error_t *
-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,
- const char *path,
- svn_revnum_t rev,
- const char *depth,
- const svn_ra_serf__dav_props_t *find_props,
- svn_ra_serf__list_t **done_list,
- apr_pool_t *pool)
+svn_ra_serf__create_propfind_handler(svn_ra_serf__handler_t **propfind_handler,
+ svn_ra_serf__session_t *sess,
+ const char *path,
+ svn_revnum_t rev,
+ const char *depth,
+ const svn_ra_serf__dav_props_t *find_props,
+ svn_ra_serf__prop_func_t prop_func,
+ void *prop_func_baton,
+ apr_pool_t *pool)
{
propfind_context_t *new_prop_ctx;
svn_ra_serf__handler_t *handler;
@@ -576,15 +462,11 @@ svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler,
new_prop_ctx = apr_pcalloc(pool, sizeof(*new_prop_ctx));
- new_prop_ctx->pool = apr_hash_pool_get(ret_props);
new_prop_ctx->path = path;
new_prop_ctx->find_props = find_props;
- new_prop_ctx->ret_props = ret_props;
+ new_prop_ctx->prop_func = prop_func;
+ new_prop_ctx->prop_func_baton = prop_func_baton;
new_prop_ctx->depth = depth;
- new_prop_ctx->sess = sess;
- new_prop_ctx->conn = conn;
- new_prop_ctx->rev = rev;
- new_prop_ctx->done_list = done_list;
if (SVN_IS_VALID_REVNUM(rev))
{
@@ -601,7 +483,9 @@ svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler,
NULL,
new_prop_ctx,
pool);
- handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
+ handler = svn_ra_serf__create_expat_handler(sess, xmlctx,
+ propfind_expected_status,
+ pool);
handler->method = "PROPFIND";
handler->path = path;
@@ -611,8 +495,7 @@ svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler,
handler->header_delegate = setup_propfind_headers;
handler->header_delegate_baton = new_prop_ctx;
- handler->session = new_prop_ctx->sess;
- handler->conn = new_prop_ctx->conn;
+ handler->no_dav_headers = TRUE;
new_prop_ctx->handler = handler;
@@ -621,208 +504,85 @@ svn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler,
return SVN_NO_ERROR;
}
-
-/*
- * 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__handler_t *handler,
- apr_pool_t *scratch_pool)
+svn_ra_serf__deliver_svn_props(void *baton,
+ const char *path,
+ const char *ns,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *scratch_pool)
{
- svn_error_t *err;
- svn_error_t *err2;
+ apr_hash_t *props = baton;
+ apr_pool_t *result_pool = apr_hash_pool_get(props);
+ const char *prop_name;
- err = svn_ra_serf__context_run_one(handler, scratch_pool);
+ prop_name = svn_ra_serf__svnname_from_wirename(ns, name, result_pool);
+ if (prop_name == NULL)
+ return SVN_NO_ERROR;
- err2 = svn_ra_serf__error_on_status(handler->sline,
- handler->path,
- handler->location);
+ svn_hash_sets(props, prop_name, svn_string_dup(value, result_pool));
- return svn_error_compose_create(err2, err);
+ return SVN_NO_ERROR;
}
/*
- * This is a blocking version of deliver_props.
+ * Implementation of svn_ra_serf__prop_func_t that delivers all DAV properties
+ * in (const char * -> apr_hash_t *) on Namespace pointing to a second hash
+ * (const char * -> svn_string_t *) to the values.
*/
-svn_error_t *
-svn_ra_serf__retrieve_props(apr_hash_t **results,
- svn_ra_serf__session_t *sess,
- svn_ra_serf__connection_t *conn,
- const char *url,
- svn_revnum_t rev,
- const char *depth,
- const svn_ra_serf__dav_props_t *props,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+static svn_error_t *
+deliver_node_props(void *baton,
+ const char *path,
+ const char *ns,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *scratch_pool)
{
- svn_ra_serf__handler_t *handler;
+ apr_hash_t *nss = baton;
+ apr_hash_t *props;
+ apr_pool_t *result_pool = apr_hash_pool_get(nss);
- *results = apr_hash_make(result_pool);
+ props = svn_hash_gets(nss, ns);
- 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(handler, scratch_pool));
+ if (!props)
+ {
+ props = apr_hash_make(result_pool);
+
+ ns = apr_pstrdup(result_pool, ns);
+ svn_hash_sets(nss, ns, props);
+ }
+
+ name = apr_pstrdup(result_pool, name);
+ svn_hash_sets(props, name, svn_string_dup(value, result_pool));
return SVN_NO_ERROR;
}
-
svn_error_t *
svn_ra_serf__fetch_node_props(apr_hash_t **results,
- svn_ra_serf__connection_t *conn,
+ svn_ra_serf__session_t *session,
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_t *multiprops;
- apr_hash_t *ver_props;
-
- /* 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 = svn_hash_gets(ver_props, url);
- 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"));
-}
-
-
-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, props); ns_hi;
- ns_hi = apr_hash_next(ns_hi))
- {
- void *ns_val;
- const void *ns_name;
- apr_hash_index_t *name_hi;
-
- /* NOTE: We do not clear ITERPOOL in this loop. Generally, there are
- very few namespaces, so this loop will not have many iterations.
- Instead, ITERPOOL is used for the inner loop. */
-
- apr_hash_this(ns_hi, &ns_name, NULL, &ns_val);
-
- for (name_hi = apr_hash_first(scratch_pool, ns_val); name_hi;
- name_hi = apr_hash_next(name_hi))
- {
- void *prop_val;
- const void *prop_name;
-
- /* See note above, regarding clearing of this pool. */
- svn_pool_clear(iterpool);
-
- apr_hash_this(name_hi, &prop_name, NULL, &prop_val);
-
- SVN_ERR(walker(baton, ns_name, prop_name, prop_val, iterpool));
- }
- }
- svn_pool_destroy(iterpool);
-
- 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)
-{
- 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 = svn_hash_gets(ver_props, name);
- 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,
- void *baton,
- apr_pool_t *pool)
-{
- apr_hash_index_t *path_hi;
- apr_hash_t *ver_props;
+ apr_hash_t *props;
+ svn_ra_serf__handler_t *handler;
- ver_props = apr_hash_get(props, &rev, sizeof(rev));
+ props = apr_hash_make(result_pool);
- if (!ver_props)
- {
- return SVN_NO_ERROR;
- }
+ SVN_ERR(svn_ra_serf__create_propfind_handler(&handler, session,
+ url, revision, "0", which_props,
+ deliver_node_props,
+ props, scratch_pool));
- for (path_hi = apr_hash_first(pool, ver_props); path_hi;
- path_hi = apr_hash_next(path_hi))
- {
- void *path_props;
- const void *path_name;
- apr_ssize_t path_len;
- apr_hash_index_t *ns_hi;
-
- apr_hash_this(path_hi, &path_name, &path_len, &path_props);
- for (ns_hi = apr_hash_first(pool, path_props); ns_hi;
- ns_hi = apr_hash_next(ns_hi))
- {
- void *ns_val;
- const void *ns_name;
- apr_ssize_t ns_len;
- apr_hash_index_t *name_hi;
- apr_hash_this(ns_hi, &ns_name, &ns_len, &ns_val);
- for (name_hi = apr_hash_first(pool, ns_val); name_hi;
- name_hi = apr_hash_next(name_hi))
- {
- void *prop_val;
- const void *prop_name;
- apr_ssize_t prop_len;
-
- apr_hash_this(name_hi, &prop_name, &prop_len, &prop_val);
- /* use a subpool? */
- SVN_ERR(walker(baton, path_name, path_len, ns_name, ns_len,
- prop_name, prop_len, prop_val, pool));
- }
- }
- }
+ SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
+ *results = props;
return SVN_NO_ERROR;
}
-
const char *
svn_ra_serf__svnname_from_wirename(const char *ns,
const char *name,
@@ -832,10 +592,10 @@ svn_ra_serf__svnname_from_wirename(const char *ns,
return apr_pstrdup(result_pool, name);
if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0)
- return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL);
+ return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, SVN_VA_NULL);
if (strcmp(ns, SVN_PROP_PREFIX) == 0)
- return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL);
+ return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, SVN_VA_NULL);
if (strcmp(name, SVN_DAV__VERSION_NAME) == 0)
return SVN_PROP_ENTRY_COMMITTED_REV;
@@ -863,99 +623,9 @@ svn_ra_serf__svnname_from_wirename(const char *ns,
}
/* An unknown namespace, must be a custom property. */
- return apr_pstrcat(result_pool, ns, name, (char *)NULL);
+ return apr_pstrcat(result_pool, ns, name, SVN_VA_NULL);
}
-
-/* Conforms to svn_ra_serf__walker_visitor_t */
-static svn_error_t *
-set_flat_props(void *baton,
- const char *ns,
- const char *name,
- const svn_string_t *value,
- apr_pool_t *pool)
-{
- apr_hash_t *props = baton;
- apr_pool_t *result_pool = apr_hash_pool_get(props);
- const char *prop_name;
-
- /* ### is VAL in the proper pool? */
-
- prop_name = svn_ra_serf__svnname_from_wirename(ns, name, result_pool);
- if (prop_name != NULL)
- svn_hash_sets(props, prop_name, value);
-
- return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
-svn_ra_serf__flatten_props(apr_hash_t **flat_props,
- apr_hash_t *props,
- 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_node_props(
- props,
- set_flat_props,
- *flat_props /* baton */,
- scratch_pool));
-}
-
-
-static svn_error_t *
-select_revprops(void *baton,
- const char *ns,
- const char *name,
- const svn_string_t *val,
- apr_pool_t *scratch_pool)
-{
- apr_hash_t *revprops = baton;
- apr_pool_t *result_pool = apr_hash_pool_get(revprops);
- const char *prop_name;
-
- /* ### copy NAME into the RESULT_POOL? */
- /* ### copy VAL into the RESULT_POOL? */
-
- if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0)
- prop_name = name;
- else if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0)
- prop_name = apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL);
- else if (strcmp(ns, SVN_PROP_PREFIX) == 0)
- prop_name = apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL);
- else if (strcmp(ns, "") == 0)
- prop_name = name;
- else
- {
- /* do nothing for now? */
- return SVN_NO_ERROR;
- }
-
- svn_hash_sets(revprops, prop_name, val);
-
- return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
-svn_ra_serf__select_revprops(apr_hash_t **revprops,
- const char *name,
- svn_revnum_t rev,
- apr_hash_t *all_revprops,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- *revprops = apr_hash_make(result_pool);
-
- return svn_error_trace(svn_ra_serf__walk_all_props(
- all_revprops, name, rev,
- select_revprops, *revprops,
- scratch_pool));
-}
-
-
/*
* Contact the server (using CONN) to calculate baseline
* information for BASELINE_URL at REVISION (which may be
@@ -969,7 +639,7 @@ svn_ra_serf__select_revprops(apr_hash_t **revprops,
static svn_error_t *
retrieve_baseline_info(svn_revnum_t *actual_revision,
const char **basecoll_url_p,
- svn_ra_serf__connection_t *conn,
+ svn_ra_serf__session_t *session,
const char *baseline_url,
svn_revnum_t revision,
apr_pool_t *result_pool,
@@ -979,7 +649,7 @@ retrieve_baseline_info(svn_revnum_t *actual_revision,
apr_hash_t *dav_props;
const char *basecoll_url;
- SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn,
+ SVN_ERR(svn_ra_serf__fetch_node_props(&props, session,
baseline_url, revision,
baseline_props,
scratch_pool, scratch_pool));
@@ -1000,12 +670,18 @@ retrieve_baseline_info(svn_revnum_t *actual_revision,
const char *version_name;
version_name = svn_prop_get_value(dav_props, SVN_DAV__VERSION_NAME);
- if (!version_name)
+ if (version_name)
+ {
+ apr_int64_t rev;
+
+ SVN_ERR(svn_cstring_atoi64(&rev, version_name));
+ *actual_revision = (svn_revnum_t)rev;
+ }
+
+ if (!version_name || !SVN_IS_VALID_REVNUM(*actual_revision))
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;
@@ -1023,7 +699,7 @@ retrieve_baseline_info(svn_revnum_t *actual_revision,
static svn_error_t *
v1_get_youngest_revnum(svn_revnum_t *youngest,
const char **basecoll_url,
- svn_ra_serf__connection_t *conn,
+ svn_ra_serf__session_t *session,
const char *vcc_url,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
@@ -1033,7 +709,7 @@ v1_get_youngest_revnum(svn_revnum_t *youngest,
/* 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_ERR(svn_ra_serf__fetch_dav_prop(&baseline_url, session, vcc_url,
SVN_INVALID_REVNUM,
"checked-in",
scratch_pool, scratch_pool));
@@ -1052,15 +728,15 @@ v1_get_youngest_revnum(svn_revnum_t *youngest,
/* First check baseline information cache. */
SVN_ERR(svn_ra_serf__blncache_get_baseline_info(&bc_url,
youngest,
- conn->session->blncache,
+ session->blncache,
baseline_url,
scratch_pool));
if (!bc_url)
{
- SVN_ERR(retrieve_baseline_info(youngest, &bc_url, conn,
+ SVN_ERR(retrieve_baseline_info(youngest, &bc_url, session,
baseline_url, SVN_INVALID_REVNUM,
scratch_pool, scratch_pool));
- SVN_ERR(svn_ra_serf__blncache_set(conn->session->blncache,
+ SVN_ERR(svn_ra_serf__blncache_set(session->blncache,
baseline_url, *youngest,
bc_url, scratch_pool));
}
@@ -1081,12 +757,12 @@ svn_ra_serf__get_youngest_revnum(svn_revnum_t *youngest,
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));
+ youngest, session, scratch_pool));
- SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, scratch_pool));
+ SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, scratch_pool));
return svn_error_trace(v1_get_youngest_revnum(youngest, NULL,
- session->conns[0], vcc_url,
+ session, vcc_url,
scratch_pool, scratch_pool));
}
@@ -1103,9 +779,9 @@ 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)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
/* If we detected HTTP v2 support on the server, we can construct
the baseline collection URL ourselves, and fetch the latest
@@ -1119,14 +795,10 @@ get_baseline_info(const char **bc_url,
else
{
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"));
+ revnum_used, session, scratch_pool));
}
- *bc_url = apr_psprintf(pool, "%s/%ld",
+ *bc_url = apr_psprintf(result_pool, "%s/%ld",
session->rev_root_stub, *revnum_used);
}
@@ -1135,20 +807,22 @@ get_baseline_info(const char **bc_url,
{
const char *vcc_url;
- SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, conn, pool));
+ SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, scratch_pool));
if (SVN_IS_VALID_REVNUM(revision))
{
/* First check baseline information cache. */
SVN_ERR(svn_ra_serf__blncache_get_bc_url(bc_url,
session->blncache,
- revision, pool));
+ revision, result_pool));
if (!*bc_url)
{
- SVN_ERR(retrieve_baseline_info(NULL, bc_url, conn,
- vcc_url, revision, pool, pool));
+ SVN_ERR(retrieve_baseline_info(NULL, bc_url, session,
+ vcc_url, revision,
+ result_pool, scratch_pool));
SVN_ERR(svn_ra_serf__blncache_set(session->blncache, NULL,
- revision, *bc_url, pool));
+ revision, *bc_url,
+ scratch_pool));
}
*revnum_used = revision;
@@ -1156,8 +830,8 @@ get_baseline_info(const char **bc_url,
else
{
SVN_ERR(v1_get_youngest_revnum(revnum_used, bc_url,
- conn, vcc_url,
- pool, pool));
+ session, vcc_url,
+ result_pool, scratch_pool));
}
}
@@ -1169,7 +843,6 @@ 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,
@@ -1183,15 +856,10 @@ svn_ra_serf__get_stable_url(const char **stable_url,
if (! url)
url = session->session_url.path;
- /* 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));
+ session, revision, scratch_pool, scratch_pool));
SVN_ERR(svn_ra_serf__get_relative_path(&repos_relpath, url,
- session, conn, scratch_pool));
+ session, scratch_pool));
*stable_url = svn_path_url_add_component2(basecoll_url, repos_relpath,
result_pool);
@@ -1203,38 +871,8 @@ svn_ra_serf__get_stable_url(const char **stable_url,
svn_error_t *
-svn_ra_serf__get_resource_type(svn_node_kind_t *kind,
- apr_hash_t *props)
-{
- apr_hash_t *dav_props;
- const char *res_type;
-
- 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"));
- }
-
- if (strcmp(res_type, "collection") == 0)
- {
- *kind = svn_node_dir;
- }
- else
- {
- *kind = svn_node_file;
- }
-
- return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
svn_ra_serf__fetch_dav_prop(const char **value,
- svn_ra_serf__connection_t *conn,
+ svn_ra_serf__session_t *session,
const char *url,
svn_revnum_t revision,
const char *propname,
@@ -1244,7 +882,7 @@ svn_ra_serf__fetch_dav_prop(const char **value,
apr_hash_t *props;
apr_hash_t *dav_props;
- SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn, url, revision,
+ SVN_ERR(svn_ra_serf__fetch_node_props(&props, session, url, revision,
checked_in_props,
scratch_pool, scratch_pool));
dav_props = apr_hash_get(props, "DAV:", 4);
@@ -1261,3 +899,19 @@ svn_ra_serf__fetch_dav_prop(const char **value,
return SVN_NO_ERROR;
}
+
+/* Removes all non regular properties from PROPS */
+void
+svn_ra_serf__keep_only_regular_props(apr_hash_t *props,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(scratch_pool, props); hi; hi = apr_hash_next(hi))
+ {
+ const char *propname = apr_hash_this_key(hi);
+
+ if (svn_property_kind2(propname) != svn_prop_regular_kind)
+ svn_hash_sets(props, propname, NULL);
+ }
+}
diff --git a/subversion/libsvn_ra_serf/ra_serf.h b/subversion/libsvn_ra_serf/ra_serf.h
index 335a9e3..fcef737 100644
--- a/subversion/libsvn_ra_serf/ra_serf.h
+++ b/subversion/libsvn_ra_serf/ra_serf.h
@@ -54,9 +54,6 @@ extern "C" {
#error Please update your version of serf to at least 1.2.1.
#endif
-/** Use this to silence compiler warnings about unused parameters. */
-#define UNUSED_CTX(x) ((void)(x))
-
/** Wait duration (in microseconds) used in calls to serf_context_run() */
#define SVN_RA_SERF__CONTEXT_RUN_DURATION 500000
@@ -99,10 +96,13 @@ typedef struct svn_ra_serf__connection_t {
* The master serf RA session.
*
* This is stored in the ra session ->priv field.
+ *
+ * ### Check ra_serf_dup_session when adding fields.
*/
struct svn_ra_serf__session_t {
/* Pool for allocations during this session */
apr_pool_t *pool;
+ apr_hash_t *config; /* For duplicating */
/* The current context */
serf_context_t *context;
@@ -154,6 +154,7 @@ struct svn_ra_serf__session_t {
/* Callback functions to get info from WC */
const svn_ra_callbacks2_t *wc_callbacks;
void *wc_callback_baton;
+ svn_auth_baton_t *auth_baton;
/* Callback function to send progress info to the client */
svn_ra_progress_notify_func_t progress_func;
@@ -263,31 +264,11 @@ struct svn_ra_serf__session_t {
*/
typedef struct svn_ra_serf__dav_props_t {
/* Element namespace */
- const char *namespace;
+ const char *xmlns;
/* Element name */
const char *name;
} svn_ra_serf__dav_props_t;
-/*
- * Structure which represents an XML namespace.
- */
-typedef struct ns_t {
- /* The assigned name. */
- const char *namespace;
- /* The full URL for this namespace. */
- const char *url;
- /* The next namespace in our list. */
- struct ns_t *next;
-} svn_ra_serf__ns_t;
-
-/*
- * An incredibly simple list.
- */
-typedef struct ra_serf_list_t {
- void *data;
- struct ra_serf_list_t *next;
-} svn_ra_serf__list_t;
-
/** DAV property sets **/
static const svn_ra_serf__dav_props_t base_props[] =
@@ -378,6 +359,16 @@ svn_ra_serf__context_run_wait(svn_boolean_t *done,
svn_ra_serf__session_t *sess,
apr_pool_t *scratch_pool);
+/* Run the context once. Manage waittime_left to handle timing out when
+ nothing happens over the session->timout.
+ */
+svn_error_t *
+svn_ra_serf__context_run(svn_ra_serf__session_t *sess,
+ apr_interval_time_t *waittime_left,
+ apr_pool_t *scratch_pool);
+
+
+
/* Callback for response handlers */
typedef svn_error_t *
(*svn_ra_serf__response_handler_t)(serf_request_t *request,
@@ -385,20 +376,26 @@ typedef svn_error_t *
void *handler_baton,
apr_pool_t *scratch_pool);
+/* Callback when the request is done */
+typedef svn_error_t *
+(*svn_ra_serf__response_done_delegate_t)(serf_request_t *request,
+ void *done_baton,
+ apr_pool_t *scratch_pool);
+
/* Callback for when a request body is needed. */
-/* ### should pass a scratch_pool */
typedef svn_error_t *
(*svn_ra_serf__request_body_delegate_t)(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *request_pool);
+ apr_pool_t *request_pool,
+ apr_pool_t *scratch_pool);
/* Callback for when request headers are needed. */
-/* ### should pass a scratch_pool */
typedef svn_error_t *
(*svn_ra_serf__request_header_delegate_t)(serf_bucket_t *headers,
void *baton,
- apr_pool_t *request_pool);
+ apr_pool_t *request_pool,
+ apr_pool_t *scratch_pool);
/* Callback for when a response has an error. */
typedef svn_error_t *
@@ -413,6 +410,8 @@ 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.
+ *
+ * Use svn_ra_serf__create_handler() to create instances of this struct.
*/
typedef struct svn_ra_serf__handler_t {
/* The HTTP method string of the request */
@@ -429,8 +428,20 @@ typedef struct svn_ra_serf__handler_t {
enabled. */
svn_boolean_t custom_accept_encoding;
+ /* If TRUE then default DAV: capabilities request headers is not configured
+ for request. */
+ svn_boolean_t no_dav_headers;
+
+ /* If TRUE doesn't fail requests on HTTP error statuses like 405, 408, 500
+ (see util.c response_done()) */
+ svn_boolean_t no_fail_on_http_failure_status;
+
+ /* If TRUE doesn't fail requests on HTTP redirect statuses like 301, 307 */
+ svn_boolean_t no_fail_on_http_redirect_status;
+
/* Has the request/response been completed? */
svn_boolean_t done;
+ svn_boolean_t scheduled; /* Is the request scheduled in a context */
/* If we captured an error from the server, then this will be non-NULL.
It will be allocated from HANDLER_POOL. */
@@ -447,6 +458,19 @@ typedef struct svn_ra_serf__handler_t {
serf_status_line sline; /* The parsed Status-Line */
const char *location; /* The Location: header, if any */
+ /* This function and baton pair allows handling the completion of request.
+ *
+ * The default handler is responsible for the HTTP failure processing.
+ *
+ * If no_fail_on_http_failure_status is not TRUE, then the callback will
+ * return recorded server errors or if there is none and the http status
+ * specifies an error returns an error for that.
+ *
+ * The default baton is the handler itself.
+ */
+ svn_ra_serf__response_done_delegate_t done_delegate;
+ void *done_delegate_baton;
+
/* 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.
@@ -490,7 +514,6 @@ typedef struct svn_ra_serf__handler_t {
/* 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;
@@ -511,149 +534,6 @@ svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler,
*/
void svn_ra_serf__request_create(svn_ra_serf__handler_t *handler);
-/* XML helper callbacks. */
-
-typedef struct svn_ra_serf__xml_state_t {
- /* A numeric value that represents the current state in parsing.
- *
- * Value 0 is reserved for use as the default state.
- */
- int current_state;
-
- /* Private pointer set by the parsing code. */
- void *private;
-
- /* Allocations should be made in this pool to match the lifetime of the
- * state.
- */
- apr_pool_t *pool;
-
- /* The currently-declared namespace for this state. */
- svn_ra_serf__ns_t *ns_list;
-
- /* Our previous states. */
- struct svn_ra_serf__xml_state_t *prev;
-} svn_ra_serf__xml_state_t;
-
-/* Forward declaration of the XML parser structure. */
-typedef struct svn_ra_serf__xml_parser_t svn_ra_serf__xml_parser_t;
-
-/* Callback invoked with @a baton by our XML @a parser when an element with
- * the @a name containing @a attrs is opened.
- */
-typedef svn_error_t *
-(*svn_ra_serf__xml_start_element_t)(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- const char **attrs,
- apr_pool_t *scratch_pool);
-
-/* Callback invoked with @a baton by our XML @a parser when an element with
- * the @a name is closed.
- */
-typedef svn_error_t *
-(*svn_ra_serf__xml_end_element_t)(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- apr_pool_t *scratch_pool);
-
-/* Callback invoked with @a baton by our XML @a parser when a CDATA portion
- * of @a data with size @a len is encountered.
- *
- * This may be invoked multiple times for the same tag.
- */
-typedef svn_error_t *
-(*svn_ra_serf__xml_cdata_chunk_handler_t)(svn_ra_serf__xml_parser_t *parser,
- const char *data,
- apr_size_t len,
- apr_pool_t *scratch_pool);
-
-/*
- * Helper structure associated with handle_xml_parser handler that will
- * specify how an XML response will be processed.
- */
-struct svn_ra_serf__xml_parser_t {
- /* Temporary allocations should be made in this pool. */
- apr_pool_t *pool;
-
- /* What kind of response are we parsing? If set, this should typically
- define the report name. */
- const char *response_type;
-
- /* Caller-specific data passed to the start, end, cdata callbacks. */
- void *user_data;
-
- /* Callback invoked when a tag is opened. */
- svn_ra_serf__xml_start_element_t start;
-
- /* Callback invoked when a tag is closed. */
- svn_ra_serf__xml_end_element_t end;
-
- /* Callback invoked when a cdata chunk is received. */
- svn_ra_serf__xml_cdata_chunk_handler_t cdata;
-
- /* Our associated expat-based XML parser. */
- XML_Parser xmlp;
-
- /* Our current state. */
- svn_ra_serf__xml_state_t *state;
-
- /* Our previously used states (will be reused). */
- svn_ra_serf__xml_state_t *free_state;
-
- /* If non-NULL, this value will be set to TRUE when the response is
- * completed.
- */
- svn_boolean_t *done;
-
- /* If non-NULL, when this parser completes, it will add done_item to
- * the list.
- */
- svn_ra_serf__list_t **done_list;
-
- /* A pointer to the item that will be inserted into the list upon
- * completeion.
- */
- svn_ra_serf__list_t *done_item;
-
- /* If this flag is TRUE, errors during parsing will be ignored.
- *
- * This is mainly used when we are processing an error XML response to
- * avoid infinite loops.
- */
- svn_boolean_t ignore_errors;
-
- /* If an error occurred, this value will be non-NULL. */
- svn_error_t *error;
-
- /* Deciding whether to pause, or not, is performed within the parsing
- callbacks. If a callback decides to set this flag, then the loop
- driving the parse (generally, a series of calls to serf_context_run())
- is going to need to coordinate the un-pausing of the parser by
- processing pending content. Thus, deciding to pause the parser is a
- coordinate effort rather than merely setting this flag.
-
- When an XML parsing callback sets this flag, note that additional
- elements may be parsed (as the current buffer is consumed). At some
- point, the flag will be recognized and arriving network content will
- be stashed away in the PENDING structure (see below).
-
- At some point, the controlling loop should clear this value. The
- underlying network processing will note the change and begin passing
- content into the XML callbacks.
-
- Note that the controlling loop should also process pending content
- since the arriving network content will typically finish first. */
- svn_boolean_t paused;
-
- /* While the XML parser is paused, content arriving from the server
- must be saved locally. We cannot stop reading, or the server may
- decide to drop the connection. The content will be stored in memory
- up to a certain limit, and will then be spilled over to disk.
-
- See libsvn_ra_serf/util.c */
- struct svn_ra_serf__pending_t *pending;
-};
-
-
/* v2 of the XML parsing functions */
/* The XML parsing context. */
@@ -719,6 +599,10 @@ typedef svn_error_t *
apr_pool_t *scratch_pool);
+/* Magic state value for the initial state in a svn_ra_serf__xml_transition_t
+ table */
+#define XML_STATE_INITIAL 0
+
/* State transition table.
When the XML Context is constructed, it is in state 0. User states are
@@ -727,6 +611,8 @@ typedef svn_error_t *
In a list of transitions, use { 0 } to indicate the end. Specifically,
the code looks for NS == NULL.
+ The initial state for each transition table is XML_STATE_INITIAL.
+
### more docco
*/
typedef struct svn_ra_serf__xml_transition_t {
@@ -757,6 +643,11 @@ typedef struct svn_ra_serf__xml_transition_t {
} svn_ra_serf__xml_transition_t;
+/* Constructor for svn_ra_serf__handler_t. Initializes a new handler
+ with default settings for SESSION. */
+svn_ra_serf__handler_t *
+svn_ra_serf__create_handler(svn_ra_serf__session_t *session,
+ apr_pool_t *result_pool);
/* Construct an XML parsing context, based on the TTABLE transition table.
As content is parsed, the CLOSED_CB callback will be invoked according
@@ -783,18 +674,26 @@ svn_ra_serf__xml_context_create(
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);
+/* Verifies if the parsing completed successfully and destroys
+ all subpools. */
+svn_error_t *
+svn_ra_serf__xml_context_done(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.
+ As part of the handling the http status value is compared to 200, or
+ if EXPECTED_STATUS is not NULL to all the values in EXPECTED_STATUS.
+ EXPECTED_STATUS is expected to be a list of integers ending with a 0
+ that lives at least as long as RESULT_POOL. If the status doesn't
+ match the request has failed and will be parsed as en error response.
+
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,
+svn_ra_serf__create_expat_handler(svn_ra_serf__session_t *session,
+ svn_ra_serf__xml_context_t *xmlctx,
+ const int *expected_status,
apr_pool_t *result_pool);
@@ -833,57 +732,25 @@ svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes,
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.
*/
struct svn_ra_serf__server_error_t {
- /* Our local representation of the error. */
- svn_error_t *error;
-
- /* Are we done with the response? */
- svn_boolean_t done;
-
- /* Have we seen an error tag? */
- svn_boolean_t in_error;
+ apr_pool_t *pool;
- /* Have we seen a HTTP "412 Precondition Failed" error? */
- svn_boolean_t contains_precondition_error;
+ /* XML parser and namespace used to parse the remote response */
+ svn_ra_serf__xml_context_t *xmlctx;
- /* Should we be collecting the XML cdata? */
- svn_boolean_t collect_cdata;
+ svn_ra_serf__response_handler_t response_handler;
+ void *response_baton;
- /* Collected cdata. NULL if cdata not needed. */
- svn_stringbuf_t *cdata;
+ /* The partial errors to construct the final error from */
+ apr_array_header_t *items;
- /* XML parser and namespace used to parse the remote response */
- svn_ra_serf__xml_parser_t parser;
+ /* The hooked handler */
+ svn_ra_serf__handler_t *handler;
};
-
/*
* Handler that discards the entire @a response body associated with a
* @a request. Implements svn_ra_serf__response_handler_t.
@@ -940,20 +807,33 @@ svn_ra_serf__expect_empty_body(serf_request_t *request,
/*
- * This function will feed the RESPONSE body into XMLP. When parsing is
- * completed (i.e. an EOF is received), *DONE is set to TRUE.
- * Implements svn_ra_serf__response_handler_t.
- *
- * If an error occurs during processing RESP_ERR is invoked with the
- * RESP_ERR_BATON.
- *
- * Temporary allocations are made in POOL.
+ * This function sets up error parsing for an existing request
*/
svn_error_t *
-svn_ra_serf__handle_xml_parser(serf_request_t *request,
- serf_bucket_t *response,
- void *handler_baton,
- apr_pool_t *pool);
+svn_ra_serf__setup_error_parsing(svn_ra_serf__server_error_t **server_err,
+ svn_ra_serf__handler_t *handler,
+ svn_boolean_t expect_207_only,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/*
+ * Forwards response data to the server error parser
+ */
+svn_error_t *
+svn_ra_serf__handle_server_error(svn_ra_serf__server_error_t *server_error,
+ svn_ra_serf__handler_t *handler,
+ serf_request_t *request,
+ serf_bucket_t *response,
+ apr_status_t *serf_status,
+ apr_pool_t *scratch_pool);
+
+/*
+ * Creates the svn_error_t * instance from the error recorded in
+ * HANDLER->server_error
+ */
+svn_error_t *
+svn_ra_serf__server_error_create(svn_ra_serf__handler_t *handler,
+ apr_pool_t *scratch_pool);
/* serf_response_handler_t implementation that completely discards
* the response.
@@ -967,28 +847,6 @@ svn_ra_serf__response_discard_handler(serf_request_t *request,
apr_pool_t *pool);
-/** XML helper functions. **/
-
-/*
- * Advance the internal XML @a parser to the @a state.
- */
-void
-svn_ra_serf__xml_push_state(svn_ra_serf__xml_parser_t *parser,
- int state);
-
-/*
- * Return to the previous internal XML @a parser state.
- */
-void
-svn_ra_serf__xml_pop_state(svn_ra_serf__xml_parser_t *parser);
-
-
-svn_error_t *
-svn_ra_serf__process_pending(svn_ra_serf__xml_parser_t *parser,
- svn_boolean_t *network_eof,
- apr_pool_t *scratch_pool);
-
-
/*
* Add the appropriate serf buckets to @a agg_bucket represented by
* the XML * @a tag and @a value.
@@ -1028,7 +886,7 @@ void
svn_ra_serf__add_open_tag_buckets(serf_bucket_t *agg_bucket,
serf_bucket_alloc_t *bkt_alloc,
const char *tag,
- ...);
+ ...) SVN_NEEDS_SENTINEL_NULL;
/*
* Add the appropriate serf buckets to AGG_BUCKET representing xml tag close
@@ -1041,6 +899,16 @@ svn_ra_serf__add_close_tag_buckets(serf_bucket_t *agg_bucket,
serf_bucket_alloc_t *bkt_alloc,
const char *tag);
+/* Add the appropriate serf buckets to AGG_BUCKET representing the XML
+ * open tag with name TAG, and then immediately closes the tag using the />
+ * notation
+ */
+void
+svn_ra_serf__add_empty_tag_buckets(serf_bucket_t *agg_bucket,
+ serf_bucket_alloc_t *bkt_alloc,
+ const char *tag,
+ ...) SVN_NEEDS_SENTINEL_NULL;
+
/*
* Add the appropriate serf buckets to AGG_BUCKET with xml-escaped
* version of DATA.
@@ -1051,82 +919,55 @@ void
svn_ra_serf__add_cdata_len_buckets(serf_bucket_t *agg_bucket,
serf_bucket_alloc_t *bkt_alloc,
const char *data, apr_size_t len);
-/*
- * 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 RESULT_POOL.
- */
-void
-svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list,
- const char *const *attrs,
- apr_pool_t *result_pool);
-/*
- * Look up @a name in the @a ns_list list for previously declared namespace
- * definitions.
- *
- * Return (in @a *returned_prop_name) a #svn_ra_serf__dav_props_t tuple
- * representing the expanded name.
- */
+
+/** PROPFIND-related functions **/
+
+/* Removes all non regular properties from PROPS */
void
-svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
- const svn_ra_serf__ns_t *ns_list,
- const char *name);
+svn_ra_serf__keep_only_regular_props(apr_hash_t *props,
+ apr_pool_t *scratch_pool);
-/** PROPFIND-related functions **/
+/* Callback used via svn_ra_serf__deliver_props2 */
+typedef svn_error_t *
+(*svn_ra_serf__prop_func_t)(void *baton,
+ const char *path,
+ const char *ns,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *scratch_pool);
/*
- * This function will deliver a PROP_CTX PROPFIND request in the SESS
- * serf context for the properties listed in LOOKUP_PROPS at URL for
- * DEPTH ("0","1","infinity").
- *
- * This function will not block waiting for the response. Callers are
- * expected to call svn_ra_serf__wait_for_props().
+ * Implementation of svn_ra_serf__prop_func_t that just delivers svn compatible
+ * properties in the apr_hash_t * that is used as baton.
*/
svn_error_t *
-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,
- const char *url,
- svn_revnum_t rev,
- const char *depth,
- const svn_ra_serf__dav_props_t *lookup_props,
- svn_ra_serf__list_t **done_list,
- apr_pool_t *pool);
+svn_ra_serf__deliver_svn_props(void *baton,
+ const char *path,
+ const char *ns,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *scratch_pool);
/*
- * This helper function will block until PROPFIND_HANDLER indicates that is
- * done or another error is returned.
+ * This function will create a handler for a PROPFIND request, which will deliver
+ * properties to PROP_FUNC() with PROP_BATON for the properties listed in LOOKUP_PROPS
+ * at URL for DEPTH ("0","1","infinity").
*/
svn_error_t *
-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.
-
- The properties are fetched and placed into RESULTS, allocated in
- RESULT_POOL.
-
- ### more docco about the other params.
-
- Temporary allocations are made in SCRATCH_POOL.
-*/
-svn_error_t *
-svn_ra_serf__retrieve_props(apr_hash_t **results,
- svn_ra_serf__session_t *sess,
- svn_ra_serf__connection_t *conn,
- const char *url,
- svn_revnum_t rev,
- const char *depth,
- const svn_ra_serf__dav_props_t *props,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
-
-
-/* Using CONN, fetch the properties specified by WHICH_PROPS using CONN
+svn_ra_serf__create_propfind_handler(svn_ra_serf__handler_t **handler,
+ svn_ra_serf__session_t *session,
+ const char *path,
+ svn_revnum_t rev,
+ const char *depth,
+ const svn_ra_serf__dav_props_t *find_props,
+ svn_ra_serf__prop_func_t prop_func,
+ void *prop_func_baton,
+ apr_pool_t *result_pool);
+
+
+/* Using SESSION, 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.
@@ -1139,7 +980,7 @@ svn_ra_serf__retrieve_props(apr_hash_t **results,
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,
+ svn_ra_serf__session_t *session,
const char *url,
svn_revnum_t revision,
const svn_ra_serf__dav_props_t *which_props,
@@ -1147,7 +988,7 @@ svn_ra_serf__fetch_node_props(apr_hash_t **results,
apr_pool_t *scratch_pool);
-/* Using CONN, fetch a DAV: property from the resource identified by URL
+/* Using SESSION, fetch a DAV: property from the resource identified by URL
within REVISION. The PROPNAME may be one of:
"checked-in"
@@ -1161,66 +1002,13 @@ svn_ra_serf__fetch_node_props(apr_hash_t **results,
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,
+ svn_ra_serf__session_t *session,
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.
- */
-void
-svn_ra_serf__set_ver_prop(apr_hash_t *props,
- const char *path, svn_revnum_t rev,
- const char *ns, const char *name,
- const svn_string_t *val, apr_pool_t *pool);
-#define svn_ra_serf__set_rev_prop svn_ra_serf__set_ver_prop
-
-/** Property walker functions **/
-
-typedef svn_error_t *
-(*svn_ra_serf__walker_visitor_t)(void *baton,
- const char *ns,
- const char *name,
- const svn_string_t *val,
- apr_pool_t *pool);
-
-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 *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,
- 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);
-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,
- void *baton,
- apr_pool_t *pool);
-
-
/* Map a property name, as passed over the wire, into its corresponding
Subversion-internal name. The returned name will be a static value,
or allocated within RESULT_POOL.
@@ -1232,75 +1020,6 @@ svn_ra_serf__svnname_from_wirename(const char *ns,
const char *name,
apr_pool_t *result_pool);
-
-/* Select the basic revision properties from the set of "all" properties.
- Return these in *REVPROPS, allocated from RESULT_POOL. */
-svn_error_t *
-svn_ra_serf__select_revprops(apr_hash_t **revprops,
- const char *name,
- svn_revnum_t rev,
- apr_hash_t *all_revprops,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
-
-
-/* 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.
-
- FLAT_PROPS is allocated in RESULT_POOL.
- ### right now, we do a shallow copy from PROPS to FLAT_PROPS. therefore,
- ### the names and values in PROPS must be in the proper pool.
-
- Temporary allocations are made in SCRATCH_POOL. */
-svn_error_t *
-svn_ra_serf__flatten_props(apr_hash_t **flat_props,
- apr_hash_t *props,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
-
-
-/* Return the property value for PATH at REV revision with a NS:NAME.
- * PROPS is a four-level nested hash: (svn_revnum_t => char *path =>
- * char *ns => char *name => svn_string_t *). */
-const svn_string_t *
-svn_ra_serf__get_ver_prop_string(apr_hash_t *props,
- const char *path, svn_revnum_t rev,
- const char *ns, const char *name);
-
-/* Same as svn_ra_serf__get_ver_prop_string(), but returns a C string. */
-const char *
-svn_ra_serf__get_ver_prop(apr_hash_t *props,
- const char *path, svn_revnum_t rev,
- const char *ns, const char *name);
-
-/* Same as svn_ra_serf__get_ver_prop_string(), but for the unknown revision. */
-const svn_string_t *
-svn_ra_serf__get_prop_string(apr_hash_t *props,
- const char *path,
- const char *ns,
- const char *name);
-
-/* Same as svn_ra_serf__get_ver_prop(), but for the unknown revision. */
-const char *
-svn_ra_serf__get_prop(apr_hash_t *props,
- const char *path,
- const char *ns,
- const char *name);
-
-/* Same as svn_ra_serf__set_rev_prop(), but for the unknown revision. */
-void
-svn_ra_serf__set_prop(apr_hash_t *props, const char *path,
- const char *ns, const char *name,
- 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);
-
-
/** MERGE-related functions **/
void
@@ -1317,9 +1036,7 @@ svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens,
locks set on the paths included in this commit. */
svn_error_t *
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,
@@ -1347,7 +1064,7 @@ svn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess,
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,
+ svn_ra_serf__session_t *session,
apr_pool_t *scratch_pool);
@@ -1362,35 +1079,29 @@ svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest,
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,
+ svn_ra_serf__session_t *session,
apr_pool_t *result_pool,
apr_pool_t *scratch_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
- * repository root URLs are cached in @a SESSION. Use @a CONN for any
- * required network communications if it is non-NULL; otherwise use the
- * default connection.
+ * repository root URLs are cached in @a SESSION.
*
- * All temporary allocations will be made in @a POOL. */
+ * All temporary allocations will be made in @a SCRATCH_POOL. */
svn_error_t *
svn_ra_serf__discover_vcc(const char **vcc_url,
svn_ra_serf__session_t *session,
- svn_ra_serf__connection_t *conn,
- apr_pool_t *pool);
+ apr_pool_t *scratch_pool);
/* Set @a REPORT_TARGET to the URI of the resource at which generic
- * (path-agnostic) REPORTs should be aimed for @a SESSION. Use @a
- * CONN for any required network communications if it is non-NULL;
- * otherwise use the default connection.
+ * (path-agnostic) REPORTs should be aimed for @a SESSION.
*
* All temporary allocations will be made in @a POOL.
*/
svn_error_t *
svn_ra_serf__report_resource(const char **report_target,
svn_ra_serf__session_t *session,
- svn_ra_serf__connection_t *conn,
apr_pool_t *pool);
/* Set @a REL_PATH to a path (not URI-encoded) relative to the root of
@@ -1402,7 +1113,6 @@ svn_error_t *
svn_ra_serf__get_relative_path(const char **rel_path,
const char *orig_path,
svn_ra_serf__session_t *session,
- svn_ra_serf__connection_t *conn,
apr_pool_t *pool);
@@ -1429,11 +1139,9 @@ svn_ra_serf__get_youngest_revnum(svn_revnum_t *youngest,
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.
+ For a specified SESSION, 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.
@@ -1452,7 +1160,6 @@ 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,
@@ -1461,6 +1168,20 @@ svn_ra_serf__get_stable_url(const char **stable_url,
/** RA functions **/
+/* Implements svn_ra__vtable_t.reparent(). */
+svn_error_t *
+svn_ra_serf__reparent(svn_ra_session_t *ra_session,
+ const char *url,
+ apr_pool_t *pool);
+
+/* Implements svn_ra__vtable_t.rev_prop(). */
+svn_error_t *
+svn_ra_serf__rev_prop(svn_ra_session_t *session,
+ svn_revnum_t rev,
+ const char *name,
+ svn_string_t **value,
+ apr_pool_t *pool);
+
/* Implements svn_ra__vtable_t.get_log(). */
svn_error_t *
svn_ra_serf__get_log(svn_ra_session_t *session,
@@ -1476,6 +1197,22 @@ svn_ra_serf__get_log(svn_ra_session_t *session,
void *receiver_baton,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.check_path(). */
+svn_error_t *
+svn_ra_serf__check_path(svn_ra_session_t *ra_session,
+ const char *rel_path,
+ svn_revnum_t revision,
+ svn_node_kind_t *kind,
+ apr_pool_t *pool);
+
+/* Implements svn_ra__vtable_t.stat(). */
+svn_error_t *
+svn_ra_serf__stat(svn_ra_session_t *ra_session,
+ const char *rel_path,
+ svn_revnum_t revision,
+ svn_dirent_t **dirent,
+ apr_pool_t *pool);
+
/* Implements svn_ra__vtable_t.get_locations(). */
svn_error_t *
svn_ra_serf__get_locations(svn_ra_session_t *session,
@@ -1572,7 +1309,12 @@ svn_ra_serf__get_dated_revision(svn_ra_session_t *session,
apr_time_t tm,
apr_pool_t *pool);
-/* Implements svn_ra__vtable_t.get_commit_editor(). */
+/* Implements svn_ra__vtable_t.get_commit_editor().
+ *
+ * Note: Like other commit editors, the returned editor requires that the
+ * @c copyfrom_path parameter passed to its @c add_file and @c add_directory
+ * methods is a URL, not a relative path.
+ */
svn_error_t *
svn_ra_serf__get_commit_editor(svn_ra_session_t *session,
const svn_delta_editor_t **editor,
@@ -1594,6 +1336,17 @@ svn_ra_serf__get_file(svn_ra_session_t *session,
apr_hash_t **props,
apr_pool_t *pool);
+/* Implements svn_ra__vtable_t.get_dir(). */
+svn_error_t *
+svn_ra_serf__get_dir(svn_ra_session_t *ra_session,
+ apr_hash_t **dirents,
+ svn_revnum_t *fetched_rev,
+ apr_hash_t **ret_props,
+ const char *rel_path,
+ svn_revnum_t revision,
+ apr_uint32_t dirent_fields,
+ apr_pool_t *result_pool);
+
/* Implements svn_ra__vtable_t.change_rev_prop(). */
svn_error_t *
svn_ra_serf__change_rev_prop(svn_ra_session_t *session,
@@ -1688,7 +1441,8 @@ svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session,
svn_error_t *
svn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess,
const char **corrected_url,
- apr_pool_t *pool);
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
/* Implements svn_ra__vtable_t.has_capability(). */
svn_error_t *
@@ -1745,12 +1499,23 @@ svn_ra_serf__credentials_callback(char **username, char **password,
* Convert an HTTP STATUS_CODE resulting from a WebDAV request against
* PATH to the relevant error code. Use the response-supplied LOCATION
* where it necessary.
+ *
+ * Returns SVN_NO_ERROR if sline doesn't specify an error condition
*/
svn_error_t *
svn_ra_serf__error_on_status(serf_status_line sline,
const char *path,
const char *location);
+/**
+ * Convert an unexpected HTTP STATUS_CODE from a request to the relevant error
+ * code. Unlike svn_ra_serf__error_on_status() this function creates an error
+ * for any result
+ */
+svn_error_t *
+svn_ra_serf__unexpected_status(svn_ra_serf__handler_t *handler);
+
+
/* ###? */
svn_error_t *
svn_ra_serf__copy_into_spillbuf(svn_spillbuf_t **spillbuf,
@@ -1773,6 +1538,25 @@ svn_ra_serf__wrap_err(apr_status_t status,
const char *fmt,
...);
+/* Create a bucket that just returns DATA (with length LEN) and then returns
+ the APR_EAGAIN status */
+serf_bucket_t *
+svn_ra_serf__create_bucket_with_eagain(const char *data,
+ apr_size_t len,
+ serf_bucket_alloc_t *allocator);
+
+/* Parse a given URL_STR, fill in all supplied fields of URI
+ * structure.
+ *
+ * This function is a compatibility wrapper around apr_uri_parse().
+ * Different apr-util versions set apr_uri_t.path to either NULL or ""
+ * for root paths, and serf expects to see "/". This function always
+ * sets URI.path to "/" for these paths. */
+svn_error_t *
+svn_ra_serf__uri_parse(apr_uri_t *uri,
+ const char *url_str,
+ apr_pool_t *result_pool);
+
#if defined(SVN_DEBUG)
/* Wrapper macros to collect file and line information */
diff --git a/subversion/libsvn_ra_serf/replay.c b/subversion/libsvn_ra_serf/replay.c
index 66e2f58..8d2da69 100644
--- a/subversion/libsvn_ra_serf/replay.c
+++ b/subversion/libsvn_ra_serf/replay.c
@@ -29,6 +29,7 @@
#include "svn_pools.h"
#include "svn_ra.h"
#include "svn_dav.h"
+#include "svn_hash.h"
#include "svn_xml.h"
#include "../libsvn_ra/ra_loader.h"
#include "svn_config.h"
@@ -46,55 +47,93 @@
* This enum represents the current state of our XML parsing.
*/
typedef enum replay_state_e {
- NONE = 0,
- REPORT,
- OPEN_DIR,
- ADD_DIR,
- OPEN_FILE,
- ADD_FILE,
- DELETE_ENTRY,
- APPLY_TEXTDELTA,
- CHANGE_PROP
+ INITIAL = XML_STATE_INITIAL,
+
+ REPLAY_REPORT,
+ REPLAY_TARGET_REVISION,
+ REPLAY_OPEN_ROOT,
+ REPLAY_OPEN_DIRECTORY,
+ REPLAY_OPEN_FILE,
+ REPLAY_ADD_DIRECTORY,
+ REPLAY_ADD_FILE,
+ REPLAY_DELETE_ENTRY,
+ REPLAY_CLOSE_FILE,
+ REPLAY_CLOSE_DIRECTORY,
+ REPLAY_CHANGE_DIRECTORY_PROP,
+ REPLAY_CHANGE_FILE_PROP,
+ REPLAY_APPLY_TEXTDELTA
} replay_state_e;
-typedef struct replay_info_t replay_info_t;
+#define S_ SVN_XML_NAMESPACE
+static const svn_ra_serf__xml_transition_t replay_ttable[] = {
+ { INITIAL, S_, "editor-report", REPLAY_REPORT,
+ FALSE, { NULL }, TRUE },
-struct replay_info_t {
- apr_pool_t *pool;
+ /* Replay just throws every operation as xml element directly
+ in the replay report, so we can't really use the nice exit
+ handling of the transition parser to handle clean callbacks */
- void *baton;
- svn_stream_t *stream;
+ { REPLAY_REPORT, S_, "target-revision", REPLAY_TARGET_REVISION,
+ FALSE, { "rev", NULL }, TRUE },
- replay_info_t *parent;
-};
+ { REPLAY_REPORT, S_, "open-root", REPLAY_OPEN_ROOT,
+ FALSE, { "rev", NULL }, TRUE },
-typedef svn_error_t *
-(*change_prop_t)(void *baton,
- const char *name,
- const svn_string_t *value,
- apr_pool_t *pool);
+ { REPLAY_REPORT, S_, "open-directory", REPLAY_OPEN_DIRECTORY,
+ FALSE, { "name", "rev", NULL }, TRUE },
-typedef struct prop_info_t {
- apr_pool_t *pool;
+ { REPLAY_REPORT, S_, "open-file", REPLAY_OPEN_FILE,
+ FALSE, { "name", "rev", NULL }, TRUE },
- change_prop_t change;
+ { REPLAY_REPORT, S_, "add-directory", REPLAY_ADD_DIRECTORY,
+ FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", NULL}, TRUE },
- const char *name;
- svn_boolean_t del_prop;
+ { REPLAY_REPORT, S_, "add-file", REPLAY_ADD_FILE,
+ FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", NULL}, TRUE },
- svn_stringbuf_t *prop_value;
+ { REPLAY_REPORT, S_, "delete-entry", REPLAY_DELETE_ENTRY,
+ FALSE, { "name", "rev", NULL }, TRUE },
- replay_info_t *parent;
-} prop_info_t;
+ { REPLAY_REPORT, S_, "close-file", REPLAY_CLOSE_FILE,
+ FALSE, { "?checksum", NULL }, TRUE },
-typedef struct replay_context_t {
- apr_pool_t *src_rev_pool;
- apr_pool_t *dst_rev_pool;
+ { REPLAY_REPORT, S_, "close-directory", REPLAY_CLOSE_DIRECTORY,
+ FALSE, { NULL }, TRUE },
- /* Are we done fetching this file? */
- svn_boolean_t done;
- svn_ra_serf__list_t **done_list;
- svn_ra_serf__list_t done_item;
+ { REPLAY_REPORT, S_, "change-dir-prop", REPLAY_CHANGE_DIRECTORY_PROP,
+ TRUE, { "name", "?del", NULL }, TRUE },
+
+ { REPLAY_REPORT, S_, "change-file-prop", REPLAY_CHANGE_FILE_PROP,
+ TRUE, { "name", "?del", NULL }, TRUE },
+
+ { REPLAY_REPORT, S_, "apply-textdelta", REPLAY_APPLY_TEXTDELTA,
+ FALSE, { "?checksum", NULL }, TRUE },
+
+ { 0 }
+};
+
+/* Per directory/file state */
+typedef struct replay_node_t {
+ apr_pool_t *pool; /* pool allocating this node's data */
+ svn_boolean_t file; /* file or dir */
+
+ void *baton; /* node baton */
+ svn_stream_t *stream; /* stream while handling txdata */
+
+ struct replay_node_t *parent; /* parent node or NULL */
+} replay_node_t;
+
+/* Per revision replay report state */
+typedef struct revision_report_t {
+ apr_pool_t *pool; /* per revision pool */
+
+ struct replay_node_t *current_node;
+ struct replay_node_t *root_node;
+
+ /* Are we done fetching this file?
+ Handles book-keeping in multi-report case */
+ svn_boolean_t *done;
+ int *replay_reports; /* NULL or number of outstanding reports */
/* callback to get an editor */
svn_ra_replay_revstart_callback_t revstart_func;
@@ -121,479 +160,324 @@ typedef struct replay_context_t {
svn_revnum_t revprop_rev;
/* Revision properties for this revision. */
- apr_hash_t *revs_props;
- apr_hash_t *props;
-
- /* Keep a reference to the XML parser ctx to report any errors. */
- svn_ra_serf__xml_parser_t *parser_ctx;
+ apr_hash_t *rev_props;
/* Handlers for the PROPFIND and REPORT for the current revision. */
svn_ra_serf__handler_t *propfind_handler;
- svn_ra_serf__handler_t *report_handler;
-
-} replay_context_t;
-
-
-static void *
-push_state(svn_ra_serf__xml_parser_t *parser,
- replay_context_t *replay_ctx,
- replay_state_e state)
-{
- svn_ra_serf__xml_push_state(parser, state);
-
- if (state == OPEN_DIR || state == ADD_DIR ||
- state == OPEN_FILE || state == ADD_FILE)
- {
- replay_info_t *info;
- apr_pool_t *pool = svn_pool_create(replay_ctx->dst_rev_pool);
-
- info = apr_palloc(pool, sizeof(*info));
-
- info->pool = pool;
- info->parent = parser->state->private;
- info->baton = NULL;
- info->stream = NULL;
+ svn_ra_serf__handler_t *report_handler; /* For done handler */
- parser->state->private = info;
- }
- else if (state == CHANGE_PROP)
- {
- prop_info_t *info;
- apr_pool_t *pool = svn_pool_create(replay_ctx->dst_rev_pool);
-
- info = apr_pcalloc(pool, sizeof(*info));
-
- info->pool = pool;
- info->parent = parser->state->private;
- info->prop_value = svn_stringbuf_create_empty(pool);
-
- parser->state->private = info;
- }
-
- return parser->state->private;
-}
+} revision_report_t;
+/* Conforms to svn_ra_serf__xml_opened_t */
static svn_error_t *
-start_replay(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- const char **attrs,
- apr_pool_t *scratch_pool)
+replay_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)
{
- replay_context_t *ctx = parser->user_data;
- replay_state_e state;
+ struct revision_report_t *ctx = baton;
- state = parser->state->current_state;
-
- if (state == NONE &&
- strcmp(name.name, "editor-report") == 0)
+ if (entered_state == REPLAY_REPORT)
{
- push_state(parser, ctx, REPORT);
-
/* Before we can continue, we need the revision properties. */
SVN_ERR_ASSERT(!ctx->propfind_handler || ctx->propfind_handler->done);
- /* Create a pool for the commit editor. */
- ctx->dst_rev_pool = svn_pool_create(ctx->src_rev_pool);
-
- SVN_ERR(svn_ra_serf__select_revprops(&ctx->props,
- ctx->revprop_target,
- ctx->revprop_rev,
- ctx->revs_props,
- ctx->dst_rev_pool,
- scratch_pool));
+ svn_ra_serf__keep_only_regular_props(ctx->rev_props, scratch_pool);
if (ctx->revstart_func)
{
SVN_ERR(ctx->revstart_func(ctx->revision, ctx->replay_baton,
&ctx->editor, &ctx->editor_baton,
- ctx->props,
- ctx->dst_rev_pool));
+ ctx->rev_props,
+ ctx->pool));
}
}
- else if (state == REPORT &&
- strcmp(name.name, "target-revision") == 0)
+ else if (entered_state == REPLAY_APPLY_TEXTDELTA)
{
- const char *rev;
-
- rev = svn_xml_get_attr_value("rev", attrs);
- if (!rev)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in target-revision element"));
- }
-
- SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
- SVN_STR_TO_REV(rev),
- scratch_pool));
+ struct replay_node_t *node = ctx->current_node;
+ apr_hash_t *attrs;
+ const char *checksum;
+ svn_txdelta_window_handler_t handler;
+ void *handler_baton;
+
+ if (! node || ! node->file || node->stream)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+ /* ### Is there a better way to access a specific attr here? */
+ attrs = svn_ra_serf__xml_gather_since(xes, REPLAY_APPLY_TEXTDELTA);
+ checksum = svn_hash_gets(attrs, "checksum");
+
+ SVN_ERR(ctx->editor->apply_textdelta(node->baton, checksum, node->pool,
+ &handler, &handler_baton));
+
+ if (handler != svn_delta_noop_window_handler)
+ {
+ node->stream = svn_base64_decode(
+ svn_txdelta_parse_svndiff(handler,
+ handler_baton,
+ TRUE,
+ node->pool),
+ node->pool);
+ }
}
- else if (state == REPORT &&
- strcmp(name.name, "open-root") == 0)
- {
- const char *rev;
- replay_info_t *info;
- rev = svn_xml_get_attr_value("rev", attrs);
-
- if (!rev)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-root element"));
- }
+ return SVN_NO_ERROR;
+}
- info = push_state(parser, ctx, OPEN_DIR);
+/* Conforms to svn_ra_serf__xml_closed_t */
+static svn_error_t *
+replay_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)
+{
+ struct revision_report_t *ctx = baton;
- SVN_ERR(ctx->editor->open_root(ctx->editor_baton,
- SVN_STR_TO_REV(rev),
- ctx->dst_rev_pool,
- &info->baton));
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "delete-entry") == 0)
+ if (leaving_state == REPLAY_REPORT)
{
- const char *file_name, *rev;
- replay_info_t *info;
+ if (ctx->current_node)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- file_name = svn_xml_get_attr_value("name", attrs);
- if (!file_name)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in delete-entry element"));
- }
- rev = svn_xml_get_attr_value("rev", attrs);
- if (!rev)
+ if (ctx->revfinish_func)
{
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in delete-entry element"));
+ SVN_ERR(ctx->revfinish_func(ctx->revision, ctx->replay_baton,
+ ctx->editor, ctx->editor_baton,
+ ctx->rev_props, scratch_pool));
}
+ }
+ else if (leaving_state == REPLAY_TARGET_REVISION)
+ {
+ const char *revstr = svn_hash_gets(attrs, "rev");
+ apr_int64_t rev;
- info = push_state(parser, ctx, DELETE_ENTRY);
-
- SVN_ERR(ctx->editor->delete_entry(file_name, SVN_STR_TO_REV(rev),
- info->baton, scratch_pool));
-
- svn_ra_serf__xml_pop_state(parser);
+ SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+ SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
+ (svn_revnum_t)rev,
+ scratch_pool));
}
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "open-directory") == 0)
+ else if (leaving_state == REPLAY_OPEN_ROOT)
{
- const char *rev, *dir_name;
- replay_info_t *info;
+ const char *revstr = svn_hash_gets(attrs, "rev");
+ apr_int64_t rev;
+ apr_pool_t *root_pool = svn_pool_create(ctx->pool);
- dir_name = svn_xml_get_attr_value("name", attrs);
- if (!dir_name)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in open-directory element"));
- }
- rev = svn_xml_get_attr_value("rev", attrs);
- if (!rev)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-directory element"));
- }
+ if (ctx->current_node || ctx->root_node)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- info = push_state(parser, ctx, OPEN_DIR);
+ ctx->root_node = apr_pcalloc(root_pool, sizeof(*ctx->root_node));
+ ctx->root_node->pool = root_pool;
- SVN_ERR(ctx->editor->open_directory(dir_name, info->parent->baton,
- SVN_STR_TO_REV(rev),
- ctx->dst_rev_pool, &info->baton));
+ ctx->current_node = ctx->root_node;
+
+ SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+ SVN_ERR(ctx->editor->open_root(ctx->editor_baton, (svn_revnum_t)rev,
+ root_pool,
+ &ctx->current_node->baton));
}
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "add-directory") == 0)
+ else if (leaving_state == REPLAY_OPEN_DIRECTORY
+ || leaving_state == REPLAY_OPEN_FILE
+ || leaving_state == REPLAY_ADD_DIRECTORY
+ || leaving_state == REPLAY_ADD_FILE)
{
- const char *dir_name, *copyfrom, *copyrev;
- svn_revnum_t rev;
- replay_info_t *info;
-
- dir_name = svn_xml_get_attr_value("name", attrs);
- if (!dir_name)
+ struct replay_node_t *node;
+ apr_pool_t *node_pool;
+ const char *name = svn_hash_gets(attrs, "name");
+ const char *rev_str;
+ apr_int64_t rev;
+
+ if (!ctx->current_node || ctx->current_node->file)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+ node_pool = svn_pool_create(ctx->current_node->pool);
+ node = apr_pcalloc(node_pool, sizeof(*node));
+ node->pool = node_pool;
+ node->parent = ctx->current_node;
+
+ if (leaving_state == REPLAY_OPEN_DIRECTORY
+ || leaving_state == REPLAY_OPEN_FILE)
{
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in add-directory element"));
+ rev_str = svn_hash_gets(attrs, "rev");
}
- copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
- copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
+ else
+ rev_str = svn_hash_gets(attrs, "copyfrom-rev");
- if (copyrev)
- rev = SVN_STR_TO_REV(copyrev);
+ if (rev_str)
+ SVN_ERR(svn_cstring_atoi64(&rev, rev_str));
else
rev = SVN_INVALID_REVNUM;
- info = push_state(parser, ctx, ADD_DIR);
-
- SVN_ERR(ctx->editor->add_directory(dir_name, info->parent->baton,
- copyfrom, rev,
- ctx->dst_rev_pool, &info->baton));
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "close-directory") == 0)
- {
- replay_info_t *info = parser->state->private;
-
- SVN_ERR(ctx->editor->close_directory(info->baton, scratch_pool));
-
- svn_ra_serf__xml_pop_state(parser);
-
- svn_pool_destroy(info->pool);
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "open-file") == 0)
- {
- const char *file_name, *rev;
- replay_info_t *info;
-
- file_name = svn_xml_get_attr_value("name", attrs);
- if (!file_name)
+ switch (leaving_state)
{
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in open-file element"));
+ case REPLAY_OPEN_DIRECTORY:
+ node->file = FALSE;
+ SVN_ERR(ctx->editor->open_directory(name,
+ ctx->current_node->baton,
+ (svn_revnum_t)rev,
+ node->pool,
+ &node->baton));
+ break;
+ case REPLAY_OPEN_FILE:
+ node->file = TRUE;
+ SVN_ERR(ctx->editor->open_file(name,
+ ctx->current_node->baton,
+ (svn_revnum_t)rev,
+ node->pool,
+ &node->baton));
+ break;
+ case REPLAY_ADD_DIRECTORY:
+ node->file = FALSE;
+ SVN_ERR(ctx->editor->add_directory(
+ name,
+ ctx->current_node->baton,
+ SVN_IS_VALID_REVNUM(rev)
+ ? svn_hash_gets(attrs, "copyfrom-path")
+ : NULL,
+ (svn_revnum_t)rev,
+ node->pool,
+ &node->baton));
+ break;
+ case REPLAY_ADD_FILE:
+ node->file = TRUE;
+ SVN_ERR(ctx->editor->add_file(
+ name,
+ ctx->current_node->baton,
+ SVN_IS_VALID_REVNUM(rev)
+ ? svn_hash_gets(attrs, "copyfrom-path")
+ : NULL,
+ (svn_revnum_t)rev,
+ node->pool,
+ &node->baton));
+ break;
+ /* default: unreachable */
}
- rev = svn_xml_get_attr_value("rev", attrs);
- if (!rev)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-file element"));
- }
-
- info = push_state(parser, ctx, OPEN_FILE);
-
- SVN_ERR(ctx->editor->open_file(file_name, info->parent->baton,
- SVN_STR_TO_REV(rev),
- info->pool, &info->baton));
+ ctx->current_node = node;
}
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "add-file") == 0)
+ else if (leaving_state == REPLAY_CLOSE_FILE)
{
- const char *file_name, *copyfrom, *copyrev;
- svn_revnum_t rev;
- replay_info_t *info;
-
- file_name = svn_xml_get_attr_value("name", attrs);
- if (!file_name)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in add-file element"));
- }
- copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
- copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
-
- info = push_state(parser, ctx, ADD_FILE);
+ struct replay_node_t *node = ctx->current_node;
- if (copyrev)
- rev = SVN_STR_TO_REV(copyrev);
- else
- rev = SVN_INVALID_REVNUM;
+ if (! node || ! node->file)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- SVN_ERR(ctx->editor->add_file(file_name, info->parent->baton,
- copyfrom, rev,
- info->pool, &info->baton));
+ SVN_ERR(ctx->editor->close_file(node->baton,
+ svn_hash_gets(attrs, "checksum"),
+ node->pool));
+ ctx->current_node = node->parent;
+ svn_pool_destroy(node->pool);
}
- else if ((state == OPEN_FILE || state == ADD_FILE) &&
- strcmp(name.name, "apply-textdelta") == 0)
+ else if (leaving_state == REPLAY_CLOSE_DIRECTORY)
{
- const char *checksum;
- replay_info_t *info;
- svn_txdelta_window_handler_t textdelta;
- void *textdelta_baton;
- svn_stream_t *delta_stream;
-
- info = push_state(parser, ctx, APPLY_TEXTDELTA);
-
- checksum = svn_xml_get_attr_value("checksum", attrs);
- if (checksum)
- {
- checksum = apr_pstrdup(info->pool, checksum);
- }
+ struct replay_node_t *node = ctx->current_node;
- SVN_ERR(ctx->editor->apply_textdelta(info->baton, checksum,
- info->pool,
- &textdelta,
- &textdelta_baton));
+ if (! node || node->file)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- delta_stream = svn_txdelta_parse_svndiff(textdelta, textdelta_baton,
- TRUE, info->pool);
- info->stream = svn_base64_decode(delta_stream, info->pool);
+ SVN_ERR(ctx->editor->close_directory(node->baton, node->pool));
+ ctx->current_node = node->parent;
+ svn_pool_destroy(node->pool);
}
- else if ((state == OPEN_FILE || state == ADD_FILE) &&
- strcmp(name.name, "close-file") == 0)
+ else if (leaving_state == REPLAY_DELETE_ENTRY)
{
- replay_info_t *info = parser->state->private;
- const char *checksum;
-
- checksum = svn_xml_get_attr_value("checksum", attrs);
-
- SVN_ERR(ctx->editor->close_file(info->baton, checksum, scratch_pool));
-
- svn_ra_serf__xml_pop_state(parser);
-
- svn_pool_destroy(info->pool);
+ struct replay_node_t *parent_node = ctx->current_node;
+ const char *name = svn_hash_gets(attrs, "name");
+ const char *revstr = svn_hash_gets(attrs, "rev");
+ apr_int64_t rev;
+
+ if (! parent_node || parent_node->file)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+
+ SVN_ERR(svn_cstring_atoi64(&rev, revstr));
+ SVN_ERR(ctx->editor->delete_entry(name,
+ (svn_revnum_t)rev,
+ parent_node->baton,
+ scratch_pool));
}
- else if (((state == OPEN_FILE || state == ADD_FILE) &&
- strcmp(name.name, "change-file-prop") == 0) ||
- ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "change-dir-prop") == 0))
+ else if (leaving_state == REPLAY_CHANGE_FILE_PROP
+ || leaving_state == REPLAY_CHANGE_DIRECTORY_PROP)
{
- const char *prop_name;
- prop_info_t *info;
+ struct replay_node_t *node = ctx->current_node;
+ const char *name;
+ const svn_string_t *value;
- prop_name = svn_xml_get_attr_value("name", attrs);
- if (!prop_name)
- {
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in %s element"),
- name.name);
- }
-
- info = push_state(parser, ctx, CHANGE_PROP);
+ if (! node || node->file != (leaving_state == REPLAY_CHANGE_FILE_PROP))
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
+ name = svn_hash_gets(attrs, "name");
- if (svn_xml_get_attr_value("del", attrs))
- info->del_prop = TRUE;
+ if (svn_hash_gets(attrs, "del"))
+ value = NULL;
else
- info->del_prop = FALSE;
+ value = svn_base64_decode_string(cdata, scratch_pool);
- info->name = apr_pstrdup(info->pool, prop_name);
- if (state == OPEN_FILE || state == ADD_FILE)
+ if (node->file)
{
- info->change = ctx->editor->change_file_prop;
+ SVN_ERR(ctx->editor->change_file_prop(node->baton, name, value,
+ scratch_pool));
}
else
{
- info->change = ctx->editor->change_dir_prop;
- }
-
- }
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-end_replay(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- apr_pool_t *scratch_pool)
-{
- replay_context_t *ctx = parser->user_data;
- replay_state_e state;
-
- state = parser->state->current_state;
-
- if (state == REPORT &&
- strcmp(name.name, "editor-report") == 0)
- {
- svn_ra_serf__xml_pop_state(parser);
- if (ctx->revfinish_func)
- {
- SVN_ERR(ctx->revfinish_func(ctx->revision, ctx->replay_baton,
- ctx->editor, ctx->editor_baton,
- ctx->props,
- ctx->dst_rev_pool));
+ SVN_ERR(ctx->editor->change_dir_prop(node->baton, name, value,
+ scratch_pool));
}
- svn_pool_destroy(ctx->dst_rev_pool);
- }
- else if (state == OPEN_DIR && strcmp(name.name, "open-directory") == 0)
- {
- /* Don't do anything. */
- }
- else if (state == ADD_DIR && strcmp(name.name, "add-directory") == 0)
- {
- /* Don't do anything. */
- }
- else if (state == OPEN_FILE && strcmp(name.name, "open-file") == 0)
- {
- /* Don't do anything. */
- }
- else if (state == ADD_FILE && strcmp(name.name, "add-file") == 0)
- {
- /* Don't do anything. */
- }
- else if ((state == OPEN_FILE || state == ADD_FILE) &&
- strcmp(name.name, "close-file") == 0)
- {
- /* Don't do anything. */
- }
- else if ((state == APPLY_TEXTDELTA) &&
- strcmp(name.name, "apply-textdelta") == 0)
- {
- replay_info_t *info = parser->state->private;
- SVN_ERR(svn_stream_close(info->stream));
- svn_ra_serf__xml_pop_state(parser);
}
- else if (state == CHANGE_PROP &&
- (strcmp(name.name, "change-file-prop") == 0 ||
- strcmp(name.name, "change-dir-prop") == 0))
+ else if (leaving_state == REPLAY_APPLY_TEXTDELTA)
{
- prop_info_t *info = parser->state->private;
- const svn_string_t *prop_val;
+ struct replay_node_t *node = ctx->current_node;
- if (info->del_prop)
- {
- prop_val = NULL;
- }
- else
- {
- const svn_string_t *morph;
-
- morph = svn_stringbuf__morph_into_string(info->prop_value);
-#ifdef SVN_DEBUG
- info->prop_value = NULL; /* morph killed the stringbuf. */
-#endif
+ if (! node || ! node->file || ! node->stream)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- prop_val = svn_base64_decode_string(morph, info->pool);
- }
+ SVN_ERR(svn_stream_close(node->stream));
- SVN_ERR(info->change(info->parent->baton, info->name, prop_val,
- info->parent->pool));
- svn_ra_serf__xml_pop_state(parser);
-
- svn_pool_destroy(info->pool);
+ node->stream = NULL;
}
-
return SVN_NO_ERROR;
}
+/* Conforms to svn_ra_serf__xml_cdata_t */
static svn_error_t *
-cdata_replay(svn_ra_serf__xml_parser_t *parser,
+replay_cdata(svn_ra_serf__xml_estate_t *xes,
+ void *baton,
+ int current_state,
const char *data,
apr_size_t len,
apr_pool_t *scratch_pool)
{
- replay_context_t *replay_ctx = parser->user_data;
- replay_state_e state;
-
- UNUSED_CTX(replay_ctx);
+ struct revision_report_t *ctx = baton;
- state = parser->state->current_state;
-
- if (state == APPLY_TEXTDELTA)
+ if (current_state == REPLAY_APPLY_TEXTDELTA)
{
- replay_info_t *info = parser->state->private;
- apr_size_t written;
-
- written = len;
+ struct replay_node_t *node = ctx->current_node;
- SVN_ERR(svn_stream_write(info->stream, data, &written));
+ if (! node || ! node->file)
+ return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL);
- if (written != len)
- return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
- _("Error writing stream: unexpected EOF"));
- }
- else if (state == CHANGE_PROP)
- {
- prop_info_t *info = parser->state->private;
+ if (node->stream)
+ {
+ apr_size_t written = len;
- svn_stringbuf_appendbytes(info->prop_value, data, len);
+ SVN_ERR(svn_stream_write(node->stream, data, &written));
+ if (written != len)
+ return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
+ _("Error writing stream: unexpected EOF"));
+ }
}
return SVN_NO_ERROR;
}
+/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_replay_body(serf_bucket_t **bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
- replay_context_t *ctx = baton;
+ struct revision_report_t *ctx = baton;
serf_bucket_t *body_bkt;
body_bkt = serf_bucket_aggregate_create(alloc);
@@ -601,7 +485,7 @@ create_replay_body(serf_bucket_t **bkt,
svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
"S:replay-report",
"xmlns:S", SVN_XML_NAMESPACE,
- NULL);
+ SVN_VA_NULL);
/* If we have a non-NULL include path, we add it to the body and
omit the revision; otherwise, the reverse. */
@@ -616,17 +500,17 @@ create_replay_body(serf_bucket_t **bkt,
{
svn_ra_serf__add_tag_buckets(body_bkt,
"S:revision",
- apr_ltoa(ctx->src_rev_pool, ctx->revision),
+ apr_ltoa(pool, ctx->revision),
alloc);
}
svn_ra_serf__add_tag_buckets(body_bkt,
"S:low-water-mark",
- apr_ltoa(ctx->src_rev_pool, ctx->low_water_mark),
+ apr_ltoa(pool, ctx->low_water_mark),
alloc);
svn_ra_serf__add_tag_buckets(body_bkt,
"S:send-deltas",
- apr_ltoa(ctx->src_rev_pool, ctx->send_deltas),
+ apr_ltoa(pool, ctx->send_deltas),
alloc);
svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "S:replay-report");
@@ -642,65 +526,49 @@ svn_ra_serf__replay(svn_ra_session_t *ra_session,
svn_boolean_t send_deltas,
const svn_delta_editor_t *editor,
void *edit_baton,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
- replay_context_t *replay_ctx;
+ struct revision_report_t ctx = { NULL };
svn_ra_serf__session_t *session = ra_session->priv;
svn_ra_serf__handler_t *handler;
- svn_ra_serf__xml_parser_t *parser_ctx;
- svn_error_t *err;
+ svn_ra_serf__xml_context_t *xmlctx;
const char *report_target;
- SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
+ SVN_ERR(svn_ra_serf__report_resource(&report_target, session,
+ scratch_pool));
+
+ ctx.pool = svn_pool_create(scratch_pool);
+ ctx.editor = editor;
+ ctx.editor_baton = edit_baton;
+ ctx.done = FALSE;
+ ctx.revision = revision;
+ ctx.low_water_mark = low_water_mark;
+ ctx.send_deltas = send_deltas;
+ ctx.rev_props = apr_hash_make(scratch_pool);
- replay_ctx = apr_pcalloc(pool, sizeof(*replay_ctx));
- replay_ctx->src_rev_pool = pool;
- replay_ctx->editor = editor;
- replay_ctx->editor_baton = edit_baton;
- replay_ctx->done = FALSE;
- replay_ctx->revision = revision;
- replay_ctx->low_water_mark = low_water_mark;
- replay_ctx->send_deltas = send_deltas;
- replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
+ xmlctx = svn_ra_serf__xml_context_create(replay_ttable,
+ replay_opened, replay_closed,
+ replay_cdata,
+ &ctx,
+ scratch_pool);
- handler = apr_pcalloc(pool, sizeof(*handler));
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL,
+ scratch_pool);
- handler->handler_pool = pool;
handler->method = "REPORT";
handler->path = session->session_url.path;
handler->body_delegate = create_replay_body;
- handler->body_delegate_baton = replay_ctx;
+ handler->body_delegate_baton = &ctx;
handler->body_type = "text/xml";
- handler->conn = session->conns[0];
- handler->session = session;
- parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
+ /* Not setting up done handler as we don't use a global context */
- parser_ctx->pool = pool;
- parser_ctx->user_data = replay_ctx;
- parser_ctx->start = start_replay;
- parser_ctx->end = end_replay;
- parser_ctx->cdata = cdata_replay;
- parser_ctx->done = &replay_ctx->done;
+ SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
- handler->response_handler = svn_ra_serf__handle_xml_parser;
- handler->response_baton = parser_ctx;
-
- /* This is only needed to handle errors during XML parsing. */
- replay_ctx->parser_ctx = parser_ctx;
- replay_ctx->report_handler = handler; /* unused */
-
- svn_ra_serf__request_create(handler);
-
- err = svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool);
-
- SVN_ERR(svn_error_compose_create(
+ return svn_error_trace(
svn_ra_serf__error_on_status(handler->sline,
handler->path,
- handler->location),
- err));
-
- return SVN_NO_ERROR;
+ handler->location));
}
/* The maximum number of outstanding requests at any time. When this
@@ -734,6 +602,33 @@ svn_ra_serf__replay(svn_ra_session_t *ra_session,
*/
#define MAX_OUTSTANDING_REQUESTS 50
+/* Implements svn_ra_serf__response_done_delegate_t for svn_ra_serf__replay_range */
+static svn_error_t *
+replay_done(serf_request_t *request,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ struct revision_report_t *ctx = baton;
+ svn_ra_serf__handler_t *handler = ctx->report_handler;
+
+ if (handler->server_error)
+ return svn_ra_serf__server_error_create(handler, scratch_pool);
+ else if (handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
+
+ *ctx->done = TRUE; /* Breaks out svn_ra_serf__context_run_wait */
+
+ /* Are re replaying multiple revisions? */
+ if (ctx->replay_reports)
+ {
+ (*ctx->replay_reports)--;
+ }
+
+ svn_pool_destroy(ctx->pool); /* Destroys handler and request! */
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
svn_revnum_t start_revision,
@@ -743,15 +638,17 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
svn_ra_replay_revstart_callback_t revstart_func,
svn_ra_replay_revfinish_callback_t revfinish_func,
void *replay_baton,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
svn_ra_serf__session_t *session = ra_session->priv;
svn_revnum_t rev = start_revision;
const char *report_target;
int active_reports = 0;
const char *include_path;
+ svn_boolean_t done;
- SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
+ SVN_ERR(svn_ra_serf__report_resource(&report_target, session,
+ scratch_pool));
/* Prior to 1.8, mod_dav_svn expect to get replay REPORT requests
aimed at the session URL. But that's incorrect -- these reports
@@ -774,8 +671,7 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
{
SVN_ERR(svn_ra_serf__get_relative_path(&include_path,
session->session_url.path,
- session, session->conns[0],
- pool));
+ session, scratch_pool));
}
else
{
@@ -784,10 +680,6 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
while (active_reports || rev <= end_revision)
{
- svn_ra_serf__list_t *done_list;
- svn_ra_serf__list_t *done_reports = NULL;
- replay_context_t *replay_ctx;
-
if (session->cancel_func)
SVN_ERR(session->cancel_func(session->cancel_baton));
@@ -795,54 +687,56 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
requests to MAX_OUTSTANDING_REQUESTS. */
if (rev <= end_revision && active_reports < MAX_OUTSTANDING_REQUESTS)
{
+ struct revision_report_t *rev_ctx;
svn_ra_serf__handler_t *handler;
- svn_ra_serf__xml_parser_t *parser_ctx;
- apr_pool_t *ctx_pool = svn_pool_create(pool);
+ apr_pool_t *rev_pool = svn_pool_create(scratch_pool);
+ svn_ra_serf__xml_context_t *xmlctx;
const char *replay_target;
- replay_ctx = apr_pcalloc(ctx_pool, sizeof(*replay_ctx));
- replay_ctx->src_rev_pool = ctx_pool;
- replay_ctx->revstart_func = revstart_func;
- replay_ctx->revfinish_func = revfinish_func;
- replay_ctx->replay_baton = replay_baton;
- replay_ctx->done = FALSE;
- replay_ctx->include_path = include_path;
- replay_ctx->revision = rev;
- replay_ctx->low_water_mark = low_water_mark;
- replay_ctx->send_deltas = send_deltas;
- replay_ctx->done_item.data = replay_ctx;
+ rev_ctx = apr_pcalloc(rev_pool, sizeof(*rev_ctx));
+ rev_ctx->pool = rev_pool;
+ rev_ctx->revstart_func = revstart_func;
+ rev_ctx->revfinish_func = revfinish_func;
+ rev_ctx->replay_baton = replay_baton;
+ rev_ctx->done = &done;
+ rev_ctx->replay_reports = &active_reports;
+ rev_ctx->include_path = include_path;
+ rev_ctx->revision = rev;
+ rev_ctx->low_water_mark = low_water_mark;
+ rev_ctx->send_deltas = send_deltas;
/* Request all properties of a certain revision. */
- replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
+ rev_ctx->rev_props = apr_hash_make(rev_ctx->pool);
if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
{
- replay_ctx->revprop_target = apr_psprintf(pool, "%s/%ld",
- session->rev_stub, rev);
- replay_ctx->revprop_rev = SVN_INVALID_REVNUM;
+ rev_ctx->revprop_target = apr_psprintf(rev_pool, "%s/%ld",
+ session->rev_stub, rev);
+ rev_ctx->revprop_rev = SVN_INVALID_REVNUM;
}
else
{
- replay_ctx->revprop_target = report_target;
- replay_ctx->revprop_rev = rev;
+ rev_ctx->revprop_target = report_target;
+ rev_ctx->revprop_rev = rev;
}
- SVN_ERR(svn_ra_serf__deliver_props(&replay_ctx->propfind_handler,
- replay_ctx->revs_props, session,
- session->conns[0],
- replay_ctx->revprop_target,
- replay_ctx->revprop_rev,
- "0", all_props,
- NULL,
- replay_ctx->src_rev_pool));
+ SVN_ERR(svn_ra_serf__create_propfind_handler(
+ &rev_ctx->propfind_handler,
+ session,
+ rev_ctx->revprop_target,
+ rev_ctx->revprop_rev,
+ "0", all_props,
+ svn_ra_serf__deliver_svn_props,
+ rev_ctx->rev_props,
+ rev_pool));
/* Spin up the serf request for the PROPFIND. */
- svn_ra_serf__request_create(replay_ctx->propfind_handler);
+ svn_ra_serf__request_create(rev_ctx->propfind_handler);
/* Send the replay REPORT request. */
if (session->supports_rev_rsrc_replay)
{
- replay_target = apr_psprintf(pool, "%s/%ld",
+ replay_target = apr_psprintf(rev_pool, "%s/%ld",
session->rev_stub, rev);
}
else
@@ -850,41 +744,24 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
replay_target = session->session_url.path;
}
- handler = apr_pcalloc(replay_ctx->src_rev_pool, sizeof(*handler));
+ xmlctx = svn_ra_serf__xml_context_create(replay_ttable,
+ replay_opened, replay_closed,
+ replay_cdata, rev_ctx,
+ rev_pool);
+
+ handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL,
+ rev_pool);
- handler->handler_pool = replay_ctx->src_rev_pool;
handler->method = "REPORT";
handler->path = replay_target;
handler->body_delegate = create_replay_body;
- handler->body_delegate_baton = replay_ctx;
- handler->conn = session->conns[0];
- handler->session = session;
-
- parser_ctx = apr_pcalloc(replay_ctx->src_rev_pool,
- sizeof(*parser_ctx));
-
- /* Setup the XML parser context.
- Because we have not one but a list of requests, the 'done' property
- on the replay_ctx is not of much use. Instead, use 'done_list'.
- On each handled response (succesfully or not), the parser will add
- done_item to done_list, so by keeping track of the state of
- done_list we know how many requests have been handled completely.
- */
- parser_ctx->pool = replay_ctx->src_rev_pool;
- parser_ctx->user_data = replay_ctx;
- parser_ctx->start = start_replay;
- parser_ctx->end = end_replay;
- parser_ctx->cdata = cdata_replay;
- parser_ctx->done = &replay_ctx->done;
- parser_ctx->done_list = &done_reports;
- parser_ctx->done_item = &replay_ctx->done_item;
- handler->response_handler = svn_ra_serf__handle_xml_parser;
- handler->response_baton = parser_ctx;
- replay_ctx->report_handler = handler;
-
- /* This is only needed to handle errors during XML parsing. */
- replay_ctx->parser_ctx = parser_ctx;
+ handler->body_delegate_baton = rev_ctx;
+ handler->body_type = "text/xml";
+
+ handler->done_delegate = replay_done;
+ handler->done_delegate_baton = rev_ctx;
+ rev_ctx->report_handler = handler;
svn_ra_serf__request_create(handler);
rev++;
@@ -892,26 +769,12 @@ svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
}
/* Run the serf loop. */
- SVN_ERR(svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool));
-
- /* Substract the number of completely handled responses from our
- total nr. of open requests', so we'll know when to stop this loop.
- Since the message is completely handled, we can destroy its pool. */
- done_list = done_reports;
- while (done_list)
- {
- replay_context_t *ctx = (replay_context_t *)done_list->data;
- svn_ra_serf__handler_t *done_handler = ctx->report_handler;
-
- done_list = done_list->next;
- SVN_ERR(svn_ra_serf__error_on_status(done_handler->sline,
- done_handler->path,
- done_handler->location));
- svn_pool_destroy(ctx->src_rev_pool);
- active_reports--;
- }
+ done = FALSE;
+ SVN_ERR(svn_ra_serf__context_run_wait(&done, session, scratch_pool));
- done_reports = NULL;
+ /* The done handler of reports decrements active_reports when a report
+ is done. This same handler reports (fatal) report errors, so we can
+ just loop here. */
}
return SVN_NO_ERROR;
diff --git a/subversion/libsvn_ra_serf/serf.c b/subversion/libsvn_ra_serf/serf.c
index 66f9962..3c47d5e 100644
--- a/subversion/libsvn_ra_serf/serf.c
+++ b/subversion/libsvn_ra_serf/serf.c
@@ -39,6 +39,7 @@
#include "svn_dirent_uri.h"
#include "svn_hash.h"
#include "svn_path.h"
+#include "svn_props.h"
#include "svn_time.h"
#include "svn_version.h"
@@ -63,7 +64,7 @@ ra_serf_version(void)
#define RA_SERF_DESCRIPTION_VER \
N_("Module for accessing a repository via WebDAV protocol using serf.\n" \
- " - using serf %d.%d.%d")
+ " - using serf %d.%d.%d (compiled with %d.%d.%d)")
/* Implements svn_ra__vtable_t.get_description(). */
static const char *
@@ -72,7 +73,12 @@ ra_serf_get_description(apr_pool_t *pool)
int major, minor, patch;
serf_lib_version(&major, &minor, &patch);
- return apr_psprintf(pool, _(RA_SERF_DESCRIPTION_VER), major, minor, patch);
+ return apr_psprintf(pool, _(RA_SERF_DESCRIPTION_VER),
+ major, minor, patch,
+ SERF_MAJOR_VERSION,
+ SERF_MINOR_VERSION,
+ SERF_PATCH_VERSION
+ );
}
/* Implements svn_ra__vtable_t.get_schemes(). */
@@ -144,10 +150,6 @@ load_http_auth_types(apr_pool_t *pool, svn_config_t *config,
runtime configuration variable. */
#define DEFAULT_HTTP_TIMEOUT 600
-/* Private symbol for the 1.9-public SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS */
-#define OPTION_HTTP_CHUNKED_REQUESTS "http-chunked-requests"
-
-
static svn_error_t *
load_config(svn_ra_serf__session_t *session,
apr_hash_t *config_hash,
@@ -161,6 +163,10 @@ load_config(svn_ra_serf__session_t *session,
const char *exceptions;
apr_port_t proxy_port;
svn_tristate_t chunked_requests;
+#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING)
+ apr_int64_t log_components;
+ apr_int64_t log_level;
+#endif
if (config_hash)
{
@@ -179,17 +185,17 @@ load_config(svn_ra_serf__session_t *session,
svn_config_get(config, &timeout_str, SVN_CONFIG_SECTION_GLOBAL,
SVN_CONFIG_OPTION_HTTP_TIMEOUT, NULL);
- if (session->wc_callbacks->auth_baton)
+ if (session->auth_baton)
{
if (config_client)
{
- svn_auth_set_parameter(session->wc_callbacks->auth_baton,
+ svn_auth_set_parameter(session->auth_baton,
SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG,
config_client);
}
if (config)
{
- svn_auth_set_parameter(session->wc_callbacks->auth_baton,
+ svn_auth_set_parameter(session->auth_baton,
SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS,
config);
}
@@ -237,18 +243,25 @@ load_config(svn_ra_serf__session_t *session,
SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS,
SVN_CONFIG_DEFAULT_OPTION_HTTP_MAX_CONNECTIONS));
- /* Should we use chunked transfer encoding. */
+ /* Should we use chunked transfer encoding. */
SVN_ERR(svn_config_get_tristate(config, &chunked_requests,
SVN_CONFIG_SECTION_GLOBAL,
- OPTION_HTTP_CHUNKED_REQUESTS,
+ SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS,
"auto", svn_tristate_unknown));
- if (config)
- server_group = svn_config_find_group(config,
- session->session_url.hostname,
- SVN_CONFIG_SECTION_GROUPS, pool);
- else
- server_group = NULL;
+#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING)
+ SVN_ERR(svn_config_get_int64(config, &log_components,
+ SVN_CONFIG_SECTION_GLOBAL,
+ SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS,
+ SERF_LOGCOMP_NONE));
+ SVN_ERR(svn_config_get_int64(config, &log_level,
+ SVN_CONFIG_SECTION_GLOBAL,
+ SVN_CONFIG_OPTION_SERF_LOG_LEVEL,
+ SERF_LOG_INFO));
+#endif
+
+ server_group = svn_auth_get_parameter(session->auth_baton,
+ SVN_AUTH_PARAM_SERVER_GROUP);
if (server_group)
{
@@ -259,9 +272,6 @@ load_config(svn_ra_serf__session_t *session,
svn_config_get(config, &timeout_str, server_group,
SVN_CONFIG_OPTION_HTTP_TIMEOUT, timeout_str);
- svn_auth_set_parameter(session->wc_callbacks->auth_baton,
- SVN_AUTH_PARAM_SERVER_GROUP, server_group);
-
/* Load the group proxy server settings, overriding global
settings. We intentionally ignore 'http-proxy-exceptions'
here because, well, if this site was an exception, why is
@@ -300,12 +310,42 @@ load_config(svn_ra_serf__session_t *session,
SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS,
session->max_connections));
- /* Should we use chunked transfer encoding. */
+ /* Should we use chunked transfer encoding. */
SVN_ERR(svn_config_get_tristate(config, &chunked_requests,
server_group,
- OPTION_HTTP_CHUNKED_REQUESTS,
+ SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS,
"auto", chunked_requests));
+
+#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING)
+ SVN_ERR(svn_config_get_int64(config, &log_components,
+ server_group,
+ SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS,
+ log_components));
+ SVN_ERR(svn_config_get_int64(config, &log_level,
+ server_group,
+ SVN_CONFIG_OPTION_SERF_LOG_LEVEL,
+ log_level));
+#endif
+ }
+
+#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING)
+ if (log_components != SERF_LOGCOMP_NONE)
+ {
+ serf_log_output_t *output;
+ apr_status_t status;
+
+ status = serf_logging_create_stream_output(&output,
+ session->context,
+ (apr_uint32_t)log_level,
+ (apr_uint32_t)log_components,
+ SERF_LOG_DEFAULT_LAYOUT,
+ stderr,
+ pool);
+
+ if (!status)
+ serf_logging_add_output(session->context, output);
}
+#endif
/* Don't allow the http-max-connections value to be larger than our
compiled-in limit, or to be too small to operate. Broken
@@ -439,8 +479,10 @@ svn_ra_serf__open(svn_ra_session_t *session,
const char *session_URL,
const svn_ra_callbacks2_t *callbacks,
void *callback_baton,
+ svn_auth_baton_t *auth_baton,
apr_hash_t *config,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
apr_status_t status;
svn_ra_serf__session_t *serf_sess;
@@ -451,10 +493,15 @@ svn_ra_serf__open(svn_ra_session_t *session,
if (corrected_url)
*corrected_url = NULL;
- serf_sess = apr_pcalloc(pool, sizeof(*serf_sess));
- serf_sess->pool = svn_pool_create(pool);
+ serf_sess = apr_pcalloc(result_pool, sizeof(*serf_sess));
+ serf_sess->pool = result_pool;
+ if (config)
+ SVN_ERR(svn_config_copy_config(&serf_sess->config, config, result_pool));
+ else
+ serf_sess->config = NULL;
serf_sess->wc_callbacks = callbacks;
serf_sess->wc_callback_baton = callback_baton;
+ serf_sess->auth_baton = auth_baton;
serf_sess->progress_func = callbacks->progress_func;
serf_sess->progress_baton = callbacks->progress_baton;
serf_sess->cancel_func = callbacks->cancel_func;
@@ -467,19 +514,8 @@ svn_ra_serf__open(svn_ra_session_t *session,
serf_sess->pool));
- status = apr_uri_parse(serf_sess->pool, session_URL, &url);
- if (status)
- {
- return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
- _("Illegal URL '%s'"),
- session_URL);
- }
- /* Depending the version of apr-util in use, for root paths url.path
- will be NULL or "", where serf requires "/". */
- if (url.path == NULL || url.path[0] == '\0')
- {
- url.path = apr_pstrdup(serf_sess->pool, "/");
- }
+ SVN_ERR(svn_ra_serf__uri_parse(&url, session_URL, serf_sess->pool));
+
if (!url.port)
{
url.port = apr_uri_port_of_scheme(url.scheme);
@@ -511,13 +547,16 @@ svn_ra_serf__open(svn_ra_session_t *session,
/* create the user agent string */
if (callbacks->get_client_string)
- SVN_ERR(callbacks->get_client_string(callback_baton, &client_string, pool));
+ SVN_ERR(callbacks->get_client_string(callback_baton, &client_string,
+ scratch_pool));
if (client_string)
- serf_sess->useragent = apr_pstrcat(pool, get_user_agent_string(pool), " ",
- client_string, (char *)NULL);
+ serf_sess->useragent = apr_pstrcat(result_pool,
+ get_user_agent_string(scratch_pool),
+ " ",
+ client_string, SVN_VA_NULL);
else
- serf_sess->useragent = get_user_agent_string(pool);
+ serf_sess->useragent = get_user_agent_string(result_pool);
/* go ahead and tell serf about the connection. */
status =
@@ -538,7 +577,24 @@ svn_ra_serf__open(svn_ra_session_t *session,
session->priv = serf_sess;
- err = svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, pool);
+ /* The following code explicitly works around a bug in serf <= r2319 / 1.3.8
+ where serf doesn't report the request as failed/cancelled when the
+ authorization request handler fails to handle the request.
+
+ As long as we allocate the request in a subpool of the serf connection
+ pool, we know that the handler is always cleaned before the connection.
+
+ Luckily our caller now passes us two pools which handle this case.
+ */
+#if defined(SVN_DEBUG) && !SERF_VERSION_AT_LEAST(1,4,0)
+ /* Currently ensured by svn_ra_open4().
+ If failing causes segfault in basic_tests.py 48, "basic auth test" */
+ SVN_ERR_ASSERT((serf_sess->pool != scratch_pool)
+ && apr_pool_is_ancestor(serf_sess->pool, scratch_pool));
+#endif
+
+ err = svn_ra_serf__exchange_capabilities(serf_sess, corrected_url,
+ result_pool, scratch_pool);
/* serf should produce a usable error code instead of APR_EGENERAL */
if (err && err->apr_err == APR_EGENERAL)
@@ -552,20 +608,188 @@ svn_ra_serf__open(svn_ra_session_t *session,
problems in any proxy. */
if ((corrected_url == NULL || *corrected_url == NULL)
&& serf_sess->detect_chunking && !serf_sess->http10)
- SVN_ERR(svn_ra_serf__probe_proxy(serf_sess, pool));
+ SVN_ERR(svn_ra_serf__probe_proxy(serf_sess, scratch_pool));
return SVN_NO_ERROR;
}
-/* Implements svn_ra__vtable_t.reparent(). */
+/* Implements svn_ra__vtable_t.dup_session */
static svn_error_t *
+ra_serf_dup_session(svn_ra_session_t *new_session,
+ svn_ra_session_t *old_session,
+ const char *new_session_url,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_serf__session_t *old_sess = old_session->priv;
+ svn_ra_serf__session_t *new_sess;
+ apr_status_t status;
+
+ new_sess = apr_pmemdup(result_pool, old_sess, sizeof(*new_sess));
+
+ new_sess->pool = result_pool;
+
+ if (new_sess->config)
+ SVN_ERR(svn_config_copy_config(&new_sess->config, new_sess->config,
+ result_pool));
+
+ /* max_connections */
+ /* using_ssl */
+ /* using_compression */
+ /* http10 */
+ /* using_chunked_requests */
+ /* detect_chunking */
+
+ if (new_sess->useragent)
+ new_sess->useragent = apr_pstrdup(result_pool, new_sess->useragent);
+
+ if (new_sess->vcc_url)
+ new_sess->vcc_url = apr_pstrdup(result_pool, new_sess->vcc_url);
+
+ new_sess->auth_state = NULL;
+ new_sess->auth_attempts = 0;
+
+ /* Callback functions to get info from WC */
+ /* wc_callbacks */
+ /* wc_callback_baton */
+
+ /* progress_func */
+ /* progress_baton */
+
+ /* cancel_func */
+ /* cancel_baton */
+
+ /* shim_callbacks */
+
+ new_sess->pending_error = NULL;
+
+ /* authn_types */
+
+ /* Keys and values are static */
+ if (new_sess->capabilities)
+ new_sess->capabilities = apr_hash_copy(result_pool, new_sess->capabilities);
+
+ if (new_sess->activity_collection_url)
+ {
+ new_sess->activity_collection_url
+ = apr_pstrdup(result_pool, new_sess->activity_collection_url);
+ }
+
+ /* using_proxy */
+
+ if (new_sess->proxy_username)
+ {
+ new_sess->proxy_username
+ = apr_pstrdup(result_pool, new_sess->proxy_username);
+ }
+
+ if (new_sess->proxy_password)
+ {
+ new_sess->proxy_username
+ = apr_pstrdup(result_pool, new_sess->proxy_password);
+ }
+
+ new_sess->proxy_auth_attempts = 0;
+
+ /* trust_default_ca */
+
+ if (new_sess->ssl_authorities)
+ {
+ new_sess->ssl_authorities = apr_pstrdup(result_pool,
+ new_sess->ssl_authorities);
+ }
+
+ if (new_sess->uuid)
+ new_sess->uuid = apr_pstrdup(result_pool, new_sess->uuid);
+
+ /* timeout */
+ /* supports_deadprop_count */
+
+ if (new_sess->me_resource)
+ new_sess->me_resource = apr_pstrdup(result_pool, new_sess->me_resource);
+ if (new_sess->rev_stub)
+ new_sess->rev_stub = apr_pstrdup(result_pool, new_sess->rev_stub);
+ if (new_sess->txn_stub)
+ new_sess->txn_stub = apr_pstrdup(result_pool, new_sess->txn_stub);
+ if (new_sess->txn_root_stub)
+ new_sess->txn_root_stub = apr_pstrdup(result_pool,
+ new_sess->txn_root_stub);
+ if (new_sess->vtxn_stub)
+ new_sess->vtxn_stub = apr_pstrdup(result_pool, new_sess->vtxn_stub);
+ if (new_sess->vtxn_root_stub)
+ new_sess->vtxn_root_stub = apr_pstrdup(result_pool,
+ new_sess->vtxn_root_stub);
+
+ /* Keys and values are static */
+ if (new_sess->supported_posts)
+ new_sess->supported_posts = apr_hash_copy(result_pool,
+ new_sess->supported_posts);
+
+ /* ### Can we copy this? */
+ SVN_ERR(svn_ra_serf__blncache_create(&new_sess->blncache,
+ new_sess->pool));
+
+ if (new_sess->server_allows_bulk)
+ new_sess->server_allows_bulk = apr_pstrdup(result_pool,
+ new_sess->server_allows_bulk);
+
+ new_sess->repos_root_str = apr_pstrdup(result_pool,
+ new_sess->repos_root_str);
+ SVN_ERR(svn_ra_serf__uri_parse(&new_sess->repos_root,
+ new_sess->repos_root_str,
+ result_pool));
+
+ new_sess->session_url_str = apr_pstrdup(result_pool, new_session_url);
+
+ SVN_ERR(svn_ra_serf__uri_parse(&new_sess->session_url,
+ new_sess->session_url_str,
+ result_pool));
+
+ /* svn_boolean_t supports_inline_props */
+ /* supports_rev_rsrc_replay */
+
+ new_sess->context = serf_context_create(result_pool);
+
+ SVN_ERR(load_config(new_sess, old_sess->config, result_pool));
+
+ new_sess->conns[0] = apr_pcalloc(result_pool,
+ sizeof(*new_sess->conns[0]));
+ new_sess->conns[0]->bkt_alloc =
+ serf_bucket_allocator_create(result_pool, NULL, NULL);
+ new_sess->conns[0]->session = new_sess;
+ new_sess->conns[0]->last_status_code = -1;
+
+ /* go ahead and tell serf about the connection. */
+ status =
+ serf_connection_create2(&new_sess->conns[0]->conn,
+ new_sess->context,
+ new_sess->session_url,
+ svn_ra_serf__conn_setup, new_sess->conns[0],
+ svn_ra_serf__conn_closed, new_sess->conns[0],
+ result_pool);
+ if (status)
+ return svn_ra_serf__wrap_err(status, NULL);
+
+ /* Set the progress callback. */
+ serf_context_set_progress_cb(new_sess->context, svn_ra_serf__progress,
+ new_sess);
+
+ new_sess->num_conns = 1;
+ new_sess->cur_conn = 0;
+
+ new_session->priv = new_sess;
+
+ return SVN_NO_ERROR;
+}
+
+/* Implements svn_ra__vtable_t.reparent(). */
+svn_error_t *
svn_ra_serf__reparent(svn_ra_session_t *ra_session,
const char *url,
apr_pool_t *pool)
{
svn_ra_serf__session_t *session = ra_session->priv;
apr_uri_t new_url;
- apr_status_t status;
/* If it's the URL we already have, wave our hands and do nothing. */
if (strcmp(session->session_url_str, url) == 0)
@@ -576,7 +800,7 @@ svn_ra_serf__reparent(svn_ra_session_t *ra_session,
if (!session->repos_root_str)
{
const char *vcc_url;
- SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool));
+ SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, pool));
}
if (!svn_uri__is_ancestor(session->repos_root_str, url))
@@ -587,25 +811,11 @@ svn_ra_serf__reparent(svn_ra_session_t *ra_session,
"URL '%s'"), url, session->repos_root_str);
}
- status = apr_uri_parse(pool, url, &new_url);
- if (status)
- {
- return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
- _("Illegal repository URL '%s'"), url);
- }
+ SVN_ERR(svn_ra_serf__uri_parse(&new_url, url, pool));
- /* Depending the version of apr-util in use, for root paths url.path
- will be NULL or "", where serf requires "/". */
/* ### Maybe we should use a string buffer for these strings so we
### don't allocate memory in the session on every reparent? */
- if (new_url.path == NULL || new_url.path[0] == '\0')
- {
- session->session_url.path = apr_pstrdup(session->pool, "/");
- }
- else
- {
- session->session_url.path = apr_pstrdup(session->pool, new_url.path);
- }
+ session->session_url.path = apr_pstrdup(session->pool, new_url.path);
session->session_url_str = apr_pstrdup(session->pool, url);
return SVN_NO_ERROR;
@@ -634,20 +844,24 @@ svn_ra_serf__get_latest_revnum(svn_ra_session_t *ra_session,
latest_revnum, session, pool));
}
-/* Implements svn_ra__vtable_t.rev_proplist(). */
+/* Implementation of svn_ra_serf__rev_proplist(). */
static svn_error_t *
-svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session,
- svn_revnum_t rev,
- apr_hash_t **ret_props,
- apr_pool_t *pool)
+serf__rev_proplist(svn_ra_session_t *ra_session,
+ svn_revnum_t rev,
+ const svn_ra_serf__dav_props_t *fetch_props,
+ apr_hash_t **ret_props,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_ra_serf__session_t *session = ra_session->priv;
apr_hash_t *props;
const char *propfind_path;
+ svn_ra_serf__handler_t *handler;
if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
{
- propfind_path = apr_psprintf(pool, "%s/%ld", session->rev_stub, rev);
+ propfind_path = apr_psprintf(scratch_pool, "%s/%ld", session->rev_stub,
+ rev);
/* svn_ra_serf__retrieve_props() wants to added the revision as
a Label to the PROPFIND, which isn't really necessary when
@@ -658,507 +872,79 @@ svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session,
else
{
/* Use the VCC as the propfind target path. */
- SVN_ERR(svn_ra_serf__discover_vcc(&propfind_path, session, NULL, pool));
- }
-
- /* ### fix: fetch hash of *just* the PATH@REV props. no nested hash. */
- SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0],
- propfind_path, rev, "0", all_props,
- pool, pool));
-
- SVN_ERR(svn_ra_serf__select_revprops(ret_props, propfind_path, rev, props,
- pool, pool));
-
- return SVN_NO_ERROR;
-}
-
-/* Implements svn_ra__vtable_t.rev_prop(). */
-static svn_error_t *
-svn_ra_serf__rev_prop(svn_ra_session_t *session,
- svn_revnum_t rev,
- const char *name,
- svn_string_t **value,
- apr_pool_t *pool)
-{
- apr_hash_t *props;
-
- SVN_ERR(svn_ra_serf__rev_proplist(session, rev, &props, pool));
-
- *value = svn_hash_gets(props, name);
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-fetch_path_props(apr_hash_t **props,
- svn_ra_serf__session_t *session,
- const char *session_relpath,
- svn_revnum_t revision,
- const svn_ra_serf__dav_props_t *desired_props,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- const char *url;
-
- url = session->session_url.path;
-
- /* If we have a relative path, append it. */
- if (session_relpath)
- url = svn_path_url_add_component2(url, session_relpath, scratch_pool);
-
- /* If we were given a specific revision, get a URL that refers to that
- specific revision (rather than floating with HEAD). */
- if (SVN_IS_VALID_REVNUM(revision))
- {
- SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */,
- session, NULL /* conn */,
- url, revision,
- scratch_pool, scratch_pool));
- }
-
- /* URL is stable, so we use SVN_INVALID_REVNUM since it is now irrelevant.
- Or we started with SVN_INVALID_REVNUM and URL may be floating. */
- SVN_ERR(svn_ra_serf__fetch_node_props(props, session->conns[0],
- url, SVN_INVALID_REVNUM,
- desired_props,
- result_pool, scratch_pool));
-
- return SVN_NO_ERROR;
-}
-
-/* Implements svn_ra__vtable_t.check_path(). */
-static svn_error_t *
-svn_ra_serf__check_path(svn_ra_session_t *ra_session,
- const char *rel_path,
- svn_revnum_t revision,
- svn_node_kind_t *kind,
- apr_pool_t *pool)
-{
- svn_ra_serf__session_t *session = ra_session->priv;
- apr_hash_t *props;
-
- svn_error_t *err = fetch_path_props(&props, session, rel_path,
- revision, check_path_props,
- pool, pool);
-
- if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
- {
- svn_error_clear(err);
- *kind = svn_node_none;
+ SVN_ERR(svn_ra_serf__discover_vcc(&propfind_path, session,
+ scratch_pool));
}
- else
- {
- /* Any other error, raise to caller. */
- if (err)
- return svn_error_trace(err);
- SVN_ERR(svn_ra_serf__get_resource_type(kind, props));
- }
-
- return SVN_NO_ERROR;
-}
+ props = apr_hash_make(result_pool);
+ SVN_ERR(svn_ra_serf__create_propfind_handler(&handler, session,
+ propfind_path, rev, "0",
+ fetch_props,
+ svn_ra_serf__deliver_svn_props,
+ props,
+ scratch_pool));
+ SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
-struct dirent_walker_baton_t {
- /* Update the fields in this entry. */
- svn_dirent_t *entry;
+ svn_ra_serf__keep_only_regular_props(props, scratch_pool);
- svn_tristate_t *supports_deadprop_count;
-
- /* If allocations are necessary, then use this pool. */
- apr_pool_t *result_pool;
-};
-
-static svn_error_t *
-dirent_walker(void *baton,
- const char *ns,
- const char *name,
- const svn_string_t *val,
- apr_pool_t *scratch_pool)
-{
- struct dirent_walker_baton_t *dwb = baton;
-
- if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0)
- {
- dwb->entry->has_props = TRUE;
- }
- else if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0)
- {
- dwb->entry->has_props = TRUE;
- }
- else if (strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0)
- {
- if(strcmp(name, "deadprop-count") == 0)
- {
- if (*val->data)
- {
- apr_int64_t deadprop_count;
- SVN_ERR(svn_cstring_atoi64(&deadprop_count, val->data));
- dwb->entry->has_props = deadprop_count > 0;
- if (dwb->supports_deadprop_count)
- *dwb->supports_deadprop_count = svn_tristate_true;
- }
- else if (dwb->supports_deadprop_count)
- *dwb->supports_deadprop_count = svn_tristate_false;
- }
- }
- else if (strcmp(ns, "DAV:") == 0)
- {
- if (strcmp(name, SVN_DAV__VERSION_NAME) == 0)
- {
- dwb->entry->created_rev = SVN_STR_TO_REV(val->data);
- }
- else if (strcmp(name, "creator-displayname") == 0)
- {
- dwb->entry->last_author = val->data;
- }
- else if (strcmp(name, SVN_DAV__CREATIONDATE) == 0)
- {
- SVN_ERR(svn_time_from_cstring(&dwb->entry->time,
- val->data,
- dwb->result_pool));
- }
- else if (strcmp(name, "getcontentlength") == 0)
- {
- /* 'getcontentlength' property is empty for directories. */
- if (val->len)
- {
- SVN_ERR(svn_cstring_atoi64(&dwb->entry->size, val->data));
- }
- }
- else if (strcmp(name, "resourcetype") == 0)
- {
- if (strcmp(val->data, "collection") == 0)
- {
- dwb->entry->kind = svn_node_dir;
- }
- else
- {
- dwb->entry->kind = svn_node_file;
- }
- }
- }
+ *ret_props = props;
return SVN_NO_ERROR;
}
-struct path_dirent_visitor_t {
- apr_hash_t *full_paths;
- apr_hash_t *base_paths;
- const char *orig_path;
- svn_tristate_t supports_deadprop_count;
- apr_pool_t *result_pool;
-};
-
-static svn_error_t *
-path_dirent_walker(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)
-{
- struct path_dirent_visitor_t *dirents = baton;
- struct dirent_walker_baton_t dwb;
- svn_dirent_t *entry;
-
- /* Skip our original path. */
- if (strcmp(path, dirents->orig_path) == 0)
- {
- return SVN_NO_ERROR;
- }
-
- entry = apr_hash_get(dirents->full_paths, path, path_len);
-
- if (!entry)
- {
- const char *base_name;
-
- entry = svn_dirent_create(pool);
-
- apr_hash_set(dirents->full_paths, path, path_len, entry);
-
- base_name = svn_path_uri_decode(svn_urlpath__basename(path, pool),
- pool);
-
- svn_hash_sets(dirents->base_paths, base_name, entry);
- }
-
- dwb.entry = entry;
- dwb.supports_deadprop_count = &dirents->supports_deadprop_count;
- dwb.result_pool = dirents->result_pool;
- return svn_error_trace(dirent_walker(&dwb, ns, name, val, pool));
-}
-
-static const svn_ra_serf__dav_props_t *
-get_dirent_props(apr_uint32_t dirent_fields,
- svn_ra_serf__session_t *session,
- apr_pool_t *pool)
-{
- svn_ra_serf__dav_props_t *prop;
- apr_array_header_t *props = apr_array_make
- (pool, 7, sizeof(svn_ra_serf__dav_props_t));
-
- if (session->supports_deadprop_count != svn_tristate_false
- || ! (dirent_fields & SVN_DIRENT_HAS_PROPS))
- {
- if (dirent_fields & SVN_DIRENT_KIND)
- {
- prop = apr_array_push(props);
- prop->namespace = "DAV:";
- prop->name = "resourcetype";
- }
-
- if (dirent_fields & SVN_DIRENT_SIZE)
- {
- prop = apr_array_push(props);
- prop->namespace = "DAV:";
- prop->name = "getcontentlength";
- }
-
- if (dirent_fields & SVN_DIRENT_HAS_PROPS)
- {
- prop = apr_array_push(props);
- prop->namespace = SVN_DAV_PROP_NS_DAV;
- prop->name = "deadprop-count";
- }
-
- if (dirent_fields & SVN_DIRENT_CREATED_REV)
- {
- svn_ra_serf__dav_props_t *p = apr_array_push(props);
- p->namespace = "DAV:";
- p->name = SVN_DAV__VERSION_NAME;
- }
-
- if (dirent_fields & SVN_DIRENT_TIME)
- {
- prop = apr_array_push(props);
- prop->namespace = "DAV:";
- prop->name = SVN_DAV__CREATIONDATE;
- }
-
- if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
- {
- prop = apr_array_push(props);
- prop->namespace = "DAV:";
- prop->name = "creator-displayname";
- }
- }
- else
- {
- /* We found an old subversion server that can't handle
- the deadprop-count property in the way we expect.
-
- The neon behavior is to retrieve all properties in this case */
- prop = apr_array_push(props);
- prop->namespace = "DAV:";
- prop->name = "allprop";
- }
-
- prop = apr_array_push(props);
- prop->namespace = NULL;
- prop->name = NULL;
-
- return (svn_ra_serf__dav_props_t *) props->elts;
-}
-
-/* Implements svn_ra__vtable_t.stat(). */
+/* Implements svn_ra__vtable_t.rev_proplist(). */
static svn_error_t *
-svn_ra_serf__stat(svn_ra_session_t *ra_session,
- const char *rel_path,
- svn_revnum_t revision,
- svn_dirent_t **dirent,
- apr_pool_t *pool)
+svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session,
+ svn_revnum_t rev,
+ apr_hash_t **ret_props,
+ apr_pool_t *result_pool)
{
- svn_ra_serf__session_t *session = ra_session->priv;
- apr_hash_t *props;
+ apr_pool_t *scratch_pool = svn_pool_create(result_pool);
svn_error_t *err;
- struct dirent_walker_baton_t dwb;
- svn_tristate_t deadprop_count = svn_tristate_unknown;
-
- err = fetch_path_props(&props,
- session, rel_path, revision,
- get_dirent_props(SVN_DIRENT_ALL, session, pool),
- pool, pool);
- if (err)
- {
- if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
- {
- svn_error_clear(err);
- *dirent = NULL;
- return SVN_NO_ERROR;
- }
- else
- return svn_error_trace(err);
- }
- dwb.entry = svn_dirent_create(pool);
- dwb.supports_deadprop_count = &deadprop_count;
- dwb.result_pool = pool;
- SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool));
+ err = serf__rev_proplist(ra_session, rev, all_props, ret_props,
+ result_pool, scratch_pool);
- if (deadprop_count == svn_tristate_false
- && session->supports_deadprop_count == svn_tristate_unknown
- && !dwb.entry->has_props)
- {
- /* We have to requery as the server didn't give us the right
- information */
- session->supports_deadprop_count = svn_tristate_false;
-
- SVN_ERR(fetch_path_props(&props,
- session, rel_path, SVN_INVALID_REVNUM,
- get_dirent_props(SVN_DIRENT_ALL, session, pool),
- pool, pool));
-
- SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool));
- }
-
- if (deadprop_count != svn_tristate_unknown)
- session->supports_deadprop_count = deadprop_count;
-
- *dirent = dwb.entry;
-
- return SVN_NO_ERROR;
+ svn_pool_destroy(scratch_pool);
+ return svn_error_trace(err);
}
-/* Reads the 'resourcetype' property from the list PROPS and checks if the
- * resource at PATH@REVISION really is a directory. Returns
- * SVN_ERR_FS_NOT_DIRECTORY if not.
- */
-static svn_error_t *
-resource_is_directory(apr_hash_t *props)
-{
- svn_node_kind_t kind;
-
- SVN_ERR(svn_ra_serf__get_resource_type(&kind, props));
-
- if (kind != svn_node_dir)
- {
- return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
- _("Can't get entries of non-directory"));
- }
-
- return SVN_NO_ERROR;
-}
-/* Implements svn_ra__vtable_t.get_dir(). */
-static svn_error_t *
-svn_ra_serf__get_dir(svn_ra_session_t *ra_session,
- apr_hash_t **dirents,
- svn_revnum_t *fetched_rev,
- apr_hash_t **ret_props,
- const char *rel_path,
- svn_revnum_t revision,
- apr_uint32_t dirent_fields,
- apr_pool_t *pool)
+/* Implements svn_ra__vtable_t.rev_prop(). */
+svn_error_t *
+svn_ra_serf__rev_prop(svn_ra_session_t *session,
+ svn_revnum_t rev,
+ const char *name,
+ svn_string_t **value,
+ apr_pool_t *result_pool)
{
- svn_ra_serf__session_t *session = ra_session->priv;
- const char *path;
-
- path = session->session_url.path;
-
- /* If we have a relative path, URI encode and append it. */
- if (rel_path)
- {
- path = svn_path_url_add_component2(path, rel_path, pool);
- }
-
- /* If the user specified a peg revision other than HEAD, we have to fetch
- the baseline collection url for that revision. If not, we can use the
- public url. */
- if (SVN_IS_VALID_REVNUM(revision) || fetched_rev)
- {
- SVN_ERR(svn_ra_serf__get_stable_url(&path, fetched_rev,
- session, NULL /* conn */,
- path, revision,
- pool, pool));
- revision = SVN_INVALID_REVNUM;
- }
- /* REVISION is always SVN_INVALID_REVNUM */
- SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision));
-
- /* If we're asked for children, fetch them now. */
- if (dirents)
+ apr_pool_t *scratch_pool = svn_pool_create(result_pool);
+ apr_hash_t *props;
+ svn_ra_serf__dav_props_t specific_props[2];
+ const svn_ra_serf__dav_props_t *fetch_props = all_props;
+
+ /* The DAV propfind doesn't allow property fetches for any property name
+ as there is no defined way to quote values. If we are just fetching a
+ "svn:property" we can safely do this. In other cases we just fetch all
+ revision properties and filter the right one out */
+ if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX)-1) == 0
+ && !strchr(name + sizeof(SVN_PROP_PREFIX)-1, ':'))
{
- struct path_dirent_visitor_t dirent_walk;
- apr_hash_t *props;
- const char *rtype;
-
- /* Always request node kind to check that path is really a
- * directory.
- */
- dirent_fields |= SVN_DIRENT_KIND;
- SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0],
- path, SVN_INVALID_REVNUM, "1",
- get_dirent_props(dirent_fields,
- session, pool),
- pool, pool));
-
- /* Check if the path is really a directory. */
- rtype = svn_ra_serf__get_prop(props, path, "DAV:", "resourcetype");
- if (rtype == NULL || strcmp(rtype, "collection") != 0)
- return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
- _("Can't get entries of non-directory"));
-
- /* We're going to create two hashes to help the walker along.
- * We're going to return the 2nd one back to the caller as it
- * will have the basenames it expects.
- */
- dirent_walk.full_paths = apr_hash_make(pool);
- dirent_walk.base_paths = apr_hash_make(pool);
- dirent_walk.orig_path = svn_urlpath__canonicalize(path, pool);
- dirent_walk.supports_deadprop_count = svn_tristate_unknown;
- dirent_walk.result_pool = pool;
-
- SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM,
- path_dirent_walker, &dirent_walk,
- pool));
-
- if (dirent_walk.supports_deadprop_count == svn_tristate_false
- && session->supports_deadprop_count == svn_tristate_unknown
- && dirent_fields & SVN_DIRENT_HAS_PROPS)
- {
- /* We have to requery as the server didn't give us the right
- information */
- session->supports_deadprop_count = svn_tristate_false;
- SVN_ERR(svn_ra_serf__retrieve_props(&props, session,
- session->conns[0],
- path, SVN_INVALID_REVNUM, "1",
- get_dirent_props(dirent_fields,
- session, pool),
- pool, pool));
-
- apr_hash_clear(dirent_walk.full_paths);
- apr_hash_clear(dirent_walk.base_paths);
-
- SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM,
- path_dirent_walker,
- &dirent_walk, pool));
- }
-
- *dirents = dirent_walk.base_paths;
+ specific_props[0].xmlns = SVN_DAV_PROP_NS_SVN;
+ specific_props[0].name = name + sizeof(SVN_PROP_PREFIX)-1;
+ specific_props[1].xmlns = NULL;
+ specific_props[1].name = NULL;
- if (dirent_walk.supports_deadprop_count != svn_tristate_unknown)
- session->supports_deadprop_count = dirent_walk.supports_deadprop_count;
+ fetch_props = specific_props;
}
- /* If we're asked for the directory properties, fetch them too. */
- if (ret_props)
- {
- apr_hash_t *props;
-
- SVN_ERR(svn_ra_serf__fetch_node_props(&props, session->conns[0],
- path, SVN_INVALID_REVNUM,
- all_props,
- pool, pool));
+ SVN_ERR(serf__rev_proplist(session, rev, fetch_props, &props,
+ result_pool, scratch_pool));
- /* Check if the path is really a directory. */
- SVN_ERR(resource_is_directory(props));
+ *value = svn_hash_gets(props, name);
- /* ### flatten_props() does not copy PROPVALUE, but fetch_node_props()
- ### put them into POOL, so we're okay. */
- SVN_ERR(svn_ra_serf__flatten_props(ret_props, props, pool, pool));
- }
+ svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
@@ -1173,7 +959,7 @@ svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session,
if (!session->repos_root_str)
{
const char *vcc_url;
- SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool));
+ SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, pool));
}
*url = session->repos_root_str;
@@ -1209,7 +995,7 @@ svn_ra_serf__get_uuid(svn_ra_session_t *ra_session,
/* We're not interested in vcc_url and relative_url, but this call also
stores the repository's uuid in the session. */
- SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool));
+ SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, pool));
if (!session->uuid)
{
return svn_error_create(SVN_ERR_RA_DAV_RESPONSE_HEADER_BADNESS, NULL,
@@ -1229,6 +1015,7 @@ static const svn_ra__vtable_t serf_vtable = {
ra_serf_get_description,
ra_serf_get_schemes,
svn_ra_serf__open,
+ ra_serf_dup_session,
svn_ra_serf__reparent,
svn_ra_serf__get_session_url,
svn_ra_serf__get_latest_revnum,
diff --git a/subversion/libsvn_ra_serf/stat.c b/subversion/libsvn_ra_serf/stat.c
new file mode 100644
index 0000000..b6d10c5
--- /dev/null
+++ b/subversion/libsvn_ra_serf/stat.c
@@ -0,0 +1,615 @@
+/*
+ * stat.c : file and directory stat and read functions
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+
+
+#define APR_WANT_STRFUNC
+#include <apr_want.h>
+
+#include <serf.h>
+
+#include "svn_private_config.h"
+#include "svn_pools.h"
+#include "svn_xml.h"
+#include "../libsvn_ra/ra_loader.h"
+#include "svn_config.h"
+#include "svn_dirent_uri.h"
+#include "svn_hash.h"
+#include "svn_path.h"
+#include "svn_props.h"
+#include "svn_time.h"
+#include "svn_version.h"
+
+#include "private/svn_dav_protocol.h"
+#include "private/svn_dep_compat.h"
+#include "private/svn_fspath.h"
+
+#include "ra_serf.h"
+
+
+
+/* Implements svn_ra__vtable_t.check_path(). */
+svn_error_t *
+svn_ra_serf__check_path(svn_ra_session_t *ra_session,
+ const char *relpath,
+ svn_revnum_t revision,
+ svn_node_kind_t *kind,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_serf__session_t *session = ra_session->priv;
+ apr_hash_t *props;
+ svn_error_t *err;
+ const char *url;
+
+ url = session->session_url.path;
+
+ /* If we have a relative path, append it. */
+ if (relpath)
+ url = svn_path_url_add_component2(url, relpath, scratch_pool);
+
+ /* If we were given a specific revision, get a URL that refers to that
+ specific revision (rather than floating with HEAD). */
+ if (SVN_IS_VALID_REVNUM(revision))
+ {
+ SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */,
+ session,
+ url, revision,
+ scratch_pool, scratch_pool));
+ }
+
+ /* URL is stable, so we use SVN_INVALID_REVNUM since it is now irrelevant.
+ Or we started with SVN_INVALID_REVNUM and URL may be floating. */
+ err = svn_ra_serf__fetch_node_props(&props, session,
+ url, SVN_INVALID_REVNUM,
+ check_path_props,
+ scratch_pool, scratch_pool);
+
+ if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ *kind = svn_node_none;
+ }
+ else
+ {
+ apr_hash_t *dav_props;
+ const char *res_type;
+
+ /* Any other error, raise to caller. */
+ SVN_ERR(err);
+
+ 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"));
+ }
+
+ if (strcmp(res_type, "collection") == 0)
+ *kind = svn_node_dir;
+ else
+ *kind = svn_node_file;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Baton for fill_dirent_propfunc() */
+struct fill_dirent_baton_t
+{
+ /* Update the fields in this entry. */
+ svn_dirent_t *entry;
+
+ svn_tristate_t *supports_deadprop_count;
+
+ /* If allocations are necessary, then use this pool. */
+ apr_pool_t *result_pool;
+};
+
+/* Implements svn_ra_serf__prop_func_t */
+static svn_error_t *
+fill_dirent_propfunc(void *baton,
+ const char *path,
+ const char *ns,
+ const char *name,
+ const svn_string_t *val,
+ apr_pool_t *scratch_pool)
+{
+ struct fill_dirent_baton_t *fdb = baton;
+
+ if (strcmp(ns, "DAV:") == 0)
+ {
+ if (strcmp(name, SVN_DAV__VERSION_NAME) == 0)
+ {
+ apr_int64_t rev;
+ SVN_ERR(svn_cstring_atoi64(&rev, val->data));
+
+ fdb->entry->created_rev = (svn_revnum_t)rev;
+ }
+ else if (strcmp(name, "creator-displayname") == 0)
+ {
+ fdb->entry->last_author = apr_pstrdup(fdb->result_pool, val->data);
+ }
+ else if (strcmp(name, SVN_DAV__CREATIONDATE) == 0)
+ {
+ SVN_ERR(svn_time_from_cstring(&fdb->entry->time,
+ val->data,
+ fdb->result_pool));
+ }
+ else if (strcmp(name, "getcontentlength") == 0)
+ {
+ /* 'getcontentlength' property is empty for directories. */
+ if (val->len)
+ {
+ SVN_ERR(svn_cstring_atoi64(&fdb->entry->size, val->data));
+ }
+ }
+ else if (strcmp(name, "resourcetype") == 0)
+ {
+ if (strcmp(val->data, "collection") == 0)
+ {
+ fdb->entry->kind = svn_node_dir;
+ }
+ else
+ {
+ fdb->entry->kind = svn_node_file;
+ }
+ }
+ }
+ else if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0)
+ {
+ fdb->entry->has_props = TRUE;
+ }
+ else if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0)
+ {
+ fdb->entry->has_props = TRUE;
+ }
+ else if (strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0)
+ {
+ if(strcmp(name, "deadprop-count") == 0)
+ {
+ if (*val->data)
+ {
+ apr_int64_t deadprop_count;
+ SVN_ERR(svn_cstring_atoi64(&deadprop_count, val->data));
+ fdb->entry->has_props = deadprop_count > 0;
+ if (fdb->supports_deadprop_count)
+ *fdb->supports_deadprop_count = svn_tristate_true;
+ }
+ else if (fdb->supports_deadprop_count)
+ *fdb->supports_deadprop_count = svn_tristate_false;
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static const svn_ra_serf__dav_props_t *
+get_dirent_props(apr_uint32_t dirent_fields,
+ svn_ra_serf__session_t *session,
+ apr_pool_t *pool)
+{
+ svn_ra_serf__dav_props_t *prop;
+ apr_array_header_t *props = apr_array_make
+ (pool, 7, sizeof(svn_ra_serf__dav_props_t));
+
+ if (session->supports_deadprop_count != svn_tristate_false
+ || ! (dirent_fields & SVN_DIRENT_HAS_PROPS))
+ {
+ if (dirent_fields & SVN_DIRENT_KIND)
+ {
+ prop = apr_array_push(props);
+ prop->xmlns = "DAV:";
+ prop->name = "resourcetype";
+ }
+
+ if (dirent_fields & SVN_DIRENT_SIZE)
+ {
+ prop = apr_array_push(props);
+ prop->xmlns = "DAV:";
+ prop->name = "getcontentlength";
+ }
+
+ if (dirent_fields & SVN_DIRENT_HAS_PROPS)
+ {
+ prop = apr_array_push(props);
+ prop->xmlns = SVN_DAV_PROP_NS_DAV;
+ prop->name = "deadprop-count";
+ }
+
+ if (dirent_fields & SVN_DIRENT_CREATED_REV)
+ {
+ svn_ra_serf__dav_props_t *p = apr_array_push(props);
+ p->xmlns = "DAV:";
+ p->name = SVN_DAV__VERSION_NAME;
+ }
+
+ if (dirent_fields & SVN_DIRENT_TIME)
+ {
+ prop = apr_array_push(props);
+ prop->xmlns = "DAV:";
+ prop->name = SVN_DAV__CREATIONDATE;
+ }
+
+ if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
+ {
+ prop = apr_array_push(props);
+ prop->xmlns = "DAV:";
+ prop->name = "creator-displayname";
+ }
+ }
+ else
+ {
+ /* We found an old subversion server that can't handle
+ the deadprop-count property in the way we expect.
+
+ The neon behavior is to retrieve all properties in this case */
+ prop = apr_array_push(props);
+ prop->xmlns = "DAV:";
+ prop->name = "allprop";
+ }
+
+ prop = apr_array_push(props);
+ prop->xmlns = NULL;
+ prop->name = NULL;
+
+ return (svn_ra_serf__dav_props_t *) props->elts;
+}
+
+/* Implements svn_ra__vtable_t.stat(). */
+svn_error_t *
+svn_ra_serf__stat(svn_ra_session_t *ra_session,
+ const char *relpath,
+ svn_revnum_t revision,
+ svn_dirent_t **dirent,
+ apr_pool_t *pool)
+{
+ svn_ra_serf__session_t *session = ra_session->priv;
+ svn_error_t *err;
+ struct fill_dirent_baton_t fdb;
+ svn_tristate_t deadprop_count = svn_tristate_unknown;
+ svn_ra_serf__handler_t *handler;
+ const char *url;
+
+ url = session->session_url.path;
+
+ /* If we have a relative path, append it. */
+ if (relpath)
+ url = svn_path_url_add_component2(url, relpath, pool);
+
+ /* If we were given a specific revision, get a URL that refers to that
+ specific revision (rather than floating with HEAD). */
+ if (SVN_IS_VALID_REVNUM(revision))
+ {
+ SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */,
+ session,
+ url, revision,
+ pool, pool));
+ }
+
+ fdb.entry = svn_dirent_create(pool);
+ fdb.supports_deadprop_count = &deadprop_count;
+ fdb.result_pool = pool;
+
+ SVN_ERR(svn_ra_serf__create_propfind_handler(&handler, session, url,
+ SVN_INVALID_REVNUM, "0",
+ get_dirent_props(SVN_DIRENT_ALL,
+ session,
+ pool),
+ fill_dirent_propfunc, &fdb, pool));
+
+ err = svn_ra_serf__context_run_one(handler, pool);
+
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ *dirent = NULL;
+ return SVN_NO_ERROR;
+ }
+ else
+ return svn_error_trace(err);
+ }
+
+ if (deadprop_count == svn_tristate_false
+ && session->supports_deadprop_count == svn_tristate_unknown
+ && !fdb.entry->has_props)
+ {
+ /* We have to requery as the server didn't give us the right
+ information */
+ session->supports_deadprop_count = svn_tristate_false;
+
+ /* Run the same handler again */
+ SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
+ }
+
+ if (deadprop_count != svn_tristate_unknown)
+ session->supports_deadprop_count = deadprop_count;
+
+ *dirent = fdb.entry;
+
+ return SVN_NO_ERROR;
+}
+
+/* Baton for get_dir_dirents_cb and get_dir_props_cb */
+struct get_dir_baton_t
+{
+ apr_pool_t *result_pool;
+ apr_hash_t *dirents;
+ apr_hash_t *ret_props;
+ svn_boolean_t is_directory;
+ svn_tristate_t supports_deadprop_count;
+ const char *path;
+};
+
+/* Implements svn_ra_serf__prop_func_t */
+static svn_error_t *
+get_dir_dirents_cb(void *baton,
+ const char *path,
+ const char *ns,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *scratch_pool)
+{
+ struct get_dir_baton_t *db = baton;
+ const char *relpath;
+
+ relpath = svn_fspath__skip_ancestor(db->path, path);
+
+ if (relpath && relpath[0] != '\0')
+ {
+ struct fill_dirent_baton_t fdb;
+
+ relpath = svn_path_uri_decode(relpath, scratch_pool);
+ fdb.entry = svn_hash_gets(db->dirents, relpath);
+
+ if (!fdb.entry)
+ {
+ fdb.entry = svn_dirent_create(db->result_pool);
+ svn_hash_sets(db->dirents,
+ apr_pstrdup(db->result_pool, relpath),
+ fdb.entry);
+ }
+
+ fdb.result_pool = db->result_pool;
+ fdb.supports_deadprop_count = &db->supports_deadprop_count;
+ SVN_ERR(fill_dirent_propfunc(&fdb, path, ns, name, value, scratch_pool));
+ }
+ else if (relpath && !db->is_directory)
+ {
+ if (strcmp(ns, "DAV:") == 0 && strcmp(name, "resourcetype") == 0)
+ {
+ if (strcmp(value->data, "collection") != 0)
+ {
+ /* Tell a lie to exit early */
+ return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
+ _("Can't get properties of non-directory"));
+ }
+ else
+ db->is_directory = TRUE;
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Implements svn_ra_serf__prop_func */
+static svn_error_t *
+get_dir_props_cb(void *baton,
+ const char *path,
+ const char *ns,
+ const char *name,
+ const svn_string_t *value,
+ apr_pool_t *scratch_pool)
+{
+ struct get_dir_baton_t *db = baton;
+ const char *propname;
+
+ propname = svn_ra_serf__svnname_from_wirename(ns, name, db->result_pool);
+ if (propname)
+ {
+ svn_hash_sets(db->ret_props, propname,
+ svn_string_dup(value, db->result_pool));
+ return SVN_NO_ERROR;
+ }
+
+ if (!db->is_directory)
+ {
+ if (strcmp(ns, "DAV:") == 0 && strcmp(name, "resourcetype") == 0)
+ {
+ if (strcmp(value->data, "collection") != 0)
+ {
+ /* Tell a lie to exit early */
+ return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
+ _("Can't get properties of non-directory"));
+ }
+ else
+ db->is_directory = TRUE;
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Implements svn_ra__vtable_t.get_dir(). */
+svn_error_t *
+svn_ra_serf__get_dir(svn_ra_session_t *ra_session,
+ apr_hash_t **dirents,
+ svn_revnum_t *fetched_rev,
+ apr_hash_t **ret_props,
+ const char *rel_path,
+ svn_revnum_t revision,
+ apr_uint32_t dirent_fields,
+ apr_pool_t *result_pool)
+{
+ svn_ra_serf__session_t *session = ra_session->priv;
+ apr_pool_t *scratch_pool = svn_pool_create(result_pool);
+ svn_ra_serf__handler_t *dirent_handler = NULL;
+ svn_ra_serf__handler_t *props_handler = NULL;
+ const char *path;
+ struct get_dir_baton_t gdb;
+ svn_error_t *err = SVN_NO_ERROR;
+
+ gdb.result_pool = result_pool;
+ gdb.is_directory = FALSE;
+ gdb.supports_deadprop_count = svn_tristate_unknown;
+
+ path = session->session_url.path;
+
+ /* If we have a relative path, URI encode and append it. */
+ if (rel_path)
+ {
+ path = svn_path_url_add_component2(path, rel_path, scratch_pool);
+ }
+
+ /* If the user specified a peg revision other than HEAD, we have to fetch
+ the baseline collection url for that revision. If not, we can use the
+ public url. */
+ if (SVN_IS_VALID_REVNUM(revision) || fetched_rev)
+ {
+ SVN_ERR(svn_ra_serf__get_stable_url(&path, fetched_rev,
+ session,
+ path, revision,
+ scratch_pool, scratch_pool));
+ revision = SVN_INVALID_REVNUM;
+ }
+ /* REVISION is always SVN_INVALID_REVNUM */
+ SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision));
+
+ gdb.path = path;
+
+ /* If we're asked for children, fetch them now. */
+ if (dirents)
+ {
+ /* Always request node kind to check that path is really a
+ * directory. */
+ if (!ret_props)
+ dirent_fields |= SVN_DIRENT_KIND;
+
+ gdb.dirents = apr_hash_make(result_pool);
+
+ SVN_ERR(svn_ra_serf__create_propfind_handler(
+ &dirent_handler, session,
+ path, SVN_INVALID_REVNUM, "1",
+ get_dirent_props(dirent_fields,
+ session,
+ scratch_pool),
+ get_dir_dirents_cb, &gdb,
+ scratch_pool));
+
+ svn_ra_serf__request_create(dirent_handler);
+ }
+ else
+ gdb.dirents = NULL;
+
+ if (ret_props)
+ {
+ gdb.ret_props = apr_hash_make(result_pool);
+ SVN_ERR(svn_ra_serf__create_propfind_handler(
+ &props_handler, session,
+ path, SVN_INVALID_REVNUM, "0",
+ all_props,
+ get_dir_props_cb, &gdb,
+ scratch_pool));
+
+ svn_ra_serf__request_create(props_handler);
+ }
+ else
+ gdb.ret_props = NULL;
+
+ if (dirent_handler)
+ {
+ err = svn_error_trace(
+ svn_ra_serf__context_run_wait(&dirent_handler->done,
+ session,
+ scratch_pool));
+
+ if (err)
+ {
+ svn_pool_clear(scratch_pool); /* Unregisters outstanding requests */
+ return err;
+ }
+
+ if (gdb.supports_deadprop_count == svn_tristate_false
+ && session->supports_deadprop_count == svn_tristate_unknown
+ && dirent_fields & SVN_DIRENT_HAS_PROPS)
+ {
+ /* We have to requery as the server didn't give us the right
+ information */
+ session->supports_deadprop_count = svn_tristate_false;
+
+ apr_hash_clear(gdb.dirents);
+
+ SVN_ERR(svn_ra_serf__create_propfind_handler(
+ &dirent_handler, session,
+ path, SVN_INVALID_REVNUM, "1",
+ get_dirent_props(dirent_fields,
+ session,
+ scratch_pool),
+ get_dir_dirents_cb, &gdb,
+ scratch_pool));
+
+ svn_ra_serf__request_create(dirent_handler);
+ }
+ }
+
+ if (props_handler)
+ {
+ err = svn_error_trace(
+ svn_ra_serf__context_run_wait(&props_handler->done,
+ session,
+ scratch_pool));
+ }
+
+ /* And dirent again for the case when we had to send the request again */
+ if (! err && dirent_handler)
+ {
+ err = svn_error_trace(
+ svn_ra_serf__context_run_wait(&dirent_handler->done,
+ session,
+ scratch_pool));
+ }
+
+ if (!err && gdb.supports_deadprop_count != svn_tristate_unknown)
+ session->supports_deadprop_count = gdb.supports_deadprop_count;
+
+ svn_pool_destroy(scratch_pool); /* Unregisters outstanding requests */
+
+ SVN_ERR(err);
+
+ if (!gdb.is_directory)
+ return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
+ _("Can't get entries of non-directory"));
+
+ if (ret_props)
+ *ret_props = gdb.ret_props;
+
+ if (dirents)
+ *dirents = gdb.dirents;
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/libsvn_ra_serf/update.c b/subversion/libsvn_ra_serf/update.c
index 88488ff..8313af0 100644
--- a/subversion/libsvn_ra_serf/update.c
+++ b/subversion/libsvn_ra_serf/update.c
@@ -49,6 +49,7 @@
#include "ra_serf.h"
#include "../libsvn_ra/ra_loader.h"
+
/*
* This enum represents the current state of our XML parsing for a REPORT.
@@ -63,23 +64,180 @@
* the tag is 'closed', the pool will be reused.
*/
typedef enum report_state_e {
- NONE = 0,
- INITIAL = 0,
- UPDATE_REPORT,
- TARGET_REVISION,
- OPEN_DIR,
- ADD_DIR,
- ABSENT_DIR,
- OPEN_FILE,
- ADD_FILE,
- ABSENT_FILE,
- PROP,
- IGNORE_PROP_NAME,
- NEED_PROP_NAME,
- TXDELTA
+ INITIAL = XML_STATE_INITIAL /* = 0 */,
+ UPDATE_REPORT,
+ TARGET_REVISION,
+
+ OPEN_DIR,
+ ADD_DIR,
+
+ OPEN_FILE,
+ ADD_FILE,
+
+ DELETE_ENTRY,
+ ABSENT_DIR,
+ ABSENT_FILE,
+
+ SET_PROP,
+ REMOVE_PROP,
+
+ PROP,
+
+ FETCH_FILE,
+ FETCH_PROPS,
+ TXDELTA,
+
+ CHECKED_IN,
+ CHECKED_IN_HREF,
+
+ MD5_CHECKSUM,
+
+ VERSION_NAME,
+ CREATIONDATE,
+ CREATOR_DISPLAYNAME
} report_state_e;
+#define D_ "DAV:"
+#define S_ SVN_XML_NAMESPACE
+#define V_ SVN_DAV_PROP_NS_DAV
+static const svn_ra_serf__xml_transition_t update_ttable[] = {
+ { INITIAL, S_, "update-report", UPDATE_REPORT,
+ FALSE, { "?inline-props", "?send-all", NULL }, TRUE },
+
+ { UPDATE_REPORT, S_, "target-revision", TARGET_REVISION,
+ FALSE, { "rev", NULL }, TRUE },
+
+ { UPDATE_REPORT, S_, "open-directory", OPEN_DIR,
+ FALSE, { "rev", NULL }, TRUE },
+
+ { OPEN_DIR, S_, "open-directory", OPEN_DIR,
+ FALSE, { "rev", "name", NULL }, TRUE },
+
+ { ADD_DIR, S_, "open-directory", OPEN_DIR,
+ FALSE, { "rev", "name", NULL }, TRUE },
+
+ { OPEN_DIR, S_, "add-directory", ADD_DIR,
+ FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", /*"?bc-url",*/
+ NULL }, TRUE },
+
+ { ADD_DIR, S_, "add-directory", ADD_DIR,
+ FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", /*"?bc-url",*/
+ NULL }, TRUE },
+
+ { OPEN_DIR, S_, "open-file", OPEN_FILE,
+ FALSE, { "rev", "name", NULL }, TRUE },
+
+ { ADD_DIR, S_, "open-file", OPEN_FILE,
+ FALSE, { "rev", "name", NULL }, TRUE },
+
+ { OPEN_DIR, S_, "add-file", ADD_FILE,
+ FALSE, { "name", "?copyfrom-path", "?copyfrom-rev",
+ "?sha1-checksum", NULL }, TRUE },
+
+ { ADD_DIR, S_, "add-file", ADD_FILE,
+ FALSE, { "name", "?copyfrom-path", "?copyfrom-rev",
+ "?sha1-checksum", NULL }, TRUE },
+
+ { OPEN_DIR, S_, "delete-entry", DELETE_ENTRY,
+ FALSE, { "?rev", "name", NULL }, TRUE },
+
+ { ADD_DIR, S_, "delete-entry", DELETE_ENTRY,
+ FALSE, { "?rev", "name", NULL }, TRUE },
+
+ { OPEN_DIR, S_, "absent-directory", ABSENT_DIR,
+ FALSE, { "name", NULL }, TRUE },
+
+ { ADD_DIR, S_, "absent-directory", ABSENT_DIR,
+ FALSE, { "name", NULL }, TRUE },
+
+ { OPEN_DIR, S_, "absent-file", ABSENT_FILE,
+ FALSE, { "name", NULL }, TRUE },
+
+ { ADD_DIR, S_, "absent-file", ABSENT_FILE,
+ FALSE, { "name", NULL }, TRUE },
+
+
+ { OPEN_DIR, D_, "checked-in", CHECKED_IN,
+ FALSE, { NULL }, FALSE },
+
+ { ADD_DIR, D_, "checked-in", CHECKED_IN,
+ FALSE, { NULL }, FALSE },
+
+ { OPEN_FILE, D_, "checked-in", CHECKED_IN,
+ FALSE, { NULL }, FALSE },
+
+ { ADD_FILE, D_, "checked-in", CHECKED_IN,
+ FALSE, { NULL }, FALSE },
+
+
+ { OPEN_DIR, S_, "set-prop", SET_PROP,
+ TRUE, { "name", "?encoding", NULL }, TRUE },
+
+ { ADD_DIR, S_, "set-prop", SET_PROP,
+ TRUE, { "name", "?encoding", NULL }, TRUE },
+
+ { OPEN_FILE, S_, "set-prop", SET_PROP,
+ TRUE, { "name", "?encoding", NULL }, TRUE },
+
+ { ADD_FILE, S_, "set-prop", SET_PROP,
+ TRUE, { "name", "?encoding", NULL }, TRUE },
+
+
+ { OPEN_DIR, S_, "remove-prop", REMOVE_PROP,
+ TRUE, { "name", NULL }, TRUE },
+
+ { ADD_DIR, S_, "remove-prop", REMOVE_PROP,
+ TRUE, { "name", NULL }, TRUE },
+
+ { OPEN_FILE, S_, "remove-prop", REMOVE_PROP,
+ TRUE, { "name", NULL }, TRUE },
+
+ { ADD_FILE, S_, "remove-prop", REMOVE_PROP,
+ TRUE, { "name", NULL }, TRUE },
+
+ { OPEN_FILE, S_, "prop", PROP,
+ FALSE, { NULL }, FALSE },
+ { OPEN_DIR, S_, "prop", PROP,
+ FALSE, { NULL }, FALSE },
+ { ADD_FILE, S_, "prop", PROP,
+ FALSE, { NULL }, FALSE },
+ { ADD_DIR, S_, "prop", PROP,
+ FALSE, { NULL }, FALSE },
+
+ { OPEN_FILE, S_, "txdelta", TXDELTA,
+ FALSE, { "?base-checksum" }, TRUE },
+
+ { ADD_FILE, S_, "txdelta", TXDELTA,
+ FALSE, { "?base-checksum" }, TRUE },
+
+ { OPEN_FILE, S_, "fetch-file", FETCH_FILE,
+ FALSE, { "?base-checksum", "?sha1-checksum", NULL }, TRUE},
+
+ { ADD_FILE, S_, "fetch-file", FETCH_FILE,
+ FALSE, { "?base-checksum", "?sha1-checksum", NULL }, TRUE },
+
+ { CHECKED_IN, D_, "href", CHECKED_IN_HREF,
+ TRUE, { NULL }, TRUE },
+
+ { PROP, V_, "md5-checksum", MD5_CHECKSUM,
+ TRUE, { NULL }, TRUE },
+
+ /* These are only reported for <= 1.6.x mod_dav_svn */
+ { OPEN_DIR, S_, "fetch-props", FETCH_PROPS,
+ FALSE, { NULL }, FALSE },
+ { OPEN_FILE, S_, "fetch-props", FETCH_PROPS,
+ FALSE, { NULL }, FALSE },
+
+ { PROP, D_, "version-name", VERSION_NAME,
+ TRUE, { NULL }, TRUE },
+ { PROP, D_, "creationdate", CREATIONDATE,
+ TRUE, { NULL }, TRUE },
+ { PROP, D_, "creator-displayname", CREATOR_DISPLAYNAME,
+ TRUE, { NULL }, TRUE },
+ { 0 }
+};
+
/* While we process the REPORT response, we will queue up GET and PROPFIND
requests. For a very large checkout, it is very easy to queue requests
faster than they are resolved. Thus, we need to pause the XML processing
@@ -97,175 +255,120 @@ typedef enum report_state_e {
#define REQUEST_COUNT_TO_PAUSE 50
#define REQUEST_COUNT_TO_RESUME 40
+#define SPILLBUF_BLOCKSIZE 4096
+#define SPILLBUF_MAXBUFFSIZE 131072
+
+#define PARSE_CHUNK_SIZE 8000 /* Copied from xml.c ### Needs tuning */
/* Forward-declare our report context. */
typedef struct report_context_t report_context_t;
-
+typedef struct body_create_baton_t body_create_baton_t;
/*
* This structure represents the information for a directory.
*/
-typedef struct report_dir_t
+typedef struct dir_baton_t
{
- /* Our parent directory.
- *
- * This value is NULL when we are the root.
- */
- struct report_dir_t *parent_dir;
+ struct dir_baton_t *parent_dir; /* NULL when root */
- apr_pool_t *pool;
+ apr_pool_t *pool; /* Subpool for this directory */
/* Pointer back to our original report context. */
- report_context_t *report_context;
-
- /* Our name sans any parents. */
- const char *base_name;
+ report_context_t *ctx;
- /* the expanded directory name (including all parent names) */
- const char *name;
+ const char *relpath; /* session relative path */
+ const char *base_name; /* Name of item "" for root */
/* the canonical url for this directory after updating. (received) */
const char *url;
- /* The original repos_relpath of this url (from the working copy)
- or NULL if the repos_relpath can be calculated from the edit root. */
+ /* The original repos_relpath of this url (via the reporter)
+ directly, or via an ancestor. */
const char *repos_relpath;
- /* Our base revision - SVN_INVALID_REVNUM if we're adding this dir. */
- svn_revnum_t base_rev;
+ svn_revnum_t base_rev; /* base revision or NULL for Add */
+
+ const char *copyfrom_path; /* NULL for open */
+ svn_revnum_t copyfrom_rev; /* SVN_INVALID_REVNUM for open */
/* controlling dir baton - this is only created in ensure_dir_opened() */
+ svn_boolean_t dir_opened;
void *dir_baton;
- apr_pool_t *dir_baton_pool;
/* How many references to this directory do we still have open? */
apr_size_t ref_count;
- /* Namespace list allocated out of this ->pool. */
- svn_ra_serf__ns_t *ns_list;
-
- /* hashtable for all of the properties (shared within a dir) */
- apr_hash_t *props;
-
- /* hashtable for all to-be-removed properties (shared within a dir) */
- apr_hash_t *removed_props;
-
- /* The propfind request for our current directory */
+ svn_boolean_t fetch_props; /* Use PROPFIND request? */
svn_ra_serf__handler_t *propfind_handler;
+ apr_hash_t *remove_props;
- /* Has the server told us to fetch the dir props? */
- svn_boolean_t fetch_props;
-
- /* Have we closed the directory tag (meaning no more additions)? */
- svn_boolean_t tag_closed;
-
- /* The children of this directory */
- struct report_dir_t *children;
-
- /* The next sibling of this directory */
- struct report_dir_t *sibling;
-} report_dir_t;
+} dir_baton_t;
/*
- * This structure represents the information for a file.
- *
- * A directory may have a report_info_t associated with it as well.
- *
- * This structure is created as we parse the REPORT response and
- * once the element is completed, we create a report_fetch_t structure
- * to give to serf to retrieve this file.
- */
-typedef struct report_info_t
+* This structure represents the information for a file.
+*
+* This structure is created as we parse the REPORT response and
+* once the element is completed, we may create a fetch_ctx_t structure
+* to give to serf to retrieve this file.
+*/
+typedef struct file_baton_t
{
- apr_pool_t *pool;
-
- /* The enclosing directory.
- *
- * If this structure refers to a directory, the dir it points to will be
- * itself.
- */
- report_dir_t *dir;
+ dir_baton_t *parent_dir; /* The parent */
+ apr_pool_t *pool; /* Subpool for this file*/
- /* Our name sans any directory info. */
+ const char *relpath; /* session relative path */
const char *base_name;
- /* the expanded file name (including all parent directory names) */
- const char *name;
-
- /* the canonical url for this file. */
+ /* the canonical url for this directory after updating. (received) */
const char *url;
+ /* The original repos_relpath of this url as reported. */
+ const char *repos_relpath;
+
/* lock token, if we had one to start off with. */
const char *lock_token;
- /* Our base revision - SVN_INVALID_REVNUM if we're adding this file. */
- svn_revnum_t base_rev;
-
- /* our delta base, if present (NULL if we're adding the file) */
- const char *delta_base;
+ svn_revnum_t base_rev; /* SVN_INVALID_REVNUM for Add */
- /* Path of original item if add with history */
- const char *copyfrom_path;
+ const char *copyfrom_path; /* NULL for open */
+ svn_revnum_t copyfrom_rev; /* SVN_INVALID_REVNUM for open */
- /* Revision of original item if add with history */
- svn_revnum_t copyfrom_rev;
+ /* controlling dir baton - this is only created in ensure_file_opened() */
+ svn_boolean_t file_opened;
+ void *file_baton;
- /* The propfind request for our current file (if present) */
+ svn_boolean_t fetch_props; /* Use PROPFIND request? */
svn_ra_serf__handler_t *propfind_handler;
-
- /* Has the server told us to fetch the file props? */
- svn_boolean_t fetch_props;
+ svn_boolean_t found_lock_prop;
+ apr_hash_t *remove_props;
/* Has the server told us to go fetch - only valid if we had it already */
svn_boolean_t fetch_file;
- /* The properties for this file */
- apr_hash_t *props;
+ /* controlling file_baton and textdelta handler */
+ svn_txdelta_window_handler_t txdelta;
+ void *txdelta_baton;
- /* pool passed to update->add_file, etc. */
- apr_pool_t *editor_pool;
+ svn_checksum_t *base_md5_checksum;
+ svn_checksum_t *final_md5_checksum;
+ svn_checksum_t *final_sha1_checksum;
- /* controlling file_baton and textdelta handler */
- void *file_baton;
- const char *base_checksum;
- const char *final_sha1_checksum;
- svn_txdelta_window_handler_t textdelta;
- void *textdelta_baton;
- svn_stream_t *svndiff_decoder;
- svn_stream_t *base64_decoder;
-
- /* Checksum for close_file */
- const char *final_checksum;
-
- /* Stream containing file contents already cached in the working
- copy (which may be used to avoid a GET request for the same). */
- svn_stream_t *cached_contents;
-
- /* temporary property for this file which is currently being parsed
- * It will eventually be stored in our parent directory's property hash.
- */
- const char *prop_ns;
- const char *prop_name;
- svn_stringbuf_t *prop_value;
- const char *prop_encoding;
-} report_info_t;
+ svn_stream_t *txdelta_stream; /* Stream that feeds windows when
+ written to within txdelta*/
+} file_baton_t;
/*
* This structure represents a single request to GET (fetch) a file with
* its associated Serf session/connection.
*/
-typedef struct report_fetch_t {
+typedef struct fetch_ctx_t {
/* The handler representing this particular fetch. */
svn_ra_serf__handler_t *handler;
- /* The session we should use to fetch the file. */
- svn_ra_serf__session_t *sess;
-
- /* The connection we should use to fetch file. */
- svn_ra_serf__connection_t *conn;
+ svn_boolean_t using_compression;
/* Stores the information for the file we want to fetch. */
- report_info_t *info;
+ file_baton_t *file;
/* Have we read our response headers yet? */
svn_boolean_t read_headers;
@@ -279,22 +382,13 @@ typedef struct report_fetch_t {
/* This is the amount of data that we have read so far. */
apr_off_t read_size;
- /* If we're receiving an svndiff, this will be non-NULL. */
- svn_stream_t *delta_stream;
-
/* If we're writing this file to a stream, this will be non-NULL. */
- svn_stream_t *target_stream;
-
- /* Are we done fetching this file? */
- svn_boolean_t done;
-
- /* Discard the rest of the content? */
- svn_boolean_t discard;
+ svn_stream_t *result_stream;
- svn_ra_serf__list_t **done_list;
- svn_ra_serf__list_t done_item;
+ /* The base-rev header */
+ const char *delta_base;
-} report_fetch_t;
+} fetch_ctx_t;
/*
* The master structure for a REPORT request and response.
@@ -303,7 +397,6 @@ struct report_context_t {
apr_pool_t *pool;
svn_ra_serf__session_t *sess;
- svn_ra_serf__connection_t *conn;
/* Source path and destination path */
const char *source;
@@ -315,6 +408,10 @@ struct report_context_t {
/* What is the target revision that we want for this REPORT? */
svn_revnum_t target_rev;
+ /* Where are we (used while parsing) */
+ dir_baton_t *cur_dir;
+ file_baton_t *cur_file;
+
/* Have we been asked to ignore ancestry or textdeltas? */
svn_boolean_t ignore_ancestry;
svn_boolean_t text_deltas;
@@ -332,170 +429,305 @@ struct report_context_t {
/* Path -> const char *repos_relpath mapping */
apr_hash_t *switched_paths;
- /* Boolean indicating whether "" is switched.
- (This indicates that the we are updating a single file) */
- svn_boolean_t root_is_switched;
-
/* Our master update editor and baton. */
- const svn_delta_editor_t *update_editor;
- void *update_baton;
+ const svn_delta_editor_t *editor;
+ void *editor_baton;
/* The file holding request body for the REPORT.
*
* ### todo: It will be better for performance to store small
* request bodies (like 4k) in memory and bigger bodies on disk.
*/
- apr_file_t *body_file;
-
- /* root directory object */
- report_dir_t *root_dir;
+ svn_stream_t *body_template;
+ body_create_baton_t *body;
/* number of pending GET requests */
unsigned int num_active_fetches;
- /* completed fetches (contains report_fetch_t) */
- svn_ra_serf__list_t *done_fetches;
-
/* number of pending PROPFIND requests */
unsigned int num_active_propfinds;
- /* completed PROPFIND requests (contains svn_ra_serf__handler_t) */
- svn_ra_serf__list_t *done_propfinds;
- svn_ra_serf__list_t *done_dir_propfinds;
-
- /* list of outstanding prop changes (contains report_dir_t) */
- svn_ra_serf__list_t *active_dir_propfinds;
-
- /* list of files that only have prop changes (contains report_info_t) */
- svn_ra_serf__list_t *file_propchanges_only;
-
- /* The path to the REPORT request */
- const char *path;
-
/* Are we done parsing the REPORT response? */
svn_boolean_t done;
/* Did we receive all data from the network? */
svn_boolean_t report_received;
- /* Did we get a complete (non-truncated) report? */
- svn_boolean_t report_completed;
-
- /* The XML parser context for the REPORT response. */
- svn_ra_serf__xml_parser_t *parser_ctx;
-
/* Did we close the root directory? */
svn_boolean_t closed_root;
};
+/* Baton for collecting REPORT body. Depending on the size this
+ work is backed by a memory buffer (via serf buckets) or by
+ a file */
+struct body_create_baton_t
+{
+ apr_pool_t *result_pool;
+ apr_size_t total_bytes;
-#ifdef NOT_USED_YET
+ apr_pool_t *scratch_pool;
-#define D_ "DAV:"
-#define S_ SVN_XML_NAMESPACE
-static const svn_ra_serf__xml_transition_t update_ttable[] = {
- { INITIAL, S_, "update-report", UPDATE_REPORT,
- FALSE, { NULL }, FALSE },
+ serf_bucket_alloc_t *alloc;
+ serf_bucket_t *collect_bucket;
- { UPDATE_REPORT, S_, "target-revision", TARGET_REVISION,
- FALSE, { "rev", NULL }, TRUE },
+ const void *all_data;
+ apr_file_t *file;
+};
- { UPDATE_REPORT, S_, "open-directory", OPEN_DIR,
- FALSE, { "rev", NULL }, TRUE },
- { OPEN_DIR, S_, "open-directory", OPEN_DIR,
- FALSE, { "rev", "name", NULL }, TRUE },
+#define MAX_BODY_IN_RAM (256*1024)
- { OPEN_DIR, S_, "add-directory", ADD_DIR,
- FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+/* Fold all previously collected data in a single buffer allocated in
+ RESULT_POOL and clear all intermediate state */
+static const char *
+body_allocate_all(body_create_baton_t *body,
+ apr_pool_t *result_pool)
+{
+ char *buffer = apr_pcalloc(result_pool, body->total_bytes);
+ const char *data;
+ apr_size_t sz;
+ apr_status_t s;
+ apr_size_t remaining = body->total_bytes;
+ char *next = buffer;
- { ADD_DIR, S_, "add-directory", ADD_DIR,
- FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+ while (!(s = serf_bucket_read(body->collect_bucket, remaining, &data, &sz)))
+ {
+ memcpy(next, data, sz);
+ remaining -= sz;
+ next += sz;
- { OPEN_DIR, S_, "open-file", OPEN_FILE,
- FALSE, { "rev", "name", NULL }, TRUE },
+ if (! remaining)
+ break;
+ }
- { OPEN_DIR, S_, "add-file", ADD_FILE,
- FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+ if (!SERF_BUCKET_READ_ERROR(s))
+ {
+ memcpy(next, data, sz);
+ }
- { ADD_DIR, S_, "add-file", ADD_FILE,
- FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+ serf_bucket_destroy(body->collect_bucket);
+ body->collect_bucket = NULL;
- { OPEN_DIR, S_, "delete-entry", OPEN_FILE,
- FALSE, { "?rev", "name", NULL }, TRUE },
+ return (s != APR_EOF) ? NULL : buffer;
+}
- { OPEN_DIR, S_, "absent-directory", ABSENT_DIR,
- FALSE, { "name", NULL }, TRUE },
+/* Noop function. Make serf take care of freeing in error situations */
+static void serf_free_no_error(void *unfreed_baton, void *block) {}
- { ADD_DIR, S_, "absent-directory", ABSENT_DIR,
- FALSE, { "name", NULL }, TRUE },
+/* Stream write function for body creation */
+static svn_error_t *
+body_write_fn(void *baton,
+ const char *data,
+ apr_size_t *len)
+{
+ body_create_baton_t *bcb = baton;
- { OPEN_DIR, S_, "absent-file", ABSENT_FILE,
- FALSE, { "name", NULL }, TRUE },
+ if (!bcb->scratch_pool)
+ bcb->scratch_pool = svn_pool_create(bcb->result_pool);
- { ADD_DIR, S_, "absent-file", ABSENT_FILE,
- FALSE, { "name", NULL }, TRUE },
+ if (bcb->file)
+ {
+ SVN_ERR(svn_io_file_write_full(bcb->file, data, *len, NULL,
+ bcb->scratch_pool));
+ svn_pool_clear(bcb->scratch_pool);
- { 0 }
-};
+ bcb->total_bytes += *len;
+ }
+ else if (*len + bcb->total_bytes > MAX_BODY_IN_RAM)
+ {
+ SVN_ERR(svn_io_open_unique_file3(&bcb->file, NULL, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ bcb->result_pool, bcb->scratch_pool));
+ if (bcb->total_bytes)
+ {
+ const char *all = body_allocate_all(bcb, bcb->scratch_pool);
+ SVN_ERR(svn_io_file_write_full(bcb->file, all, bcb->total_bytes,
+ NULL, bcb->scratch_pool));
+ }
-/* Conforms to svn_ra_serf__xml_opened_t */
-static svn_error_t *
-update_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)
-{
- report_context_t *ctx = baton;
+ SVN_ERR(svn_io_file_write_full(bcb->file, data, *len, NULL,
+ bcb->scratch_pool));
+ bcb->total_bytes += *len;
+ }
+ else
+ {
+ if (!bcb->alloc)
+ bcb->alloc = serf_bucket_allocator_create(bcb->scratch_pool,
+ serf_free_no_error, NULL);
+
+ if (!bcb->collect_bucket)
+ bcb->collect_bucket = serf_bucket_aggregate_create(bcb->alloc);
+
+ serf_bucket_aggregate_append(bcb->collect_bucket,
+ serf_bucket_simple_copy_create(data, *len,
+ bcb->alloc));
+
+ bcb->total_bytes += *len;
+ }
return SVN_NO_ERROR;
}
+/* Stream close function for collecting body */
+static svn_error_t *
+body_done_fn(void *baton)
+{
+ body_create_baton_t *bcb = baton;
+ if (bcb->file)
+ {
+ /* We need to flush the file, make it unbuffered (so that it can be
+ * zero-copied via mmap), and reset the position before attempting
+ * to deliver the file.
+ *
+ * N.B. If we have APR 1.3+, we can unbuffer the file to let us use
+ * mmap and zero-copy the PUT body. However, on older APR versions,
+ * we can't check the buffer status; but serf will fall through and
+ * create a file bucket for us on the buffered handle.
+ */
+ SVN_ERR(svn_io_file_flush(bcb->file, bcb->scratch_pool));
+ apr_file_buffer_set(bcb->file, NULL, 0);
+ }
+ else if (bcb->collect_bucket)
+ bcb->all_data = body_allocate_all(bcb, bcb->result_pool);
+
+ if (bcb->scratch_pool)
+ svn_pool_destroy(bcb->scratch_pool);
+
+ return SVN_NO_ERROR;
+}
-/* Conforms to svn_ra_serf__xml_closed_t */
static svn_error_t *
-update_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)
+create_dir_baton(dir_baton_t **new_dir,
+ report_context_t *ctx,
+ const char *name,
+ apr_pool_t *scratch_pool)
{
- report_context_t *ctx = baton;
+ dir_baton_t *parent = ctx->cur_dir;
+ apr_pool_t *dir_pool;
+ dir_baton_t *dir;
- if (leaving_state == TARGET_REVISION)
+ if (parent)
+ dir_pool = svn_pool_create(parent->pool);
+ else
+ dir_pool = svn_pool_create(ctx->pool);
+
+ dir = apr_pcalloc(dir_pool, sizeof(*dir));
+ dir->pool = dir_pool;
+ dir->ctx = ctx;
+
+ if (parent)
{
- const char *rev = svn_hash_gets(attrs, "rev");
+ dir->parent_dir = parent;
+ parent->ref_count++;
+ }
- SVN_ERR(ctx->update_editor->set_target_revision(ctx->update_baton,
- SVN_STR_TO_REV(rev),
- ctx->sess->pool));
+ dir->relpath = parent ? svn_relpath_join(parent->relpath, name, dir_pool)
+ : apr_pstrdup(dir_pool, name);
+ dir->base_name = svn_relpath_basename(dir->relpath, NULL);
+
+ dir->repos_relpath = svn_hash_gets(ctx->switched_paths, dir->relpath);
+ if (!dir->repos_relpath)
+ {
+ if (parent)
+ dir->repos_relpath = svn_relpath_join(parent->repos_relpath, name,
+ dir_pool);
+ else
+ dir->repos_relpath = svn_uri_skip_ancestor(ctx->sess->repos_root_str,
+ ctx->sess->session_url_str,
+ dir_pool);
}
+ dir->base_rev = SVN_INVALID_REVNUM;
+ dir->copyfrom_rev = SVN_INVALID_REVNUM;
+
+ dir->ref_count = 1;
+
+ ctx->cur_dir = dir;
+
+ *new_dir = dir;
return SVN_NO_ERROR;
}
-
-/* Conforms to svn_ra_serf__xml_cdata_t */
static svn_error_t *
-update_cdata(svn_ra_serf__xml_estate_t *xes,
- void *baton,
- int current_state,
- const char *data,
- apr_size_t len,
- apr_pool_t *scratch_pool)
+create_file_baton(file_baton_t **new_file,
+ report_context_t *ctx,
+ const char *name,
+ apr_pool_t *scratch_pool)
{
- report_context_t *ctx = baton;
+ dir_baton_t *parent = ctx->cur_dir;
+ apr_pool_t *file_pool;
+ file_baton_t *file;
+
+ file_pool = svn_pool_create(parent->pool);
+
+ file = apr_pcalloc(file_pool, sizeof(*file));
+ file->pool = file_pool;
+
+ file->parent_dir = parent;
+ parent->ref_count++;
+
+ file->relpath = svn_relpath_join(parent->relpath, name, file_pool);
+ file->base_name = svn_relpath_basename(file->relpath, NULL);
+
+ file->repos_relpath = svn_hash_gets(ctx->switched_paths, file->relpath);
+ if (!file->repos_relpath)
+ file->repos_relpath = svn_relpath_join(parent->repos_relpath, name,
+ file_pool);
+
+ /* Sane defaults */
+ file->base_rev = SVN_INVALID_REVNUM;
+ file->copyfrom_rev = SVN_INVALID_REVNUM;
+
+ *new_file = file;
+
+ ctx->cur_file = file;
return SVN_NO_ERROR;
}
-#endif /* NOT_USED_YET */
+/** Minimum nr. of outstanding requests needed before a new connection is
+ * opened. */
+#define REQS_PER_CONN 8
+/** This function creates a new connection for this serf session, but only
+ * if the number of NUM_ACTIVE_REQS > REQS_PER_CONN or if there currently is
+ * only one main connection open.
+ */
+static svn_error_t *
+open_connection_if_needed(svn_ra_serf__session_t *sess, int num_active_reqs)
+{
+ /* For each REQS_PER_CONN outstanding requests open a new connection, with
+ * a minimum of 1 extra connection. */
+ if (sess->num_conns == 1 ||
+ ((num_active_reqs / REQS_PER_CONN) > sess->num_conns))
+ {
+ int cur = sess->num_conns;
+ apr_status_t status;
+
+ sess->conns[cur] = apr_pcalloc(sess->pool, sizeof(*sess->conns[cur]));
+ sess->conns[cur]->bkt_alloc = serf_bucket_allocator_create(sess->pool,
+ NULL, NULL);
+ sess->conns[cur]->last_status_code = -1;
+ sess->conns[cur]->session = sess;
+ status = serf_connection_create2(&sess->conns[cur]->conn,
+ sess->context,
+ sess->session_url,
+ svn_ra_serf__conn_setup,
+ sess->conns[cur],
+ svn_ra_serf__conn_closed,
+ sess->conns[cur],
+ sess->pool);
+ if (status)
+ return svn_ra_serf__wrap_err(status, NULL);
+
+ sess->num_conns++;
+ }
+
+ return SVN_NO_ERROR;
+}
/* Returns best connection for fetching files/properties. */
static svn_ra_serf__connection_t *
@@ -520,391 +752,199 @@ get_best_connection(report_context_t *ctx)
if (ctx->report_received && (ctx->sess->max_connections > 2))
first_conn = 0;
- /* Currently, we just cycle connections. In the future we could
- store the number of pending requests on each connection, or
- perform other heuristics, to achieve better connection usage.
- (As an optimization, if there's only one available auxiliary
- connection to use, don't bother doing all the cur_conn math --
- just return that one connection.) */
+ /* If there's only one available auxiliary connection to use, don't bother
+ doing all the cur_conn math -- just return that one connection. */
if (ctx->sess->num_conns - first_conn == 1)
{
conn = ctx->sess->conns[first_conn];
}
else
{
+#if SERF_VERSION_AT_LEAST(1, 4, 0)
+ /* Often one connection is slower than others, e.g. because the server
+ process/thread has to do more work for the particular set of requests.
+ In the worst case, when REQUEST_COUNT_TO_RESUME requests are queued
+ on such a slow connection, ra_serf will completely stop sending
+ requests.
+
+ The method used here selects the connection with the least amount of
+ pending requests, thereby giving more work to lightly loaded server
+ processes.
+ */
+ int i, best_conn = first_conn;
+ unsigned int min = INT_MAX;
+ for (i = first_conn; i < ctx->sess->num_conns; i++)
+ {
+ serf_connection_t *sc = ctx->sess->conns[i]->conn;
+ unsigned int pending = serf_connection_pending_requests(sc);
+ if (pending < min)
+ {
+ min = pending;
+ best_conn = i;
+ }
+ }
+ conn = ctx->sess->conns[best_conn];
+#else
+ /* We don't know how many requests are pending per connection, so just
+ cycle them. */
conn = ctx->sess->conns[ctx->sess->cur_conn];
ctx->sess->cur_conn++;
if (ctx->sess->cur_conn >= ctx->sess->num_conns)
ctx->sess->cur_conn = first_conn;
+#endif
}
return conn;
}
-
-
-/** Report state management helper **/
-
-static report_info_t *
-push_state(svn_ra_serf__xml_parser_t *parser,
- report_context_t *ctx,
- report_state_e state)
-{
- report_info_t *info;
- apr_pool_t *info_parent_pool;
-
- svn_ra_serf__xml_push_state(parser, state);
-
- info = parser->state->private;
-
- /* Our private pool needs to be disjoint from the state pool. */
- if (!info)
- {
- info_parent_pool = ctx->pool;
- }
- else
- {
- info_parent_pool = info->pool;
- }
-
- if (state == OPEN_DIR || state == ADD_DIR)
- {
- report_info_t *new_info;
-
- new_info = apr_pcalloc(info_parent_pool, sizeof(*new_info));
- new_info->pool = svn_pool_create(info_parent_pool);
- new_info->lock_token = NULL;
- new_info->prop_value = svn_stringbuf_create_empty(new_info->pool);
-
- new_info->dir = apr_pcalloc(new_info->pool, sizeof(*new_info->dir));
- new_info->dir->pool = new_info->pool;
-
- /* Create the root property tree. */
- new_info->dir->props = apr_hash_make(new_info->pool);
- new_info->props = new_info->dir->props;
- new_info->dir->removed_props = apr_hash_make(new_info->pool);
-
- new_info->dir->report_context = ctx;
-
- if (info)
- {
- info->dir->ref_count++;
-
- new_info->dir->parent_dir = info->dir;
-
- /* Point our ns_list at our parents to try to reuse it. */
- new_info->dir->ns_list = info->dir->ns_list;
-
- /* Add ourselves to our parent's list */
- new_info->dir->sibling = info->dir->children;
- info->dir->children = new_info->dir;
- }
- else
- {
- /* Allow us to be found later. */
- ctx->root_dir = new_info->dir;
- }
-
- parser->state->private = new_info;
- }
- else if (state == OPEN_FILE || state == ADD_FILE)
- {
- report_info_t *new_info;
-
- new_info = apr_pcalloc(info_parent_pool, sizeof(*new_info));
- new_info->pool = svn_pool_create(info_parent_pool);
- new_info->file_baton = NULL;
- new_info->lock_token = NULL;
- new_info->fetch_file = FALSE;
- new_info->prop_value = svn_stringbuf_create_empty(new_info->pool);
-
- /* Point at our parent's directory state. */
- new_info->dir = info->dir;
- info->dir->ref_count++;
-
- new_info->props = apr_hash_make(new_info->pool);
-
- parser->state->private = new_info;
- }
-
- return parser->state->private;
-}
-
-
-/** Wrappers around our various property walkers **/
-
-static svn_error_t *
-set_file_props(void *baton,
- const char *ns,
- const char *name,
- const svn_string_t *val,
- apr_pool_t *scratch_pool)
-{
- report_info_t *info = baton;
- const svn_delta_editor_t *editor = info->dir->report_context->update_editor;
- const char *prop_name;
-
- prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
- if (prop_name != NULL)
- return svn_error_trace(editor->change_file_prop(info->file_baton,
- prop_name,
- val,
- scratch_pool));
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *
-set_dir_props(void *baton,
- const char *ns,
- const char *name,
- const svn_string_t *val,
- apr_pool_t *scratch_pool)
-{
- report_dir_t *dir = baton;
- const svn_delta_editor_t *editor = dir->report_context->update_editor;
- const char *prop_name;
-
- prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
- if (prop_name != NULL)
- return svn_error_trace(editor->change_dir_prop(dir->dir_baton,
- prop_name,
- val,
- scratch_pool));
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *
-remove_file_props(void *baton,
- const char *ns,
- const char *name,
- const svn_string_t *val,
- apr_pool_t *scratch_pool)
-{
- report_info_t *info = baton;
- const svn_delta_editor_t *editor = info->dir->report_context->update_editor;
- const char *prop_name;
-
- prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
- if (prop_name != NULL)
- return svn_error_trace(editor->change_file_prop(info->file_baton,
- prop_name,
- NULL,
- scratch_pool));
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *
-remove_dir_props(void *baton,
- const char *ns,
- const char *name,
- const svn_string_t *val,
- apr_pool_t *scratch_pool)
-{
- report_dir_t *dir = baton;
- const svn_delta_editor_t *editor = dir->report_context->update_editor;
- const char *prop_name;
-
- prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
- if (prop_name != NULL)
- return svn_error_trace(editor->change_dir_prop(dir->dir_baton,
- prop_name,
- NULL,
- scratch_pool));
- return SVN_NO_ERROR;
-}
-
/** Helpers to open and close directories */
static svn_error_t*
-ensure_dir_opened(report_dir_t *dir)
+ensure_dir_opened(dir_baton_t *dir,
+ apr_pool_t *scratch_pool)
{
- report_context_t *ctx = dir->report_context;
+ report_context_t *ctx = dir->ctx;
- /* if we're already open, return now */
- if (dir->dir_baton)
- {
- return SVN_NO_ERROR;
- }
+ if (dir->dir_opened)
+ return SVN_NO_ERROR;
if (dir->base_name[0] == '\0')
{
- dir->dir_baton_pool = svn_pool_create(dir->pool);
-
if (ctx->destination
&& ctx->sess->wc_callbacks->invalidate_wc_props)
{
SVN_ERR(ctx->sess->wc_callbacks->invalidate_wc_props(
ctx->sess->wc_callback_baton,
ctx->update_target,
- SVN_RA_SERF__WC_CHECKED_IN_URL, dir->pool));
+ SVN_RA_SERF__WC_CHECKED_IN_URL, scratch_pool));
}
- SVN_ERR(ctx->update_editor->open_root(ctx->update_baton, dir->base_rev,
- dir->dir_baton_pool,
- &dir->dir_baton));
+ SVN_ERR(ctx->editor->open_root(ctx->editor_baton, dir->base_rev,
+ dir->pool,
+ &dir->dir_baton));
}
else
{
- SVN_ERR(ensure_dir_opened(dir->parent_dir));
-
- dir->dir_baton_pool = svn_pool_create(dir->parent_dir->dir_baton_pool);
+ SVN_ERR(ensure_dir_opened(dir->parent_dir, scratch_pool));
if (SVN_IS_VALID_REVNUM(dir->base_rev))
{
- SVN_ERR(ctx->update_editor->open_directory(dir->name,
- dir->parent_dir->dir_baton,
- dir->base_rev,
- dir->dir_baton_pool,
- &dir->dir_baton));
+ SVN_ERR(ctx->editor->open_directory(dir->relpath,
+ dir->parent_dir->dir_baton,
+ dir->base_rev,
+ dir->pool,
+ &dir->dir_baton));
}
else
{
- SVN_ERR(ctx->update_editor->add_directory(dir->name,
- dir->parent_dir->dir_baton,
- NULL, SVN_INVALID_REVNUM,
- dir->dir_baton_pool,
- &dir->dir_baton));
+ SVN_ERR(ctx->editor->add_directory(dir->relpath,
+ dir->parent_dir->dir_baton,
+ dir->copyfrom_path,
+ dir->copyfrom_rev,
+ dir->pool,
+ &dir->dir_baton));
}
}
+ dir->dir_opened = TRUE;
+
return SVN_NO_ERROR;
}
static svn_error_t *
-close_dir(report_dir_t *dir)
+maybe_close_dir(dir_baton_t *dir)
{
- report_dir_t *prev;
- report_dir_t *sibling;
-
- /* ### is there a better pool... this is tossed at end-of-func */
- apr_pool_t *scratch_pool = dir->dir_baton_pool;
+ apr_pool_t *scratch_pool = dir->pool;
+ dir_baton_t *parent = dir->parent_dir;
+ report_context_t *ctx = dir->ctx;
- SVN_ERR_ASSERT(! dir->ref_count);
-
- SVN_ERR(svn_ra_serf__walk_all_props(dir->props, dir->base_name,
- dir->base_rev,
- set_dir_props, dir,
- scratch_pool));
-
- SVN_ERR(svn_ra_serf__walk_all_props(dir->removed_props, dir->base_name,
- dir->base_rev, remove_dir_props, dir,
- scratch_pool));
-
- if (dir->fetch_props)
+ if (--dir->ref_count)
{
- SVN_ERR(svn_ra_serf__walk_all_props(dir->props, dir->url,
- dir->report_context->target_rev,
- set_dir_props, dir,
- scratch_pool));
+ return SVN_NO_ERROR;
}
- SVN_ERR(dir->report_context->update_editor->close_directory(
- dir->dir_baton, scratch_pool));
+ SVN_ERR(ensure_dir_opened(dir, dir->pool));
- /* remove us from our parent's children list */
- if (dir->parent_dir)
+ if (dir->remove_props)
{
- prev = NULL;
- sibling = dir->parent_dir->children;
+ apr_hash_index_t *hi;
- while (sibling != dir)
+ for (hi = apr_hash_first(scratch_pool, dir->remove_props);
+ hi;
+ hi = apr_hash_next(hi))
{
- prev = sibling;
- sibling = sibling->sibling;
- if (!sibling)
- SVN_ERR_MALFUNCTION();
- }
-
- if (!prev)
- {
- dir->parent_dir->children = dir->sibling;
- }
- else
- {
- prev->sibling = dir->sibling;
+ SVN_ERR(ctx->editor->change_file_prop(dir->dir_baton,
+ apr_hash_this_key(hi),
+ NULL /* value */,
+ scratch_pool));
}
}
- svn_pool_destroy(dir->dir_baton_pool);
- svn_pool_destroy(dir->pool);
+ SVN_ERR(dir->ctx->editor->close_directory(dir->dir_baton, scratch_pool));
- return SVN_NO_ERROR;
-}
+ svn_pool_destroy(dir->pool /* scratch_pool */);
-static svn_error_t *close_all_dirs(report_dir_t *dir)
-{
- while (dir->children)
- {
- SVN_ERR(close_all_dirs(dir->children));
- dir->ref_count--;
- }
-
- SVN_ERR_ASSERT(! dir->ref_count);
-
- SVN_ERR(ensure_dir_opened(dir));
-
- return close_dir(dir);
+ if (parent)
+ return svn_error_trace(maybe_close_dir(parent));
+ else
+ return SVN_NO_ERROR;
}
-
-/** Routines called when we are fetching a file */
-
-/* This function works around a bug in some older versions of
- * mod_dav_svn in that it will not send remove-prop in the update
- * report when a lock property disappears when send-all is false.
- *
- * Therefore, we'll try to look at our properties and see if there's
- * an active lock. If not, then we'll assume there isn't a lock
- * anymore.
- */
-static void
-check_lock(report_info_t *info)
+static svn_error_t *
+ensure_file_opened(file_baton_t *file,
+ apr_pool_t *scratch_pool)
{
- const char *lock_val;
+ const svn_delta_editor_t *editor = file->parent_dir->ctx->editor;
- lock_val = svn_ra_serf__get_ver_prop(info->props, info->url,
- info->dir->report_context->target_rev,
- "DAV:", "lockdiscovery");
+ if (file->file_opened)
+ return SVN_NO_ERROR;
- if (lock_val)
+ /* Ensure our parent is open. */
+ SVN_ERR(ensure_dir_opened(file->parent_dir, scratch_pool));
+
+ /* Open (or add) the file. */
+ if (SVN_IS_VALID_REVNUM(file->base_rev))
{
- char *new_lock;
- new_lock = apr_pstrdup(info->editor_pool, lock_val);
- apr_collapse_spaces(new_lock, new_lock);
- lock_val = new_lock;
+ SVN_ERR(editor->open_file(file->relpath,
+ file->parent_dir->dir_baton,
+ file->base_rev,
+ file->pool,
+ &file->file_baton));
}
-
- if (!lock_val || lock_val[0] == '\0')
+ else
{
- svn_string_t *str;
+ SVN_ERR(editor->add_file(file->relpath,
+ file->parent_dir->dir_baton,
+ file->copyfrom_path,
+ file->copyfrom_rev,
+ file->pool,
+ &file->file_baton));
+ }
- str = svn_string_ncreate("", 1, info->editor_pool);
+ file->file_opened = TRUE;
- svn_ra_serf__set_ver_prop(info->dir->removed_props, info->base_name,
- info->base_rev, "DAV:", "lock-token",
- str, info->dir->pool);
- }
+ return SVN_NO_ERROR;
}
+
+/** Routines called when we are fetching a file */
+
static svn_error_t *
headers_fetch(serf_bucket_t *headers,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
- report_fetch_t *fetch_ctx = baton;
+ fetch_ctx_t *fetch_ctx = baton;
/* note that we have old VC URL */
- if (SVN_IS_VALID_REVNUM(fetch_ctx->info->base_rev) &&
- fetch_ctx->info->delta_base)
+ if (fetch_ctx->delta_base)
{
serf_bucket_headers_setn(headers, SVN_DAV_DELTA_BASE_HEADER,
- fetch_ctx->info->delta_base);
+ fetch_ctx->delta_base);
serf_bucket_headers_setn(headers, "Accept-Encoding",
"svndiff1;q=0.9,svndiff;q=0.8");
}
- else if (fetch_ctx->sess->using_compression)
+ else if (fetch_ctx->using_compression)
{
serf_bucket_headers_setn(headers, "Accept-Encoding", "gzip");
}
@@ -918,7 +958,7 @@ cancel_fetch(serf_request_t *request,
int status_code,
void *baton)
{
- report_fetch_t *fetch_ctx = baton;
+ fetch_ctx_t *fetch_ctx = baton;
/* Uh-oh. Our connection died on us.
*
@@ -948,30 +988,6 @@ cancel_fetch(serf_request_t *request,
SVN_ERR_MALFUNCTION();
}
-static svn_error_t *
-error_fetch(serf_request_t *request,
- report_fetch_t *fetch_ctx,
- svn_error_t *err)
-{
- fetch_ctx->done = TRUE;
-
- fetch_ctx->done_item.data = fetch_ctx;
- fetch_ctx->done_item.next = *fetch_ctx->done_list;
- *fetch_ctx->done_list = &fetch_ctx->done_item;
-
- /* Discard the rest of this request
- (This makes sure it doesn't error when the request is aborted later) */
- serf_request_set_handler(request,
- svn_ra_serf__response_discard_handler, NULL);
-
- /* Some errors would be handled by serf; make sure they really make
- the update fail by wrapping it in a different error. */
- if (!SERF_BUCKET_READ_ERROR(err->apr_err))
- return svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
-
- return err;
-}
-
/* Wield the editor referenced by INFO to open (or add) the file
file also associated with INFO, setting properties on the file and
calling the editor's apply_textdelta() function on it if necessary
@@ -980,95 +996,92 @@ error_fetch(serf_request_t *request,
Callers will probably want to also see the function that serves
the opposite purpose of this one, close_updated_file(). */
static svn_error_t *
-open_updated_file(report_info_t *info,
- svn_boolean_t force_apply_textdelta,
+open_file_txdelta(file_baton_t *file,
apr_pool_t *scratch_pool)
{
- report_context_t *ctx = info->dir->report_context;
- const svn_delta_editor_t *update_editor = ctx->update_editor;
+ const svn_delta_editor_t *editor = file->parent_dir->ctx->editor;
- /* Ensure our parent is open. */
- SVN_ERR(ensure_dir_opened(info->dir));
- info->editor_pool = svn_pool_create(info->dir->dir_baton_pool);
+ SVN_ERR_ASSERT(file->txdelta == NULL);
- /* Expand our full name now if we haven't done so yet. */
- if (!info->name)
- {
- info->name = svn_relpath_join(info->dir->name, info->base_name,
- info->editor_pool);
- }
-
- /* Open (or add) the file. */
- if (SVN_IS_VALID_REVNUM(info->base_rev))
- {
- SVN_ERR(update_editor->open_file(info->name,
- info->dir->dir_baton,
- info->base_rev,
- info->editor_pool,
- &info->file_baton));
- }
- else
- {
- SVN_ERR(update_editor->add_file(info->name,
- info->dir->dir_baton,
- info->copyfrom_path,
- info->copyfrom_rev,
- info->editor_pool,
- &info->file_baton));
- }
-
- /* Check for lock information. */
- if (info->lock_token)
- check_lock(info);
+ SVN_ERR(ensure_file_opened(file, scratch_pool));
/* Get (maybe) a textdelta window handler for transmitting file
content changes. */
- if (info->fetch_file || force_apply_textdelta)
- {
- SVN_ERR(update_editor->apply_textdelta(info->file_baton,
- info->base_checksum,
- info->editor_pool,
- &info->textdelta,
- &info->textdelta_baton));
- }
+ SVN_ERR(editor->apply_textdelta(file->file_baton,
+ svn_checksum_to_cstring(
+ file->base_md5_checksum,
+ scratch_pool),
+ file->pool,
+ &file->txdelta,
+ &file->txdelta_baton));
return SVN_NO_ERROR;
}
-/* Close the file associated with INFO->file_baton, and cleanup other
- bits of that structure managed by open_updated_file(). */
+/* Close the file, handling loose ends and cleanup */
static svn_error_t *
-close_updated_file(report_info_t *info,
- apr_pool_t *scratch_pool)
+close_file(file_baton_t *file,
+ apr_pool_t *scratch_pool)
{
- report_context_t *ctx = info->dir->report_context;
+ dir_baton_t *parent_dir = file->parent_dir;
+ report_context_t *ctx = parent_dir->ctx;
+
+ SVN_ERR(ensure_file_opened(file, scratch_pool));
/* Set all of the properties we received */
- SVN_ERR(svn_ra_serf__walk_all_props(info->props,
- info->base_name,
- info->base_rev,
- set_file_props, info,
- scratch_pool));
- SVN_ERR(svn_ra_serf__walk_all_props(info->dir->removed_props,
- info->base_name,
- info->base_rev,
- remove_file_props, info,
- scratch_pool));
- if (info->fetch_props)
+ if (file->remove_props)
+ {
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(scratch_pool, file->remove_props);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
+ apr_hash_this_key(hi),
+ NULL /* value */,
+ scratch_pool));
+ }
+ }
+
+ /* Check for lock information. */
+
+ /* This works around a bug in some older versions of mod_dav_svn in that it
+ * will not send remove-prop in the update report when a lock property
+ * disappears when send-all is false.
+
+ ### Given that we only fetch props on additions, is this really necessary?
+ Or is it covering up old local copy bugs where we copied locks to other
+ paths? */
+ if (!ctx->add_props_included
+ && file->lock_token && !file->found_lock_prop
+ && SVN_IS_VALID_REVNUM(file->base_rev) /* file_is_added */)
+ {
+ SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
+ SVN_PROP_ENTRY_LOCK_TOKEN,
+ NULL,
+ scratch_pool));
+ }
+
+ if (file->url)
{
- SVN_ERR(svn_ra_serf__walk_all_props(info->props,
- info->url,
- ctx->target_rev,
- set_file_props, info,
- scratch_pool));
+ SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
+ SVN_RA_SERF__WC_CHECKED_IN_URL,
+ svn_string_create(file->url,
+ scratch_pool),
+ scratch_pool));
}
/* Close the file via the editor. */
- SVN_ERR(info->dir->report_context->update_editor->close_file(
- info->file_baton, info->final_checksum, scratch_pool));
+ SVN_ERR(ctx->editor->close_file(file->file_baton,
+ svn_checksum_to_cstring(
+ file->final_md5_checksum,
+ scratch_pool),
+ scratch_pool));
+
+ svn_pool_destroy(file->pool);
- /* We're done with our editor pool. */
- svn_pool_destroy(info->editor_pool);
+ SVN_ERR(maybe_close_dir(parent_dir)); /* Remove reference */
return SVN_NO_ERROR;
}
@@ -1083,8 +1096,8 @@ handle_fetch(serf_request_t *request,
const char *data;
apr_size_t len;
apr_status_t status;
- report_fetch_t *fetch_ctx = handler_baton;
- svn_error_t *err;
+ fetch_ctx_t *fetch_ctx = handler_baton;
+ file_baton_t *file = fetch_ctx->file;
/* ### new field. make sure we didn't miss some initialization. */
SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
@@ -1093,50 +1106,53 @@ handle_fetch(serf_request_t *request,
{
serf_bucket_t *hdrs;
const char *val;
- report_info_t *info;
+
+ /* If the error code wasn't 200, something went wrong. Don't use the
+ * returned data as its probably an error message. Just bail out instead.
+ */
+ if (fetch_ctx->handler->sline.code != 200)
+ {
+ fetch_ctx->handler->discard_body = TRUE;
+ return SVN_NO_ERROR; /* Will return an error in the DONE handler */
+ }
hdrs = serf_bucket_response_get_headers(response);
val = serf_bucket_headers_get(hdrs, "Content-Type");
- info = fetch_ctx->info;
if (val && svn_cstring_casecmp(val, SVN_SVNDIFF_MIME_TYPE) == 0)
{
- fetch_ctx->delta_stream =
- svn_txdelta_parse_svndiff(info->textdelta,
- info->textdelta_baton,
- TRUE, info->editor_pool);
+ fetch_ctx->result_stream =
+ svn_txdelta_parse_svndiff(file->txdelta,
+ file->txdelta_baton,
+ TRUE, file->pool);
/* Validate the delta base claimed by the server matches
what we asked for! */
val = serf_bucket_headers_get(hdrs, SVN_DAV_DELTA_BASE_HEADER);
- if (val && (strcmp(val, info->delta_base) != 0))
+ if (val && fetch_ctx->delta_base == NULL)
+ {
+ /* We recieved response with delta base header while we didn't
+ requested it -- report it as error. */
+ return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+ _("GET request returned unexpected "
+ "delta base: %s"), val);
+ }
+ else if (val && (strcmp(val, fetch_ctx->delta_base) != 0))
{
- err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
- _("GET request returned unexpected "
- "delta base: %s"), val);
- return error_fetch(request, fetch_ctx, err);
+ return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+ _("GET request returned unexpected "
+ "delta base: %s"), val);
}
}
else
{
- fetch_ctx->delta_stream = NULL;
+ fetch_ctx->result_stream = NULL;
}
fetch_ctx->read_headers = TRUE;
}
- /* If the error code wasn't 200, something went wrong. Don't use the returned
- data as its probably an error message. Just bail out instead. */
- if (fetch_ctx->handler->sline.code != 200)
- {
- err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
- _("GET request failed: %d %s"),
- fetch_ctx->handler->sline.code,
- fetch_ctx->handler->sline.reason);
- return error_fetch(request, fetch_ctx, err);
- }
-
- while (1)
+ while (TRUE)
{
svn_txdelta_window_t delta_window = { 0 };
svn_txdelta_op_t delta_op;
@@ -1163,10 +1179,9 @@ handle_fetch(serf_request_t *request,
}
/* Skip on to the next iteration of this loop. */
- if (APR_STATUS_IS_EAGAIN(status))
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
+ if (status /* includes EAGAIN */)
+ return svn_ra_serf__wrap_err(status, NULL);
+
continue;
}
@@ -1176,17 +1191,12 @@ handle_fetch(serf_request_t *request,
/* Update data and len to just provide the new data. */
skip = len - (fetch_ctx->read_size - fetch_ctx->aborted_read_size);
data += skip;
- len -= skip;
+ len -= (apr_size_t)skip;
}
- if (fetch_ctx->delta_stream)
- {
- err = svn_stream_write(fetch_ctx->delta_stream, data, &len);
- if (err)
- {
- return error_fetch(request, fetch_ctx, err);
- }
- }
+ if (fetch_ctx->result_stream)
+ SVN_ERR(svn_stream_write(fetch_ctx->result_stream, data, &len));
+
/* otherwise, manually construct the text delta window. */
else if (len)
{
@@ -1203,378 +1213,271 @@ handle_fetch(serf_request_t *request,
delta_window.new_data = &window_data;
/* write to the file located in the info. */
- err = fetch_ctx->info->textdelta(&delta_window,
- fetch_ctx->info->textdelta_baton);
- if (err)
- {
- return error_fetch(request, fetch_ctx, err);
- }
+ SVN_ERR(file->txdelta(&delta_window, file->txdelta_baton));
}
if (APR_STATUS_IS_EOF(status))
{
- report_info_t *info = fetch_ctx->info;
-
- if (fetch_ctx->delta_stream)
- err = svn_error_trace(svn_stream_close(fetch_ctx->delta_stream));
+ if (fetch_ctx->result_stream)
+ SVN_ERR(svn_stream_close(fetch_ctx->result_stream));
else
- err = svn_error_trace(info->textdelta(NULL,
- info->textdelta_baton));
- if (err)
- {
- return error_fetch(request, fetch_ctx, err);
- }
-
- err = close_updated_file(info, info->pool);
- if (err)
- {
- return svn_error_trace(error_fetch(request, fetch_ctx, err));
- }
-
- fetch_ctx->done = TRUE;
-
- fetch_ctx->done_item.data = fetch_ctx;
- fetch_ctx->done_item.next = *fetch_ctx->done_list;
- *fetch_ctx->done_list = &fetch_ctx->done_item;
-
- /* We're done with our pool. */
- svn_pool_destroy(info->pool);
-
- if (status)
- return svn_ra_serf__wrap_err(status, NULL);
- }
- if (APR_STATUS_IS_EAGAIN(status))
- {
- return svn_ra_serf__wrap_err(status, NULL);
+ SVN_ERR(file->txdelta(NULL, file->txdelta_baton));
}
+
+ /* Report EOF, EEAGAIN and other special errors to serf */
+ if (status)
+ return svn_ra_serf__wrap_err(status, NULL);
}
- /* not reached */
}
-/* Implements svn_ra_serf__response_handler_t */
-static svn_error_t *
-handle_stream(serf_request_t *request,
- serf_bucket_t *response,
- void *handler_baton,
- apr_pool_t *pool)
-{
- report_fetch_t *fetch_ctx = handler_baton;
- svn_error_t *err;
- apr_status_t status;
-
- /* ### new field. make sure we didn't miss some initialization. */
- SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
+/* --------------------------------------------------------- */
- err = svn_ra_serf__error_on_status(fetch_ctx->handler->sline,
- fetch_ctx->info->name,
- fetch_ctx->handler->location);
- if (err)
- {
- fetch_ctx->handler->done = TRUE;
+/** Wrappers around our various property walkers **/
- err = svn_error_compose_create(
- err,
- svn_ra_serf__handle_discard_body(request, response, NULL, pool));
+/* Implements svn_ra_serf__prop_func */
+static svn_error_t *
+set_file_props(void *baton,
+ const char *path,
+ const char *ns,
+ const char *name,
+ const svn_string_t *val,
+ apr_pool_t *scratch_pool)
+{
+ file_baton_t *file = baton;
+ report_context_t *ctx = file->parent_dir->ctx;
+ const char *prop_name;
- return svn_error_trace(err);
- }
+ prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
- while (1)
+ if (!prop_name)
{
- const char *data;
- apr_size_t len;
-
- status = serf_bucket_read(response, 8000, &data, &len);
- if (SERF_BUCKET_READ_ERROR(status))
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
-
- fetch_ctx->read_size += len;
-
- if (fetch_ctx->aborted_read)
+ /* This works around a bug in some older versions of
+ * mod_dav_svn in that it will not send remove-prop in the update
+ * report when a lock property disappears when send-all is false.
+ *
+ * Therefore, we'll try to look at our properties and see if there's
+ * an active lock. If not, then we'll assume there isn't a lock
+ * anymore.
+ */
+ /* assert(!ctx->add_props_included); // Or we wouldn't be here */
+ if (file->lock_token
+ && !file->found_lock_prop
+ && val
+ && strcmp(ns, "DAV:") == 0
+ && strcmp(name, "lockdiscovery") == 0)
{
- /* We haven't caught up to where we were before. */
- if (fetch_ctx->read_size < fetch_ctx->aborted_read_size)
- {
- /* Eek. What did the file shrink or something? */
- if (APR_STATUS_IS_EOF(status))
- {
- SVN_ERR_MALFUNCTION();
- }
-
- /* Skip on to the next iteration of this loop. */
- if (APR_STATUS_IS_EAGAIN(status))
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
- continue;
- }
+ char *new_lock;
+ new_lock = apr_pstrdup(scratch_pool, val->data);
+ apr_collapse_spaces(new_lock, new_lock);
- /* Woo-hoo. We're back. */
- fetch_ctx->aborted_read = FALSE;
-
- /* Increment data and len by the difference. */
- data += fetch_ctx->read_size - fetch_ctx->aborted_read_size;
- len += fetch_ctx->read_size - fetch_ctx->aborted_read_size;
+ if (new_lock[0] != '\0')
+ file->found_lock_prop = TRUE;
}
- if (len)
- {
- apr_size_t written_len;
-
- written_len = len;
+ return SVN_NO_ERROR;
+ }
- SVN_ERR(svn_stream_write(fetch_ctx->target_stream, data,
- &written_len));
- }
+ SVN_ERR(ensure_file_opened(file, scratch_pool));
- if (APR_STATUS_IS_EOF(status))
- {
- fetch_ctx->done = TRUE;
- }
+ SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
+ prop_name, val,
+ scratch_pool));
- if (status)
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
- }
- /* not reached */
+ return SVN_NO_ERROR;
}
-/* Close the directory represented by DIR -- and any suitable parents
- thereof -- if we are able to do so. This is the case whenever:
-
- - there are no remaining open items within the directory, and
- - the directory's XML close tag has been processed (so we know
- there are no more children to worry about in the future), and
- - either:
- - we aren't fetching properties for this directory, or
- - we've already finished fetching those properties.
-*/
+/* Implements svn_ra_serf__response_done_delegate_t */
static svn_error_t *
-maybe_close_dir_chain(report_dir_t *dir)
+file_props_done(serf_request_t *request,
+ void *baton,
+ apr_pool_t *scratch_pool)
{
- report_dir_t *cur_dir = dir;
+ file_baton_t *file = baton;
+ svn_ra_serf__handler_t *handler = file->propfind_handler;
- SVN_ERR(ensure_dir_opened(cur_dir));
+ if (handler->server_error)
+ return svn_error_trace(svn_ra_serf__server_error_create(handler,
+ scratch_pool));
- while (cur_dir
- && !cur_dir->ref_count
- && cur_dir->tag_closed
- && (!cur_dir->fetch_props || cur_dir->propfind_handler->done))
- {
- report_dir_t *parent = cur_dir->parent_dir;
- report_context_t *report_context = cur_dir->report_context;
- svn_boolean_t propfind_in_done_list = FALSE;
- svn_ra_serf__list_t *done_list;
-
- /* Make sure there are no references to this dir in the
- active_dir_propfinds list. If there are, don't close the
- directory -- which would delete the pool from which the
- relevant active_dir_propfinds list item is allocated -- and
- of course don't crawl upward to check the parents for
- a closure opportunity, either. */
- done_list = report_context->active_dir_propfinds;
- while (done_list)
- {
- if (done_list->data == cur_dir)
- {
- propfind_in_done_list = TRUE;
- break;
- }
- done_list = done_list->next;
- }
- if (propfind_in_done_list)
- break;
+ if (handler->sline.code != 207)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
- SVN_ERR(close_dir(cur_dir));
- if (parent)
- {
- parent->ref_count--;
- }
- else
- {
- report_context->closed_root = TRUE;
- }
- cur_dir = parent;
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Open the file associated with INFO for editing, pass along any
- propchanges we've recorded for it, and then close the file. */
-static svn_error_t *
-handle_propchange_only(report_info_t *info,
- apr_pool_t *scratch_pool)
-{
- SVN_ERR(open_updated_file(info, FALSE, scratch_pool));
- SVN_ERR(close_updated_file(info, scratch_pool));
+ file->parent_dir->ctx->num_active_propfinds--;
- /* We're done with our pool. */
- svn_pool_destroy(info->pool);
+ file->fetch_props = FALSE;
- info->dir->ref_count--;
+ if (file->fetch_file)
+ return SVN_NO_ERROR; /* Still processing file request */
- /* See if the parent directory of this file (and perhaps even
- parents of that) can be closed now. */
- SVN_ERR(maybe_close_dir_chain(info->dir));
+ /* Closing the file will automatically deliver the propfind props.
+ *
+ * Note that closing the directory may dispose the pool containing the
+ * handler, which is only a valid operation in this callback, as only
+ * after this callback our serf plumbing assumes the request is done. */
- return SVN_NO_ERROR;
+ return svn_error_trace(close_file(file, scratch_pool));
}
-/* "Fetch" a file whose contents were made available via the
- get_wc_contents() callback (as opposed to requiring a GET to the
- server), and feed the information through the associated update
- editor. In editor-speak, this will add/open the file, transmit any
- property changes, handle the contents, and then close the file. */
static svn_error_t *
-handle_local_content(report_info_t *info,
- apr_pool_t *scratch_pool)
+file_fetch_done(serf_request_t *request,
+ void *baton,
+ apr_pool_t *scratch_pool)
{
- SVN_ERR(svn_txdelta_send_stream(info->cached_contents, info->textdelta,
- info->textdelta_baton, NULL, scratch_pool));
- SVN_ERR(svn_stream_close(info->cached_contents));
- info->cached_contents = NULL;
- SVN_ERR(close_updated_file(info, scratch_pool));
+ fetch_ctx_t *fetch_ctx = baton;
+ file_baton_t *file = fetch_ctx->file;
+ svn_ra_serf__handler_t *handler = fetch_ctx->handler;
- /* We're done with our pool. */
- svn_pool_destroy(info->pool);
+ if (handler->server_error)
+ return svn_error_trace(svn_ra_serf__server_error_create(handler,
+ scratch_pool));
- info->dir->ref_count--;
+ if (handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
- /* See if the parent directory of this fetched item (and
- perhaps even parents of that) can be closed now. */
- SVN_ERR(maybe_close_dir_chain(info->dir));
+ file->parent_dir->ctx->num_active_fetches--;
- return SVN_NO_ERROR;
-}
+ file->fetch_file = FALSE;
-/* --------------------------------------------------------- */
+ if (file->fetch_props)
+ return SVN_NO_ERROR; /* Still processing PROPFIND request */
+ /* Closing the file will automatically deliver the propfind props.
+ *
+ * Note that closing the directory may dispose the pool containing the
+ * handler, fetch_ctx, etc. which is only a valid operation in this
+ * callback, as only after this callback our serf plumbing assumes the
+ * request is done. */
+ return svn_error_trace(close_file(file, scratch_pool));
+}
+
+/* Initiates additional requests needed for a file when not in "send-all" mode.
+ */
static svn_error_t *
-fetch_file(report_context_t *ctx, report_info_t *info)
+fetch_for_file(file_baton_t *file,
+ apr_pool_t *scratch_pool)
{
+ report_context_t *ctx = file->parent_dir->ctx;
svn_ra_serf__connection_t *conn;
svn_ra_serf__handler_t *handler;
+ /* Open extra connections if we have enough requests to send. */
+ if (ctx->sess->num_conns < ctx->sess->max_connections)
+ SVN_ERR(open_connection_if_needed(ctx->sess, ctx->num_active_fetches +
+ ctx->num_active_propfinds));
+
/* What connection should we go on? */
conn = get_best_connection(ctx);
- /* If needed, create the PROPFIND to retrieve the file's properties. */
- info->propfind_handler = NULL;
- if (info->fetch_props)
- {
- SVN_ERR(svn_ra_serf__deliver_props(&info->propfind_handler, info->props,
- ctx->sess, conn, info->url,
- ctx->target_rev, "0", all_props,
- &ctx->done_propfinds,
- info->dir->pool));
- SVN_ERR_ASSERT(info->propfind_handler);
-
- /* Create a serf request for the PROPFIND. */
- svn_ra_serf__request_create(info->propfind_handler);
-
- ctx->num_active_propfinds++;
- }
+ /* Note that we (still) use conn for both requests.. Should we send
+ them out on different connections? */
- /* If we've been asked to fetch the file or it's an add, do so.
- * Otherwise, handle the case where only the properties changed.
- */
- if (info->fetch_file && ctx->text_deltas)
+ if (file->fetch_file)
{
- svn_stream_t *contents = NULL;
+ SVN_ERR(open_file_txdelta(file, scratch_pool));
- /* Open the file for editing. */
- SVN_ERR(open_updated_file(info, FALSE, info->pool));
-
- if (info->textdelta == svn_delta_noop_window_handler)
+ if (!ctx->text_deltas
+ || file->txdelta == svn_delta_noop_window_handler)
{
- /* There is nobody looking for an actual stream.
-
- Just report an empty stream instead of fetching
- to be ingored data */
- info->cached_contents = svn_stream_empty(info->pool);
+ SVN_ERR(file->txdelta(NULL, file->txdelta_baton));
+ file->fetch_file = FALSE;
}
- else if (ctx->sess->wc_callbacks->get_wc_contents
- && info->final_sha1_checksum)
- {
- svn_error_t *err = NULL;
- svn_checksum_t *checksum = NULL;
-
- /* Parse the optional SHA1 checksum (1.7+) */
- err = svn_checksum_parse_hex(&checksum, svn_checksum_sha1,
- info->final_sha1_checksum,
- info->pool);
- /* Okay so far? Let's try to get a stream on some readily
- available matching content. */
- if (!err && checksum)
- {
- err = ctx->sess->wc_callbacks->get_wc_contents(
- ctx->sess->wc_callback_baton, &contents,
- checksum, info->pool);
+ if (file->fetch_file
+ && file->final_sha1_checksum
+ && ctx->sess->wc_callbacks->get_wc_contents)
+ {
+ svn_error_t *err;
+ svn_stream_t *cached_contents = NULL;
- if (! err)
- info->cached_contents = contents;
- }
+ err = ctx->sess->wc_callbacks->get_wc_contents(
+ ctx->sess->wc_callback_baton,
+ &cached_contents,
+ file->final_sha1_checksum,
+ scratch_pool);
- if (err)
+ if (err || !cached_contents)
+ svn_error_clear(err); /* ### Can we return some/most errors? */
+ else
{
- /* Meh. Maybe we'll care one day why we're in an
- errorful state, but this codepath is optional. */
- svn_error_clear(err);
+ /* ### For debugging purposes we could validate the md5 here,
+ but our implementations in libsvn_client already do that
+ for us... */
+ SVN_ERR(svn_txdelta_send_stream(cached_contents,
+ file->txdelta,
+ file->txdelta_baton,
+ NULL, scratch_pool));
+ SVN_ERR(svn_stream_close(cached_contents));
+ file->fetch_file = FALSE;
}
}
- /* If the working copy can provide cached contents for this
- file, we don't have to fetch them from the server. */
- if (info->cached_contents)
+ if (file->fetch_file)
{
- /* If we'll be doing a PROPFIND for this file... */
- if (info->propfind_handler)
+ fetch_ctx_t *fetch_ctx;
+
+ /* Let's fetch the file with a GET request... */
+ SVN_ERR_ASSERT(file->url && file->repos_relpath);
+
+ /* Otherwise, we use a GET request for the file's contents. */
+
+ fetch_ctx = apr_pcalloc(file->pool, sizeof(*fetch_ctx));
+ fetch_ctx->file = file;
+ fetch_ctx->using_compression = ctx->sess->using_compression;
+
+ /* Can we somehow get away with just obtaining a DIFF? */
+ if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->sess))
{
- /* ... then we'll just leave ourselves a little "todo"
- about that fact (and we'll deal with the file content
- stuff later, after we've handled that PROPFIND
- response. */
- svn_ra_serf__list_t *list_item;
-
- list_item = apr_pcalloc(info->dir->pool, sizeof(*list_item));
- list_item->data = info;
- list_item->next = ctx->file_propchanges_only;
- ctx->file_propchanges_only = list_item;
+ /* If this file is switched vs the editor root we should provide
+ its real url instead of the one calculated from the session root.
+ */
+ if (SVN_IS_VALID_REVNUM(file->base_rev))
+ {
+ fetch_ctx->delta_base = apr_psprintf(file->pool, "%s/%ld/%s",
+ ctx->sess->rev_root_stub,
+ file->base_rev,
+ svn_path_uri_encode(
+ file->repos_relpath,
+ scratch_pool));
+ }
+ else if (file->copyfrom_path)
+ {
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(file->copyfrom_rev));
+
+ fetch_ctx->delta_base = apr_psprintf(file->pool, "%s/%ld/%s",
+ ctx->sess->rev_root_stub,
+ file->copyfrom_rev,
+ svn_path_uri_encode(
+ file->copyfrom_path+1,
+ scratch_pool));
+ }
}
- else
+ else if (ctx->sess->wc_callbacks->get_wc_prop)
{
- /* Otherwise, if we've no PROPFIND to do, we might as
- well take care of those locally accessible file
- contents now. */
- SVN_ERR(handle_local_content(info, info->pool));
+ /* If we have a WC, we might be able to dive all the way into the WC
+ * to get the previous URL so we can do a differential GET with the
+ * base URL.
+ */
+ const svn_string_t *value = NULL;
+ SVN_ERR(ctx->sess->wc_callbacks->get_wc_prop(
+ ctx->sess->wc_callback_baton,
+ file->relpath,
+ SVN_RA_SERF__WC_CHECKED_IN_URL,
+ &value, scratch_pool));
+
+ fetch_ctx->delta_base = value
+ ? apr_pstrdup(file->pool, value->data)
+ : NULL;
}
- }
- else
- {
- /* Otherwise, we use a GET request for the file's contents. */
- report_fetch_t *fetch_ctx;
- fetch_ctx = apr_pcalloc(info->dir->pool, sizeof(*fetch_ctx));
- fetch_ctx->info = info;
- fetch_ctx->done_list = &ctx->done_fetches;
- fetch_ctx->sess = ctx->sess;
- fetch_ctx->conn = conn;
+ handler = svn_ra_serf__create_handler(ctx->sess, file->pool);
- handler = apr_pcalloc(info->dir->pool, sizeof(*handler));
-
- handler->handler_pool = info->dir->pool;
handler->method = "GET";
- handler->path = fetch_ctx->info->url;
+ handler->path = file->url;
- handler->conn = conn;
- handler->session = ctx->sess;
+ handler->conn = conn; /* Explicit scheduling */
handler->custom_accept_encoding = TRUE;
+ handler->no_dav_headers = TRUE;
handler->header_delegate = headers_fetch;
handler->header_delegate_baton = fetch_ctx;
@@ -1584,6 +1487,9 @@ fetch_file(report_context_t *ctx, report_info_t *info)
handler->response_error = cancel_fetch;
handler->response_error_baton = fetch_ctx;
+ handler->done_delegate = file_fetch_done;
+ handler->done_delegate_baton = fetch_ctx;
+
fetch_ctx->handler = handler;
svn_ra_serf__request_create(handler);
@@ -1591,944 +1497,691 @@ fetch_file(report_context_t *ctx, report_info_t *info)
ctx->num_active_fetches++;
}
}
- else if (info->propfind_handler)
- {
- svn_ra_serf__list_t *list_item;
- list_item = apr_pcalloc(info->dir->pool, sizeof(*list_item));
- list_item->data = info;
- list_item->next = ctx->file_propchanges_only;
- ctx->file_propchanges_only = list_item;
- }
- else
+ /* If needed, create the PROPFIND to retrieve the file's properties. */
+ if (file->fetch_props)
{
- /* No propfind or GET request. Just handle the prop changes now. */
- SVN_ERR(handle_propchange_only(info, info->pool));
+ SVN_ERR(svn_ra_serf__create_propfind_handler(&file->propfind_handler,
+ ctx->sess, file->url,
+ ctx->target_rev, "0",
+ all_props,
+ set_file_props, file,
+ file->pool));
+ file->propfind_handler->conn = conn; /* Explicit scheduling */
+
+ file->propfind_handler->done_delegate = file_props_done;
+ file->propfind_handler->done_delegate_baton = file;
+
+ /* Create a serf request for the PROPFIND. */
+ svn_ra_serf__request_create(file->propfind_handler);
+
+ ctx->num_active_propfinds++;
}
- if (ctx->num_active_fetches + ctx->num_active_propfinds
- > REQUEST_COUNT_TO_PAUSE)
- ctx->parser_ctx->paused = TRUE;
+ if (file->fetch_props || file->fetch_file)
+ return SVN_NO_ERROR;
- return SVN_NO_ERROR;
-}
-
-/** XML callbacks for our update-report response parsing */
+ /* Somehow we are done; probably via the local cache.
+ Close the file and release memory, etc. */
+ return svn_error_trace(close_file(file, scratch_pool));
+}
+
+/* Implements svn_ra_serf__prop_func */
static svn_error_t *
-start_report(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- const char **attrs,
+set_dir_prop(void *baton,
+ const char *path,
+ const char *ns,
+ const char *name,
+ const svn_string_t *val,
apr_pool_t *scratch_pool)
{
- report_context_t *ctx = parser->user_data;
- report_state_e state;
+ dir_baton_t *dir = baton;
+ report_context_t *ctx = dir->ctx;
+ const char *prop_name;
- state = parser->state->current_state;
+ prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
+ if (prop_name == NULL)
+ return SVN_NO_ERROR;
- if (state == NONE && strcmp(name.name, "update-report") == 0)
- {
- const char *val;
+ SVN_ERR(ensure_dir_opened(dir, scratch_pool));
- val = svn_xml_get_attr_value("inline-props", attrs);
- if (val && (strcmp(val, "true") == 0))
- ctx->add_props_included = TRUE;
+ SVN_ERR(ctx->editor->change_dir_prop(dir->dir_baton,
+ prop_name, val,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
- val = svn_xml_get_attr_value("send-all", attrs);
- if (val && (strcmp(val, "true") == 0))
- {
- ctx->send_all_mode = TRUE;
+/* Implements svn_ra_serf__response_done_delegate_t */
+static svn_error_t *
+dir_props_done(serf_request_t *request,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ dir_baton_t *dir = baton;
+ svn_ra_serf__handler_t *handler = dir->propfind_handler;
- /* All properties are included in send-all mode. */
- ctx->add_props_included = TRUE;
- }
- }
- else if (state == NONE && strcmp(name.name, "target-revision") == 0)
- {
- const char *rev;
+ if (handler->server_error)
+ return svn_ra_serf__server_error_create(handler, scratch_pool);
- rev = svn_xml_get_attr_value("rev", attrs);
+ if (handler->sline.code != 207)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
- if (!rev)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in target-revision element"));
- }
+ dir->ctx->num_active_propfinds--;
- SVN_ERR(ctx->update_editor->set_target_revision(ctx->update_baton,
- SVN_STR_TO_REV(rev),
- ctx->sess->pool));
- }
- else if (state == NONE && strcmp(name.name, "open-directory") == 0)
- {
- const char *rev;
- report_info_t *info;
+ /* Closing the directory will automatically deliver the propfind props.
+ *
+ * Note that closing the directory may dispose the pool containing the
+ * handler, which is only a valid operation in this callback, as after
+ * this callback serf assumes the request is done. */
- rev = svn_xml_get_attr_value("rev", attrs);
+ return svn_error_trace(maybe_close_dir(dir));
+}
- if (!rev)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-directory element"));
- }
+/* Initiates additional requests needed for a directory when not in "send-all"
+ * mode */
+static svn_error_t *
+fetch_for_dir(dir_baton_t *dir,
+ apr_pool_t *scratch)
+{
+ report_context_t *ctx = dir->ctx;
+ svn_ra_serf__connection_t *conn;
- info = push_state(parser, ctx, OPEN_DIR);
+ /* Open extra connections if we have enough requests to send. */
+ if (ctx->sess->num_conns < ctx->sess->max_connections)
+ SVN_ERR(open_connection_if_needed(ctx->sess, ctx->num_active_fetches +
+ ctx->num_active_propfinds));
- info->base_rev = SVN_STR_TO_REV(rev);
- info->dir->base_rev = info->base_rev;
- info->fetch_props = TRUE;
+ /* What connection should we go on? */
+ conn = get_best_connection(ctx);
- info->dir->base_name = "";
- info->dir->name = "";
+ /* If needed, create the PROPFIND to retrieve the file's properties. */
+ if (dir->fetch_props)
+ {
+ SVN_ERR(svn_ra_serf__create_propfind_handler(&dir->propfind_handler,
+ ctx->sess, dir->url,
+ ctx->target_rev, "0",
+ all_props,
+ set_dir_prop, dir,
+ dir->pool));
- info->base_name = info->dir->base_name;
- info->name = info->dir->name;
+ dir->propfind_handler->conn = conn;
+ dir->propfind_handler->done_delegate = dir_props_done;
+ dir->propfind_handler->done_delegate_baton = dir;
- info->dir->repos_relpath = svn_hash_gets(ctx->switched_paths, "");
+ /* Create a serf request for the PROPFIND. */
+ svn_ra_serf__request_create(dir->propfind_handler);
- if (!info->dir->repos_relpath)
- SVN_ERR(svn_ra_serf__get_relative_path(&info->dir->repos_relpath,
- ctx->sess->session_url.path,
- ctx->sess, ctx->conn,
- info->dir->pool));
- }
- else if (state == NONE)
- {
- /* do nothing as we haven't seen our valid start tag yet. */
+ ctx->num_active_propfinds++;
}
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "open-directory") == 0)
- {
- const char *rev, *dirname;
- report_dir_t *dir;
- report_info_t *info;
+ else
+ SVN_ERR_MALFUNCTION();
- rev = svn_xml_get_attr_value("rev", attrs);
+ return SVN_NO_ERROR;
+}
- if (!rev)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-directory element"));
- }
+
+/** XML callbacks for our update-report response parsing */
- dirname = svn_xml_get_attr_value("name", attrs);
+/* Conforms to svn_ra_serf__xml_opened_t */
+static svn_error_t *
+update_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)
+{
+ report_context_t *ctx = baton;
+ apr_hash_t *attrs;
- if (!dirname)
+ switch (entered_state)
+ {
+ case UPDATE_REPORT:
{
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in open-directory element"));
- }
-
- info = push_state(parser, ctx, OPEN_DIR);
-
- dir = info->dir;
+ const char *val;
- info->base_rev = SVN_STR_TO_REV(rev);
- dir->base_rev = info->base_rev;
+ attrs = svn_ra_serf__xml_gather_since(xes, UPDATE_REPORT);
+ val = svn_hash_gets(attrs, "inline-props");
- info->fetch_props = FALSE;
+ if (val && (strcmp(val, "true") == 0))
+ ctx->add_props_included = TRUE;
- dir->base_name = apr_pstrdup(dir->pool, dirname);
- info->base_name = dir->base_name;
+ val = svn_hash_gets(attrs, "send-all");
- /* Expand our name. */
- dir->name = svn_relpath_join(dir->parent_dir->name, dir->base_name,
- dir->pool);
- info->name = dir->name;
-
- dir->repos_relpath = svn_hash_gets(ctx->switched_paths, dir->name);
+ if (val && (strcmp(val, "true") == 0))
+ {
+ ctx->send_all_mode = TRUE;
- if (!dir->repos_relpath)
- dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath,
- dir->base_name, dir->pool);
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "add-directory") == 0)
- {
- const char *dir_name, *cf, *cr;
- report_dir_t *dir;
- report_info_t *info;
+ /* All properties are included in send-all mode. */
+ ctx->add_props_included = TRUE;
+ }
+ }
+ break;
- dir_name = svn_xml_get_attr_value("name", attrs);
- if (!dir_name)
+ case OPEN_DIR:
+ case ADD_DIR:
{
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in add-directory element"));
- }
- cf = svn_xml_get_attr_value("copyfrom-path", attrs);
- cr = svn_xml_get_attr_value("copyfrom-rev", attrs);
+ dir_baton_t *dir;
+ const char *name;
+ attrs = svn_ra_serf__xml_gather_since(xes, entered_state);
- info = push_state(parser, ctx, ADD_DIR);
+ name = svn_hash_gets(attrs, "name");
+ if (!name)
+ name = "";
- dir = info->dir;
+ SVN_ERR(create_dir_baton(&dir, ctx, name, scratch_pool));
- dir->base_name = apr_pstrdup(dir->pool, dir_name);
- info->base_name = dir->base_name;
+ if (entered_state == OPEN_DIR)
+ {
+ apr_int64_t base_rev;
- /* Expand our name. */
- dir->name = svn_relpath_join(dir->parent_dir->name, dir->base_name,
- dir->pool);
- info->name = dir->name;
+ SVN_ERR(svn_cstring_atoi64(&base_rev,
+ svn_hash_gets(attrs, "rev")));
+ dir->base_rev = (svn_revnum_t)base_rev;
+ }
+ else
+ {
+ dir->copyfrom_path = svn_hash_gets(attrs, "copyfrom-path");
- info->copyfrom_path = cf ? apr_pstrdup(info->pool, cf) : NULL;
- info->copyfrom_rev = cr ? SVN_STR_TO_REV(cr) : SVN_INVALID_REVNUM;
+ if (dir->copyfrom_path)
+ {
+ apr_int64_t copyfrom_rev;
+ const char *copyfrom_rev_str;
+ dir->copyfrom_path = svn_fspath__canonicalize(
+ dir->copyfrom_path,
+ dir->pool);
- /* Mark that we don't have a base. */
- info->base_rev = SVN_INVALID_REVNUM;
- dir->base_rev = info->base_rev;
+ copyfrom_rev_str = svn_hash_gets(attrs, "copyfrom-rev");
- /* If the server isn't included properties for added items,
- we'll need to fetch them ourselves. */
- if (! ctx->add_props_included)
- dir->fetch_props = TRUE;
+ if (!copyfrom_rev_str)
+ return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND,
+ NULL,
+ _("Missing '%s' attribute"),
+ "copyfrom-rev");
- dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath,
- dir->base_name, dir->pool);
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "open-file") == 0)
- {
- const char *file_name, *rev;
- report_info_t *info;
+ SVN_ERR(svn_cstring_atoi64(&copyfrom_rev, copyfrom_rev_str));
- file_name = svn_xml_get_attr_value("name", attrs);
+ dir->copyfrom_rev = (svn_revnum_t)copyfrom_rev;
+ }
- if (!file_name)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in open-file element"));
+ if (! ctx->add_props_included)
+ dir->fetch_props = TRUE;
+ }
}
-
- rev = svn_xml_get_attr_value("rev", attrs);
-
- if (!rev)
+ break;
+ case OPEN_FILE:
+ case ADD_FILE:
{
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-file element"));
- }
+ file_baton_t *file;
- info = push_state(parser, ctx, OPEN_FILE);
+ attrs = svn_ra_serf__xml_gather_since(xes, entered_state);
- info->base_rev = SVN_STR_TO_REV(rev);
- info->fetch_props = FALSE;
+ SVN_ERR(create_file_baton(&file, ctx, svn_hash_gets(attrs, "name"),
+ scratch_pool));
- info->base_name = apr_pstrdup(info->pool, file_name);
- info->name = NULL;
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "add-file") == 0)
- {
- const char *file_name, *cf, *cr;
- report_info_t *info;
-
- file_name = svn_xml_get_attr_value("name", attrs);
- cf = svn_xml_get_attr_value("copyfrom-path", attrs);
- cr = svn_xml_get_attr_value("copyfrom-rev", attrs);
+ if (entered_state == OPEN_FILE)
+ {
+ apr_int64_t base_rev;
- if (!file_name)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in add-file element"));
- }
+ SVN_ERR(svn_cstring_atoi64(&base_rev,
+ svn_hash_gets(attrs, "rev")));
+ file->base_rev = (svn_revnum_t)base_rev;
+ }
+ else
+ {
+ const char *sha1_checksum;
+ file->copyfrom_path = svn_hash_gets(attrs, "copyfrom-path");
- info = push_state(parser, ctx, ADD_FILE);
+ if (file->copyfrom_path)
+ {
+ apr_int64_t copyfrom_rev;
+ const char *copyfrom_rev_str;
- info->base_rev = SVN_INVALID_REVNUM;
+ file->copyfrom_path = svn_fspath__canonicalize(
+ file->copyfrom_path,
+ file->pool);
- /* If the server isn't in "send-all" mode, we should expect to
- fetch contents for added files. */
- if (! ctx->send_all_mode)
- info->fetch_file = TRUE;
+ copyfrom_rev_str = svn_hash_gets(attrs, "copyfrom-rev");
- /* If the server isn't included properties for added items,
- we'll need to fetch them ourselves. */
- if (! ctx->add_props_included)
- info->fetch_props = TRUE;
+ if (!copyfrom_rev_str)
+ return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND,
+ NULL,
+ _("Missing '%s' attribute"),
+ "copyfrom-rev");
- info->base_name = apr_pstrdup(info->pool, file_name);
- info->name = NULL;
+ SVN_ERR(svn_cstring_atoi64(&copyfrom_rev, copyfrom_rev_str));
- info->copyfrom_path = cf ? apr_pstrdup(info->pool, cf) : NULL;
- info->copyfrom_rev = cr ? SVN_STR_TO_REV(cr) : SVN_INVALID_REVNUM;
+ file->copyfrom_rev = (svn_revnum_t)copyfrom_rev;
+ }
- info->final_sha1_checksum =
- svn_xml_get_attr_value("sha1-checksum", attrs);
- if (info->final_sha1_checksum)
- info->final_sha1_checksum = apr_pstrdup(info->pool,
- info->final_sha1_checksum);
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "delete-entry") == 0)
- {
- const char *file_name;
- const char *rev_str;
- report_info_t *info;
- apr_pool_t *tmppool;
- const char *full_path;
- svn_revnum_t delete_rev = SVN_INVALID_REVNUM;
+ sha1_checksum = svn_hash_gets(attrs, "sha1-checksum");
+ if (sha1_checksum)
+ {
+ SVN_ERR(svn_checksum_parse_hex(&file->final_sha1_checksum,
+ svn_checksum_sha1,
+ sha1_checksum,
+ file->pool));
+ }
- file_name = svn_xml_get_attr_value("name", attrs);
+ /* If the server isn't in "send-all" mode, we should expect to
+ fetch contents for added files. */
+ if (! ctx->send_all_mode)
+ file->fetch_file = TRUE;
- if (!file_name)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in delete-entry element"));
+ /* If the server isn't included properties for added items,
+ we'll need to fetch them ourselves. */
+ if (! ctx->add_props_included)
+ file->fetch_props = TRUE;
+ }
}
+ break;
- rev_str = svn_xml_get_attr_value("rev", attrs);
- if (rev_str) /* Not available on older repositories! */
- delete_rev = SVN_STR_TO_REV(rev_str);
+ case TXDELTA:
+ {
+ file_baton_t *file = ctx->cur_file;
+ const char *base_checksum;
- info = parser->state->private;
+ /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in
+ addition to <fetch-file>s and such) when *not* in
+ "send-all" mode. As a client, we're smart enough to know
+ that's wrong, so we'll just ignore these tags. */
+ if (! ctx->send_all_mode)
+ break;
- SVN_ERR(ensure_dir_opened(info->dir));
+ file->fetch_file = FALSE;
- tmppool = svn_pool_create(info->dir->dir_baton_pool);
+ attrs = svn_ra_serf__xml_gather_since(xes, entered_state);
+ base_checksum = svn_hash_gets(attrs, "base-checksum");
- full_path = svn_relpath_join(info->dir->name, file_name, tmppool);
+ if (base_checksum)
+ SVN_ERR(svn_checksum_parse_hex(&file->base_md5_checksum,
+ svn_checksum_md5, base_checksum,
+ file->pool));
- SVN_ERR(ctx->update_editor->delete_entry(full_path,
- delete_rev,
- info->dir->dir_baton,
- tmppool));
+ SVN_ERR(open_file_txdelta(ctx->cur_file, scratch_pool));
- svn_pool_destroy(tmppool);
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "absent-directory") == 0)
- {
- const char *file_name;
- report_info_t *info;
+ if (ctx->cur_file->txdelta != svn_delta_noop_window_handler)
+ {
+ svn_stream_t *decoder;
- file_name = svn_xml_get_attr_value("name", attrs);
+ decoder = svn_txdelta_parse_svndiff(file->txdelta,
+ file->txdelta_baton,
+ TRUE /* error early close*/,
+ file->pool);
- if (!file_name)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in absent-directory element"));
+ file->txdelta_stream = svn_base64_decode(decoder, file->pool);
+ }
}
+ break;
- info = parser->state->private;
+ case FETCH_PROPS:
+ {
+ /* Subversion <= 1.6 servers will return a fetch-props element on
+ open-file and open-dir when non entry props were changed in
+ !send-all mode. In turn we fetch the full set of properties
+ and send all of those as *changes* to the editor. So these
+ editors have to be aware that they receive-non property changes.
+ (In case of incomplete directories they have to be aware anyway)
- SVN_ERR(ensure_dir_opened(info->dir));
+ In r1063337 this behavior was changed in mod_dav_svn to always
+ send property changes inline in these cases. (See issue #3657)
- SVN_ERR(ctx->update_editor->absent_directory(
- svn_relpath_join(info->name, file_name,
- info->dir->pool),
- info->dir->dir_baton,
- info->dir->pool));
+ Note that before that change the property changes to the last_*
+ entry props were already inlined via specific xml elements. */
+ if (ctx->cur_file)
+ ctx->cur_file->fetch_props = TRUE;
+ else if (ctx->cur_dir)
+ ctx->cur_dir->fetch_props = TRUE;
+ }
+ break;
}
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "absent-file") == 0)
- {
- const char *file_name;
- report_info_t *info;
- file_name = svn_xml_get_attr_value("name", attrs);
+ return SVN_NO_ERROR;
+}
- if (!file_name)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in absent-file element"));
- }
- info = parser->state->private;
- SVN_ERR(ensure_dir_opened(info->dir));
+/* Conforms to svn_ra_serf__xml_closed_t */
+static svn_error_t *
+update_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)
+{
+ report_context_t *ctx = baton;
- SVN_ERR(ctx->update_editor->absent_file(
- svn_relpath_join(info->name, file_name,
- info->dir->pool),
- info->dir->dir_baton,
- info->dir->pool));
- }
- else if (state == OPEN_DIR || state == ADD_DIR)
+ switch (leaving_state)
{
- report_info_t *info;
-
- if (strcmp(name.name, "checked-in") == 0)
- {
- info = push_state(parser, ctx, IGNORE_PROP_NAME);
- info->prop_ns = name.namespace;
- info->prop_name = apr_pstrdup(parser->state->pool, name.name);
- info->prop_encoding = NULL;
- svn_stringbuf_setempty(info->prop_value);
- }
- else if (strcmp(name.name, "set-prop") == 0 ||
- strcmp(name.name, "remove-prop") == 0)
+ case UPDATE_REPORT:
+ ctx->done = TRUE;
+ break;
+ case TARGET_REVISION:
{
- const char *full_prop_name;
- const char *colon;
-
- info = push_state(parser, ctx, PROP);
-
- full_prop_name = svn_xml_get_attr_value("name", attrs);
- if (!full_prop_name)
- {
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in %s element"),
- name.name);
- }
-
- colon = strchr(full_prop_name, ':');
-
- if (colon)
- colon++;
- else
- colon = full_prop_name;
+ const char *revstr = svn_hash_gets(attrs, "rev");
+ apr_int64_t rev;
- info->prop_ns = apr_pstrmemdup(info->dir->pool, full_prop_name,
- colon - full_prop_name);
- info->prop_name = apr_pstrdup(parser->state->pool, colon);
- info->prop_encoding = svn_xml_get_attr_value("encoding", attrs);
- svn_stringbuf_setempty(info->prop_value);
- }
- else if (strcmp(name.name, "prop") == 0)
- {
- /* need to fetch it. */
- push_state(parser, ctx, NEED_PROP_NAME);
- }
- else if (strcmp(name.name, "fetch-props") == 0)
- {
- info = parser->state->private;
+ SVN_ERR(svn_cstring_atoi64(&rev, revstr));
- info->dir->fetch_props = TRUE;
- }
- else
- {
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Unknown tag '%s' while at state %d"),
- name.name, state);
+ SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
+ (svn_revnum_t)rev,
+ scratch_pool));
}
+ break;
- }
- else if (state == OPEN_FILE || state == ADD_FILE)
- {
- report_info_t *info;
+ case CHECKED_IN_HREF:
+ if (ctx->cur_file)
+ ctx->cur_file->url = apr_pstrdup(ctx->cur_file->pool, cdata->data);
+ else
+ ctx->cur_dir->url = apr_pstrdup(ctx->cur_dir->pool, cdata->data);
+ break;
- if (strcmp(name.name, "checked-in") == 0)
- {
- info = push_state(parser, ctx, IGNORE_PROP_NAME);
- info->prop_ns = name.namespace;
- info->prop_name = apr_pstrdup(parser->state->pool, name.name);
- info->prop_encoding = NULL;
- svn_stringbuf_setempty(info->prop_value);
- }
- else if (strcmp(name.name, "prop") == 0)
- {
- /* need to fetch it. */
- push_state(parser, ctx, NEED_PROP_NAME);
- }
- else if (strcmp(name.name, "fetch-props") == 0)
+ case SET_PROP:
+ case REMOVE_PROP:
{
- info = parser->state->private;
+ const char *name = svn_hash_gets(attrs, "name");
+ const char *encoding;
+ const svn_string_t *value;
- info->fetch_props = TRUE;
- }
- else if (strcmp(name.name, "fetch-file") == 0)
- {
- info = parser->state->private;
- info->base_checksum = svn_xml_get_attr_value("base-checksum", attrs);
+ if (leaving_state == REMOVE_PROP)
+ value = NULL;
+ else if ((encoding = svn_hash_gets(attrs, "encoding")))
+ {
+ if (strcmp(encoding, "base64") != 0)
+ return svn_error_createf(SVN_ERR_XML_UNKNOWN_ENCODING, NULL,
+ _("Got unrecognized encoding '%s'"),
+ encoding);
- if (info->base_checksum)
- info->base_checksum = apr_pstrdup(info->pool, info->base_checksum);
+ value = svn_base64_decode_string(cdata, scratch_pool);
+ }
+ else
+ value = cdata;
- info->final_sha1_checksum =
- svn_xml_get_attr_value("sha1-checksum", attrs);
- if (info->final_sha1_checksum)
- info->final_sha1_checksum = apr_pstrdup(info->pool,
- info->final_sha1_checksum);
+ if (ctx->cur_file)
+ {
+ file_baton_t *file = ctx->cur_file;
- info->fetch_file = TRUE;
- }
- else if (strcmp(name.name, "set-prop") == 0 ||
- strcmp(name.name, "remove-prop") == 0)
- {
- const char *full_prop_name;
- const char *colon;
+ if (value
+ || ctx->add_props_included
+ || SVN_IS_VALID_REVNUM(file->base_rev))
+ {
+ SVN_ERR(ensure_file_opened(file, scratch_pool));
- info = push_state(parser, ctx, PROP);
+ SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
+ name,
+ value,
+ scratch_pool));
+ }
+ else
+ {
+ if (!file->remove_props)
+ file->remove_props = apr_hash_make(file->pool);
- full_prop_name = svn_xml_get_attr_value("name", attrs);
- if (!full_prop_name)
- {
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in %s element"),
- name.name);
+ svn_hash_sets(file->remove_props,
+ apr_pstrdup(file->pool, name),
+ "");
+ }
}
- colon = strchr(full_prop_name, ':');
-
- if (colon)
- colon++;
else
- colon = full_prop_name;
-
- info->prop_ns = apr_pstrmemdup(info->dir->pool, full_prop_name,
- colon - full_prop_name);
- info->prop_name = apr_pstrdup(parser->state->pool, colon);
- info->prop_encoding = svn_xml_get_attr_value("encoding", attrs);
- svn_stringbuf_setempty(info->prop_value);
- }
- else if (strcmp(name.name, "txdelta") == 0)
- {
- /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in
- addition to <fetch-file>s and such) when *not* in
- "send-all" mode. As a client, we're smart enough to know
- that's wrong, so we'll just ignore these tags. */
- if (ctx->send_all_mode)
{
- const svn_delta_editor_t *update_editor = ctx->update_editor;
-
- info = push_state(parser, ctx, TXDELTA);
+ dir_baton_t *dir = ctx->cur_dir;
- if (! info->file_baton)
+ if (value
+ || ctx->add_props_included
+ || SVN_IS_VALID_REVNUM(dir->base_rev))
{
- SVN_ERR(open_updated_file(info, FALSE, info->pool));
+ SVN_ERR(ensure_dir_opened(dir, scratch_pool));
+
+ SVN_ERR(ctx->editor->change_dir_prop(dir->dir_baton,
+ name,
+ value,
+ scratch_pool));
}
+ else
+ {
+ if (!dir->remove_props)
+ dir->remove_props = apr_hash_make(dir->pool);
- info->base_checksum = svn_xml_get_attr_value("base-checksum",
- attrs);
- SVN_ERR(update_editor->apply_textdelta(info->file_baton,
- info->base_checksum,
- info->editor_pool,
- &info->textdelta,
- &info->textdelta_baton));
- info->svndiff_decoder = svn_txdelta_parse_svndiff(
- info->textdelta,
- info->textdelta_baton,
- TRUE, info->pool);
- info->base64_decoder = svn_base64_decode(info->svndiff_decoder,
- info->pool);
+ svn_hash_sets(dir->remove_props,
+ apr_pstrdup(dir->pool, name),
+ "");
+ }
}
}
- else
- {
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Unknown tag '%s' while at state %d"),
- name.name, state);
- }
- }
- else if (state == IGNORE_PROP_NAME)
- {
- report_info_t *info = push_state(parser, ctx, PROP);
- info->prop_encoding = svn_xml_get_attr_value("encoding", attrs);
- }
- else if (state == NEED_PROP_NAME)
- {
- report_info_t *info;
-
- info = push_state(parser, ctx, PROP);
-
- info->prop_ns = name.namespace;
- info->prop_name = apr_pstrdup(parser->state->pool, name.name);
- info->prop_encoding = svn_xml_get_attr_value("encoding", attrs);
- svn_stringbuf_setempty(info->prop_value);
- }
-
- return SVN_NO_ERROR;
-}
+ break;
-static svn_error_t *
-end_report(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- apr_pool_t *scratch_pool)
-{
- report_context_t *ctx = parser->user_data;
- report_state_e state;
+ case OPEN_DIR:
+ case ADD_DIR:
+ {
+ dir_baton_t *dir = ctx->cur_dir;
+ ctx->cur_dir = ctx->cur_dir->parent_dir;
- state = parser->state->current_state;
+ if (dir->fetch_props && ! dir->url)
+ {
+ return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+ _("The REPORT response did not "
+ "include the requested checked-in "
+ "value"));
+ }
- if (state == NONE)
- {
- if (strcmp(name.name, "update-report") == 0)
- {
- ctx->report_completed = TRUE;
- }
- else
- {
- /* nothing to close yet. */
- return SVN_NO_ERROR;
+ if (!dir->fetch_props)
+ {
+ SVN_ERR(maybe_close_dir(dir));
+ break; /* dir potentially no longer valid */
+ }
+ else
+ {
+ /* Otherwise, if the server is *not* in "send-all" mode, we
+ are at a point where we can queue up the PROPFIND request */
+ SVN_ERR(fetch_for_dir(dir, scratch_pool));
+ }
}
- }
+ break;
- if (((state == OPEN_DIR && (strcmp(name.name, "open-directory") == 0)) ||
- (state == ADD_DIR && (strcmp(name.name, "add-directory") == 0))))
- {
- const char *checked_in_url;
- report_info_t *info = parser->state->private;
+ case OPEN_FILE:
+ case ADD_FILE:
+ {
+ file_baton_t *file = ctx->cur_file;
- /* We've now closed this directory; note it. */
- info->dir->tag_closed = TRUE;
+ ctx->cur_file = NULL;
+ /* go fetch info->name from DAV:checked-in */
- /* go fetch info->file_name from DAV:checked-in */
- checked_in_url =
- svn_ra_serf__get_ver_prop(info->dir->props, info->base_name,
- info->base_rev, "DAV:", "checked-in");
+ if ((file->fetch_file || file->fetch_props) && ! file->url)
+ {
+ return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+ _("The REPORT response did not "
+ "include the requested checked-in "
+ "value"));
+ }
- /* If we were expecting to have the properties and we aren't able to
- * get it, bail.
- */
- if (!checked_in_url &&
- (!SVN_IS_VALID_REVNUM(info->dir->base_rev) || info->dir->fetch_props))
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("The REPORT or PROPFIND response did not "
- "include the requested checked-in value"));
+ /* If the server is in "send-all" mode or didn't get further work,
+ we can now close the file */
+ if (! file->fetch_file && ! file->fetch_props)
+ {
+ SVN_ERR(close_file(file, scratch_pool));
+ break; /* file is no longer valid */
+ }
+ else
+ {
+ /* Otherwise, if the server is *not* in "send-all" mode, we
+ should be at a point where we can queue up any auxiliary
+ content-fetching requests. */
+ SVN_ERR(fetch_for_file(file, scratch_pool));
+ }
}
+ break;
- info->dir->url = checked_in_url;
+ case MD5_CHECKSUM:
+ SVN_ERR(svn_checksum_parse_hex(&ctx->cur_file->final_md5_checksum,
+ svn_checksum_md5,
+ cdata->data,
+ ctx->cur_file->pool));
+ break;
- /* At this point, we should have the checked-in href.
- * If needed, create the PROPFIND to retrieve the dir's properties.
- */
- if (info->dir->fetch_props)
- {
- svn_ra_serf__list_t *list_item;
-
- SVN_ERR(svn_ra_serf__deliver_props(&info->dir->propfind_handler,
- info->dir->props, ctx->sess,
- get_best_connection(ctx),
- info->dir->url,
- ctx->target_rev, "0",
- all_props,
- &ctx->done_dir_propfinds,
- info->dir->pool));
- SVN_ERR_ASSERT(info->dir->propfind_handler);
-
- /* Create a serf request for the PROPFIND. */
- svn_ra_serf__request_create(info->dir->propfind_handler);
-
- ctx->num_active_propfinds++;
-
- list_item = apr_pcalloc(info->dir->pool, sizeof(*list_item));
- list_item->data = info->dir;
- list_item->next = ctx->active_dir_propfinds;
- ctx->active_dir_propfinds = list_item;
-
- if (ctx->num_active_fetches + ctx->num_active_propfinds
- > REQUEST_COUNT_TO_PAUSE)
- ctx->parser_ctx->paused = TRUE;
- }
- else
+ case FETCH_FILE:
{
- info->dir->propfind_handler = NULL;
- }
+ file_baton_t *file = ctx->cur_file;
+ const char *base_checksum = svn_hash_gets(attrs, "base-checksum");
+ const char *sha1_checksum = svn_hash_gets(attrs, "sha1-checksum");
- /* See if this directory (and perhaps even parents of that) can
- be closed now. This is likely to be the case only if we
- didn't need to contact the server for supplemental
- information required to handle any of this directory's
- children. */
- SVN_ERR(maybe_close_dir_chain(info->dir));
- svn_ra_serf__xml_pop_state(parser);
- }
- else if (state == OPEN_FILE && strcmp(name.name, "open-file") == 0)
- {
- report_info_t *info = parser->state->private;
+ if (base_checksum)
+ SVN_ERR(svn_checksum_parse_hex(&file->base_md5_checksum,
+ svn_checksum_md5, base_checksum,
+ file->pool));
- /* Expand our full name now if we haven't done so yet. */
- if (!info->name)
- {
- info->name = svn_relpath_join(info->dir->name, info->base_name,
- info->pool);
- }
+ /* Property is duplicated between add-file and fetch-file */
+ if (sha1_checksum && !file->final_sha1_checksum)
+ SVN_ERR(svn_checksum_parse_hex(&file->final_sha1_checksum,
+ svn_checksum_sha1,
+ sha1_checksum,
+ file->pool));
- if (info->lock_token && !info->fetch_props)
- info->fetch_props = TRUE;
+ /* Some 0.3x mod_dav_svn wrote both txdelta and fetch-file
+ elements in send-all mode. (See neon for history) */
+ if (! ctx->send_all_mode)
+ file->fetch_file = TRUE;
+ }
+ break;
- /* If possible, we'd like to fetch only a delta against a
- * version of the file we already have in our working copy,
- * rather than fetching a fulltext.
- *
- * In HTTP v2, we can simply construct the URL we need given the
- * repos_relpath and base revision number.
- */
- if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->sess))
+ case DELETE_ENTRY:
{
- const char *repos_relpath;
+ const char *name = svn_hash_gets(attrs, "name");
+ const char *revstr;
+ apr_int64_t delete_rev;
- /* If this file is switched vs the editor root we should provide
- its real url instead of the one calculated from the session root.
- */
- repos_relpath = svn_hash_gets(ctx->switched_paths, info->name);
+ SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool));
- if (!repos_relpath)
- {
- if (ctx->root_is_switched)
- {
- /* We are updating a direct target (most likely a file)
- that is switched vs its parent url */
- SVN_ERR_ASSERT(*svn_relpath_dirname(info->name, info->pool)
- == '\0');
+ revstr = svn_hash_gets(attrs, "rev");
- repos_relpath = svn_hash_gets(ctx->switched_paths, "");
- }
- else
- repos_relpath = svn_relpath_join(info->dir->repos_relpath,
- info->base_name, info->pool);
- }
-
- info->delta_base = apr_psprintf(info->pool, "%s/%ld/%s",
- ctx->sess->rev_root_stub,
- info->base_rev,
- svn_path_uri_encode(repos_relpath,
- info->pool));
- }
- else if (ctx->sess->wc_callbacks->get_wc_prop)
- {
- /* If we have a WC, we might be able to dive all the way into the WC
- * to get the previous URL so we can do a differential GET with the
- * base URL.
- */
- const svn_string_t *value = NULL;
- SVN_ERR(ctx->sess->wc_callbacks->get_wc_prop(
- ctx->sess->wc_callback_baton, info->name,
- SVN_RA_SERF__WC_CHECKED_IN_URL, &value, info->pool));
+ if (revstr)
+ SVN_ERR(svn_cstring_atoi64(&delete_rev, revstr));
+ else
+ delete_rev = SVN_INVALID_REVNUM;
- info->delta_base = value ? value->data : NULL;
+ SVN_ERR(ctx->editor->delete_entry(
+ svn_relpath_join(ctx->cur_dir->relpath,
+ name,
+ scratch_pool),
+ (svn_revnum_t)delete_rev,
+ ctx->cur_dir->dir_baton,
+ scratch_pool));
}
+ break;
- /* go fetch info->name from DAV:checked-in */
- info->url = svn_ra_serf__get_ver_prop(info->props, info->base_name,
- info->base_rev, "DAV:", "checked-in");
- if (!info->url)
+ case ABSENT_DIR:
{
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("The REPORT or PROPFIND response did not "
- "include the requested checked-in value"));
- }
+ const char *name = svn_hash_gets(attrs, "name");
- /* If the server is in "send-all" mode, we might have opened the
- file when we started seeing content for it. If we didn't get
- any content for it, we still need to open the file. But in
- any case, we can then immediately close it. */
- if (ctx->send_all_mode)
- {
- if (! info->file_baton)
- {
- SVN_ERR(open_updated_file(info, FALSE, info->pool));
- }
- SVN_ERR(close_updated_file(info, info->pool));
- info->dir->ref_count--;
- }
- /* Otherwise, if the server is *not* in "send-all" mode, we
- should be at a point where we can queue up any auxiliary
- content-fetching requests. */
- else
- {
- SVN_ERR(fetch_file(ctx, info));
- }
-
- svn_ra_serf__xml_pop_state(parser);
- }
- else if (state == ADD_FILE && strcmp(name.name, "add-file") == 0)
- {
- report_info_t *info = parser->state->private;
-
- /* go fetch info->name from DAV:checked-in */
- info->url = svn_ra_serf__get_ver_prop(info->props, info->base_name,
- info->base_rev, "DAV:", "checked-in");
- if (!info->url)
- {
- return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("The REPORT or PROPFIND response did not "
- "include the requested checked-in value"));
- }
+ SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool));
- /* If the server is in "send-all" mode, we might have opened the
- file when we started seeing content for it. If we didn't get
- any content for it, we still need to open the file. But in
- any case, we can then immediately close it. */
- if (ctx->send_all_mode)
- {
- if (! info->file_baton)
- {
- SVN_ERR(open_updated_file(info, FALSE, info->pool));
- }
- SVN_ERR(close_updated_file(info, info->pool));
- info->dir->ref_count--;
+ SVN_ERR(ctx->editor->absent_directory(
+ svn_relpath_join(ctx->cur_dir->relpath,
+ name, scratch_pool),
+ ctx->cur_dir->dir_baton,
+ scratch_pool));
}
- /* Otherwise, if the server is *not* in "send-all" mode, we
- should be at a point where we can queue up any auxiliary
- content-fetching requests. */
- else
+ break;
+ case ABSENT_FILE:
{
- SVN_ERR(fetch_file(ctx, info));
- }
+ const char *name = svn_hash_gets(attrs, "name");
- svn_ra_serf__xml_pop_state(parser);
- }
- else if (state == TXDELTA && strcmp(name.name, "txdelta") == 0)
- {
- report_info_t *info = parser->state->private;
+ SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool));
- /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in addition to
- <fetch-file>s and such) when *not* in "send-all" mode. As a
- client, we're smart enough to know that's wrong, so when not
- in "receiving-all" mode, we'll ignore these tags. */
- if (ctx->send_all_mode)
- {
- SVN_ERR(svn_stream_close(info->base64_decoder));
+ SVN_ERR(ctx->editor->absent_file(
+ svn_relpath_join(ctx->cur_dir->relpath,
+ name, scratch_pool),
+ ctx->cur_dir->dir_baton,
+ scratch_pool));
}
+ break;
- svn_ra_serf__xml_pop_state(parser);
- }
- else if (state == PROP)
- {
- /* We need to move the prop_ns, prop_name, and prop_value into the
- * same lifetime as the dir->pool.
- */
- svn_ra_serf__ns_t *ns, *ns_name_match;
- svn_boolean_t found = FALSE;
- report_info_t *info;
- report_dir_t *dir;
- apr_hash_t *props;
- const svn_string_t *set_val_str;
- apr_pool_t *pool;
-
- info = parser->state->private;
- dir = info->dir;
-
- /* We're going to be slightly tricky. We don't care what the ->url
- * field is here at this point. So, we're going to stick a single
- * copy of the property name inside of the ->url field.
- */
- ns_name_match = NULL;
- for (ns = dir->ns_list; ns; ns = ns->next)
+ case TXDELTA:
{
- if (strcmp(ns->namespace, info->prop_ns) == 0)
- {
- ns_name_match = ns;
- if (strcmp(ns->url, info->prop_name) == 0)
- {
- found = TRUE;
- break;
- }
- }
- }
+ file_baton_t *file = ctx->cur_file;
- if (!found)
- {
- ns = apr_palloc(dir->pool, sizeof(*ns));
- if (!ns_name_match)
+ if (file->txdelta_stream)
{
- ns->namespace = apr_pstrdup(dir->pool, info->prop_ns);
+ SVN_ERR(svn_stream_close(file->txdelta_stream));
+ file->txdelta_stream = NULL;
}
- else
- {
- ns->namespace = ns_name_match->namespace;
- }
- ns->url = apr_pstrdup(dir->pool, info->prop_name);
-
- ns->next = dir->ns_list;
- dir->ns_list = ns;
}
+ break;
- if (strcmp(name.name, "remove-prop") != 0)
+ case VERSION_NAME:
+ case CREATIONDATE:
+ case CREATOR_DISPLAYNAME:
{
- props = info->props;
- pool = info->pool;
- }
- else
- {
- props = dir->removed_props;
- pool = dir->pool;
- svn_stringbuf_setempty(info->prop_value);
- }
+ /* Subversion <= 1.6 servers would return a fetch-props element on
+ open-file and open-dir when non entry props were changed in
+ !send-all mode. In turn we fetch the full set of properties and
+ send those as *changes* to the editor. So these editors have to
+ be aware that they receive non property changes.
+ (In case of incomplete directories they have to be aware anyway)
- if (info->prop_encoding)
- {
- if (strcmp(info->prop_encoding, "base64") == 0)
- {
- svn_string_t tmp;
+ In that case the last_* entry props are posted as 3 specific xml
+ elements, which we handle here.
- /* Don't use morph_info_string cuz we need prop_value to
- remain usable. */
- tmp.data = info->prop_value->data;
- tmp.len = info->prop_value->len;
+ In r1063337 this behavior was changed in mod_dav_svn to always
+ send property changes inline in these cases. (See issue #3657)
+ */
- set_val_str = svn_base64_decode_string(&tmp, pool);
- }
+ const char *propname;
+
+ if (ctx->cur_file)
+ SVN_ERR(ensure_file_opened(ctx->cur_file, scratch_pool));
+ else if (ctx->cur_dir)
+ SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool));
else
+ break;
+
+ switch (leaving_state)
{
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
- NULL,
- _("Got unrecognized encoding '%s'"),
- info->prop_encoding);
+ case VERSION_NAME:
+ propname = SVN_PROP_ENTRY_COMMITTED_REV;
+ break;
+ case CREATIONDATE:
+ propname = SVN_PROP_ENTRY_COMMITTED_DATE;
+ break;
+ case CREATOR_DISPLAYNAME:
+ propname = SVN_PROP_ENTRY_LAST_AUTHOR;
+ break;
+ default:
+ SVN_ERR_MALFUNCTION(); /* Impossible to reach */
}
- }
- else
- {
- set_val_str = svn_string_create_from_buf(info->prop_value, pool);
- }
-
- svn_ra_serf__set_ver_prop(props, info->base_name, info->base_rev,
- ns->namespace, ns->url, set_val_str, pool);
- /* Advance handling: if we spotted the md5-checksum property on
- the wire, remember it's value. */
- if (strcmp(ns->url, "md5-checksum") == 0
- && strcmp(ns->namespace, SVN_DAV_PROP_NS_DAV) == 0)
- info->final_checksum = apr_pstrdup(info->pool, set_val_str->data);
-
- svn_ra_serf__xml_pop_state(parser);
- }
- else if (state == IGNORE_PROP_NAME || state == NEED_PROP_NAME)
- {
- svn_ra_serf__xml_pop_state(parser);
+ if (ctx->cur_file)
+ SVN_ERR(ctx->editor->change_file_prop(ctx->cur_file->file_baton,
+ propname, cdata,
+ scratch_pool));
+ else
+ SVN_ERR(ctx->editor->change_dir_prop(ctx->cur_dir->dir_baton,
+ propname, cdata,
+ scratch_pool));
+ }
+ break;
}
return SVN_NO_ERROR;
}
+
+/* Conforms to svn_ra_serf__xml_cdata_t */
static svn_error_t *
-cdata_report(svn_ra_serf__xml_parser_t *parser,
+update_cdata(svn_ra_serf__xml_estate_t *xes,
+ void *baton,
+ int current_state,
const char *data,
apr_size_t len,
apr_pool_t *scratch_pool)
{
- report_context_t *ctx = parser->user_data;
-
- UNUSED_CTX(ctx);
-
- if (parser->state->current_state == PROP)
- {
- report_info_t *info = parser->state->private;
+ report_context_t *ctx = baton;
- svn_stringbuf_appendbytes(info->prop_value, data, len);
- }
- else if (parser->state->current_state == TXDELTA)
+ if (current_state == TXDELTA && ctx->cur_file
+ && ctx->cur_file->txdelta_stream)
{
- /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in addition to
- <fetch-file>s and such) when *not* in "send-all" mode. As a
- client, we're smart enough to know that's wrong, so when not
- in "receiving-all" mode, we'll ignore these tags. */
- if (ctx->send_all_mode)
- {
- apr_size_t nlen = len;
- report_info_t *info = parser->state->private;
-
- SVN_ERR(svn_stream_write(info->base64_decoder, data, &nlen));
- if (nlen != len)
- {
- /* Short write without associated error? "Can't happen." */
- return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
- _("Error writing to '%s': unexpected EOF"),
- info->name);
- }
- }
+ SVN_ERR(svn_stream_write(ctx->cur_file->txdelta_stream, data, &len));
}
return SVN_NO_ERROR;
@@ -2544,7 +2197,8 @@ make_simple_xml_tag(svn_stringbuf_t **buf_p,
const char *cdata,
apr_pool_t *pool)
{
- svn_xml_make_open_tag(buf_p, pool, svn_xml_protect_pcdata, tagname, NULL);
+ svn_xml_make_open_tag(buf_p, pool, svn_xml_protect_pcdata, tagname,
+ SVN_VA_NULL);
svn_xml_escape_cdata_cstring(buf_p, cdata, pool);
svn_xml_make_close_tag(buf_p, pool, tagname);
}
@@ -2566,12 +2220,11 @@ set_path(void *report_baton,
"lock-token", lock_token,
"depth", svn_depth_to_word(depth),
"start-empty", start_empty ? "true" : NULL,
- NULL);
+ SVN_VA_NULL);
svn_xml_escape_cdata_cstring(&buf, path, pool);
svn_xml_make_close_tag(&buf, pool, "S:entry");
- SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len,
- NULL, pool));
+ SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len));
return SVN_NO_ERROR;
}
@@ -2586,8 +2239,7 @@ delete_path(void *report_baton,
make_simple_xml_tag(&buf, "S:missing", path, pool);
- SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len,
- NULL, pool));
+ SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len));
return SVN_NO_ERROR;
}
@@ -2619,12 +2271,10 @@ link_path(void *report_baton,
_("Unable to parse URL '%s'"), url);
}
- SVN_ERR(svn_ra_serf__report_resource(&report_target, report->sess,
- NULL, pool));
- SVN_ERR(svn_ra_serf__get_relative_path(&link, uri.path, report->sess,
- NULL, pool));
+ SVN_ERR(svn_ra_serf__report_resource(&report_target, report->sess, pool));
+ SVN_ERR(svn_ra_serf__get_relative_path(&link, uri.path, report->sess, pool));
- link = apr_pstrcat(pool, "/", link, (char *)NULL);
+ link = apr_pstrcat(pool, "/", link, SVN_VA_NULL);
svn_xml_make_open_tag(&buf, pool, svn_xml_protect_pcdata, "S:entry",
"rev", apr_ltoa(pool, revision),
@@ -2632,80 +2282,55 @@ link_path(void *report_baton,
"depth", svn_depth_to_word(depth),
"linkpath", link,
"start-empty", start_empty ? "true" : NULL,
- NULL);
+ SVN_VA_NULL);
svn_xml_escape_cdata_cstring(&buf, path, pool);
svn_xml_make_close_tag(&buf, pool, "S:entry");
- SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len,
- NULL, pool));
+ SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len));
/* Store the switch roots to allow generating repos_relpaths from just
the working copy paths. (Needed for HTTPv2) */
path = apr_pstrdup(report->pool, path);
- svn_hash_sets(report->switched_paths,
- path, apr_pstrdup(report->pool, link + 1));
+ link = apr_pstrdup(report->pool, link + 1);
+ svn_hash_sets(report->switched_paths, path, link);
- if (!*path)
- report->root_is_switched = TRUE;
-
- return APR_SUCCESS;
-}
-
-/** Minimum nr. of outstanding requests needed before a new connection is
- * opened. */
-#define REQS_PER_CONN 8
-
-/** This function creates a new connection for this serf session, but only
- * if the number of NUM_ACTIVE_REQS > REQS_PER_CONN or if there currently is
- * only one main connection open.
- */
-static svn_error_t *
-open_connection_if_needed(svn_ra_serf__session_t *sess, int num_active_reqs)
-{
- /* For each REQS_PER_CONN outstanding requests open a new connection, with
- * a minimum of 1 extra connection. */
- if (sess->num_conns == 1 ||
- ((num_active_reqs / REQS_PER_CONN) > sess->num_conns))
+ if (!path[0] && report->update_target[0])
{
- int cur = sess->num_conns;
- apr_status_t status;
-
- sess->conns[cur] = apr_pcalloc(sess->pool, sizeof(*sess->conns[cur]));
- sess->conns[cur]->bkt_alloc = serf_bucket_allocator_create(sess->pool,
- NULL, NULL);
- sess->conns[cur]->last_status_code = -1;
- sess->conns[cur]->session = sess;
- status = serf_connection_create2(&sess->conns[cur]->conn,
- sess->context,
- sess->session_url,
- svn_ra_serf__conn_setup,
- sess->conns[cur],
- svn_ra_serf__conn_closed,
- sess->conns[cur],
- sess->pool);
- if (status)
- return svn_ra_serf__wrap_err(status, NULL);
-
- sess->num_conns++;
+ /* The update root is switched. Make sure we store it the way
+ we expect it to find */
+ svn_hash_sets(report->switched_paths, report->update_target, link);
}
- return SVN_NO_ERROR;
+ return APR_SUCCESS;
}
-/* Serf callback to create update request body bucket. */
+/* Serf callback to create update request body bucket.
+ Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_update_report_body(serf_bucket_t **body_bkt,
void *baton,
serf_bucket_alloc_t *alloc,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
report_context_t *report = baton;
- apr_off_t offset;
+ body_create_baton_t *body = report->body;
- offset = 0;
- apr_file_seek(report->body_file, APR_SET, &offset);
+ if (body->file)
+ {
+ apr_off_t offset;
+
+ offset = 0;
+ SVN_ERR(svn_io_file_seek(body->file, APR_SET, &offset, pool));
- *body_bkt = serf_bucket_file_create(report->body_file, alloc);
+ *body_bkt = serf_bucket_file_create(report->body->file, alloc);
+ }
+ else
+ {
+ *body_bkt = serf_bucket_simple_create(body->all_data,
+ body->total_bytes,
+ NULL, NULL, alloc);
+ }
return SVN_NO_ERROR;
}
@@ -2714,7 +2339,8 @@ create_update_report_body(serf_bucket_t **body_bkt,
static svn_error_t *
setup_update_report_headers(serf_bucket_t *headers,
void *baton,
- apr_pool_t *pool)
+ apr_pool_t *pool /* request pool */,
+ apr_pool_t *scratch_pool)
{
report_context_t *report = baton;
@@ -2732,368 +2358,347 @@ setup_update_report_headers(serf_bucket_t *headers,
return SVN_NO_ERROR;
}
+/* Baton for update_delay_handler */
+typedef struct update_delay_baton_t
+{
+ report_context_t *report;
+ svn_spillbuf_t *spillbuf;
+ svn_ra_serf__response_handler_t inner_handler;
+ void *inner_handler_baton;
+} update_delay_baton_t;
+
+/* Helper for update_delay_handler() and process_pending() to
+ call UDB->INNER_HANDLER with buffer pointed by DATA. */
static svn_error_t *
-finish_report(void *report_baton,
- apr_pool_t *pool)
+process_buffer(update_delay_baton_t *udb,
+ serf_request_t *request,
+ const void *data,
+ apr_size_t len,
+ svn_boolean_t at_eof,
+ serf_bucket_alloc_t *alloc,
+ apr_pool_t *pool)
{
- report_context_t *report = report_baton;
- svn_ra_serf__session_t *sess = report->sess;
- svn_ra_serf__handler_t *handler;
- svn_ra_serf__xml_parser_t *parser_ctx;
- const char *report_target;
- svn_stringbuf_t *buf = NULL;
- apr_pool_t *iterpool = svn_pool_create(pool);
+ serf_bucket_t *tmp_bucket;
svn_error_t *err;
- apr_interval_time_t waittime_left = sess->timeout;
- svn_xml_make_close_tag(&buf, iterpool, "S:update-report");
- SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len,
- NULL, iterpool));
+ /* ### This code (and the eagain bucket code) can probably be
+ ### simplified by using a bit of aggregate bucket magic.
+ ### See mail from Ivan to dev@s.a.o. */
+ if (at_eof)
+ {
+ tmp_bucket = serf_bucket_simple_create(data, len, NULL, NULL,
+ alloc);
+ }
+ else
+ {
+ tmp_bucket = svn_ra_serf__create_bucket_with_eagain(data, len,
+ alloc);
+ }
- /* We need to flush the file, make it unbuffered (so that it can be
- * zero-copied via mmap), and reset the position before attempting to
- * deliver the file.
- *
- * N.B. If we have APR 1.3+, we can unbuffer the file to let us use mmap
- * and zero-copy the PUT body. However, on older APR versions, we can't
- * check the buffer status; but serf will fall through and create a file
- * bucket for us on the buffered svndiff handle.
- */
- apr_file_flush(report->body_file);
-#if APR_VERSION_AT_LEAST(1, 3, 0)
- apr_file_buffer_set(report->body_file, NULL, 0);
-#endif
+ /* If not at EOF create a bucket that finishes with EAGAIN, otherwise
+ use a standard bucket with default EOF handling */
+ err = udb->inner_handler(request, tmp_bucket,
+ udb->inner_handler_baton, pool);
- SVN_ERR(svn_ra_serf__report_resource(&report_target, sess, NULL, pool));
+ /* And free the bucket explicitly to avoid growing request allocator
+ storage (in a loop) */
+ serf_bucket_destroy(tmp_bucket);
- /* create and deliver request */
- report->path = report_target;
+ return svn_error_trace(err);
+}
- handler = apr_pcalloc(pool, sizeof(*handler));
- handler->handler_pool = pool;
- handler->method = "REPORT";
- handler->path = report->path;
- handler->body_delegate = create_update_report_body;
- handler->body_delegate_baton = report;
- handler->body_type = "text/xml";
- handler->custom_accept_encoding = TRUE;
- handler->header_delegate = setup_update_report_headers;
- handler->header_delegate_baton = report;
- handler->conn = sess->conns[0];
- handler->session = sess;
+/* Delaying wrapping reponse handler, to avoid creating too many
+ requests to deliver efficiently */
+static svn_error_t *
+update_delay_handler(serf_request_t *request,
+ serf_bucket_t *response,
+ void *handler_baton,
+ apr_pool_t *scratch_pool)
+{
+ update_delay_baton_t *udb = handler_baton;
+ apr_status_t status;
+ apr_pool_t *iterpool = NULL;
- parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
+ if (! udb->spillbuf)
+ {
+ if (udb->report->send_all_mode)
+ {
+ /* Easy out... We only have one request, so avoid everything and just
+ call the inner handler.
- parser_ctx->pool = pool;
- parser_ctx->response_type = "update-report";
- parser_ctx->user_data = report;
- parser_ctx->start = start_report;
- parser_ctx->end = end_report;
- parser_ctx->cdata = cdata_report;
- parser_ctx->done = &report->done;
+ We will always get in the loop (below) on the first chunk, as only
+ the server can get us in true send-all mode */
- handler->response_handler = svn_ra_serf__handle_xml_parser;
- handler->response_baton = parser_ctx;
+ return svn_error_trace(udb->inner_handler(request, response,
+ udb->inner_handler_baton,
+ scratch_pool));
+ }
- report->parser_ctx = parser_ctx;
+ while ((udb->report->num_active_fetches + udb->report->num_active_propfinds)
+ < REQUEST_COUNT_TO_RESUME)
+ {
+ const char *data;
+ apr_size_t len;
+ svn_boolean_t at_eof = FALSE;
+ svn_error_t *err;
- svn_ra_serf__request_create(handler);
+ status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
+ if (SERF_BUCKET_READ_ERROR(status))
+ return svn_ra_serf__wrap_err(status, NULL);
+ else if (APR_STATUS_IS_EOF(status))
+ udb->report->report_received = at_eof = TRUE;
- /* Open the first extra connection. */
- SVN_ERR(open_connection_if_needed(sess, 0));
+ if (!iterpool)
+ iterpool = svn_pool_create(scratch_pool);
+ else
+ svn_pool_clear(iterpool);
- sess->cur_conn = 1;
+ if (len == 0 && !at_eof)
+ return svn_ra_serf__wrap_err(status, NULL);
- /* Note that we may have no active GET or PROPFIND requests, yet the
- processing has not been completed. This could be from a delay on the
- network or because we've spooled the entire response into our "pending"
- content of the XML parser. The DONE flag will get set when all the
- XML content has been received *and* parsed. */
- while (!report->done
- || report->num_active_fetches
- || report->num_active_propfinds)
+ err = process_buffer(udb, request, data, len, at_eof,
+ serf_request_get_alloc(request),
+ iterpool);
+
+ if (err && SERF_BUCKET_READ_ERROR(err->apr_err))
+ return svn_error_trace(err);
+ else if (err && APR_STATUS_IS_EAGAIN(err->apr_err))
+ {
+ svn_error_clear(err); /* Throttling is working ok */
+ }
+ else if (err && (APR_STATUS_IS_EOF(err->apr_err)))
+ {
+ svn_pool_destroy(iterpool);
+ return svn_error_trace(err); /* No buffering was necessary */
+ }
+ else
+ {
+ /* SERF_ERROR_WAIT_CONN should be impossible? */
+ return svn_error_trace(err);
+ }
+ }
+
+ /* Let's start using the spill infrastructure */
+ udb->spillbuf = svn_spillbuf__create(SPILLBUF_BLOCKSIZE,
+ SPILLBUF_MAXBUFFSIZE,
+ udb->report->pool);
+ }
+
+ /* Read everything we can to a spillbuffer */
+ do
{
- apr_pool_t *iterpool_inner;
- svn_ra_serf__list_t *done_list;
- int i;
- apr_status_t status;
+ const char *data;
+ apr_size_t len;
- /* Note: this throws out the old ITERPOOL_INNER. */
- svn_pool_clear(iterpool);
+ /* ### What blocksize should we pass? */
+ status = serf_bucket_read(response, 8*PARSE_CHUNK_SIZE, &data, &len);
- if (sess->cancel_func)
- SVN_ERR(sess->cancel_func(sess->cancel_baton));
+ if (!SERF_BUCKET_READ_ERROR(status))
+ SVN_ERR(svn_spillbuf__write(udb->spillbuf, data, len, scratch_pool));
+ }
+ while (status == APR_SUCCESS);
- /* We need to be careful between the outer and inner ITERPOOLs,
- and what items are allocated within. */
- iterpool_inner = svn_pool_create(iterpool);
+ if (APR_STATUS_IS_EOF(status))
+ udb->report->report_received = TRUE;
- status = serf_context_run(sess->context,
- SVN_RA_SERF__CONTEXT_RUN_DURATION,
- iterpool_inner);
+ /* We handle feeding the data from the main context loop, which will be right
+ after processing the pending data */
- err = sess->pending_error;
- sess->pending_error = SVN_NO_ERROR;
+ if (status)
+ return svn_ra_serf__wrap_err(status, NULL);
+ else
+ return SVN_NO_ERROR;
+}
+
+/* Process pending data from the update report, if any */
+static svn_error_t *
+process_pending(update_delay_baton_t *udb,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool = NULL;
+ serf_bucket_alloc_t *alloc = NULL;
+
+ while ((udb->report->num_active_fetches + udb->report->num_active_propfinds)
+ < REQUEST_COUNT_TO_RESUME)
+ {
+ const char *data;
+ apr_size_t len;
+ svn_boolean_t at_eof;
+ svn_error_t *err;
- if (!err && handler->done && handler->server_error)
+ if (!iterpool)
{
- err = handler->server_error->error;
+ iterpool = svn_pool_create(scratch_pool);
+ alloc = serf_bucket_allocator_create(scratch_pool, NULL, NULL);
}
+ else
+ svn_pool_clear(iterpool);
- /* If the context duration timeout is up, we'll subtract that
- duration from the total time alloted for such things. If
- there's no time left, we fail with a message indicating that
- the connection timed out. */
- if (APR_STATUS_IS_TIMEUP(status))
- {
- svn_error_clear(err);
- err = SVN_NO_ERROR;
- status = 0;
+ SVN_ERR(svn_spillbuf__read(&data, &len, udb->spillbuf, iterpool));
- if (sess->timeout)
- {
- if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION)
- {
- waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION;
- }
- else
- {
- return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
- _("Connection timed out"));
- }
- }
- }
+ if (data == NULL && !udb->report->report_received)
+ break;
+ else if (data == NULL)
+ at_eof = TRUE;
else
- {
- waittime_left = sess->timeout;
- }
+ at_eof = FALSE;
+
+ err = process_buffer(udb, NULL /* allowed? */, data, len,
+ at_eof, alloc, iterpool);
- if (status && handler->sline.code != 200)
+ if (err && APR_STATUS_IS_EAGAIN(err->apr_err))
{
- return svn_error_trace(
- svn_error_compose_create(
- svn_ra_serf__error_on_status(handler->sline,
- handler->path,
- handler->location),
- err));
+ svn_error_clear(err); /* Throttling is working */
}
- SVN_ERR(err);
- if (status)
+ else if (err && APR_STATUS_IS_EOF(err->apr_err))
{
- return svn_ra_serf__wrap_err(status, _("Error retrieving REPORT"));
+ svn_error_clear(err);
+
+ svn_pool_destroy(iterpool);
+ udb->spillbuf = NULL;
+ return SVN_NO_ERROR;
}
+ else if (err)
+ return svn_error_trace(err);
+ }
- /* Open extra connections if we have enough requests to send. */
- if (sess->num_conns < sess->max_connections)
- SVN_ERR(open_connection_if_needed(sess, report->num_active_fetches +
- report->num_active_propfinds));
+ if (iterpool)
+ svn_pool_destroy(iterpool);
- /* Prune completed file PROPFINDs. */
- done_list = report->done_propfinds;
- while (done_list)
- {
- svn_ra_serf__list_t *next_done = done_list->next;
+ return SVN_NO_ERROR;
+}
- svn_pool_clear(iterpool_inner);
+/* Process the 'update' editor report */
+static svn_error_t *
+process_editor_report(report_context_t *ctx,
+ svn_ra_serf__handler_t *handler,
+ apr_pool_t *scratch_pool)
+{
+ svn_ra_serf__session_t *sess = ctx->sess;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_interval_time_t waittime_left = sess->timeout;
+ update_delay_baton_t *ud;
- report->num_active_propfinds--;
+ /* Now wrap the response handler with delay support to avoid sending
+ out too many requests at once */
+ ud = apr_pcalloc(scratch_pool, sizeof(*ud));
+ ud->report = ctx;
- /* If we have some files that we won't be fetching the content
- * for, ensure that we update the file with any altered props.
- */
- if (report->file_propchanges_only)
- {
- svn_ra_serf__list_t *cur, *prev;
+ ud->inner_handler = handler->response_handler;
+ ud->inner_handler_baton = handler->response_baton;
- prev = NULL;
- cur = report->file_propchanges_only;
+ handler->response_handler = update_delay_handler;
+ handler->response_baton = ud;
- while (cur)
- {
- report_info_t *item = cur->data;
+ /* Open the first extra connection. */
+ SVN_ERR(open_connection_if_needed(sess, 0));
- if (item->propfind_handler == done_list->data)
- {
- break;
- }
+ sess->cur_conn = 1;
- prev = cur;
- cur = cur->next;
- }
+ /* Note that we may have no active GET or PROPFIND requests, yet the
+ processing has not been completed. This could be from a delay on the
+ network or because we've spooled the entire response into our "pending"
+ content of the XML parser. The DONE flag will get set when all the
+ XML content has been received *and* parsed. */
+ while (!handler->done
+ || ctx->num_active_fetches
+ || ctx->num_active_propfinds
+ || !ctx->done)
+ {
+ svn_error_t *err;
+ int i;
- /* If we found a match, set the new props and remove this
- * propchange from our list.
- */
- if (cur)
- {
- report_info_t *info = cur->data;
-
- if (!prev)
- {
- report->file_propchanges_only = cur->next;
- }
- else
- {
- prev->next = cur->next;
- }
-
- /* If we've got cached file content for this file,
- take care of the locally collected properties and
- file content at once. Otherwise, just deal with
- the collected properties.
-
- NOTE: These functions below could delete
- info->dir->pool (via maybe_close_dir_chain()),
- from which is allocated the list item in
- report->file_propchanges_only.
- */
- if (info->cached_contents)
- {
- SVN_ERR(handle_local_content(info, iterpool_inner));
- }
- else
- {
- SVN_ERR(handle_propchange_only(info, iterpool_inner));
- }
- }
- }
+ svn_pool_clear(iterpool);
- done_list = next_done;
- }
- report->done_propfinds = NULL;
+ err = svn_ra_serf__context_run(sess, &waittime_left, iterpool);
- /* Prune completed fetches from our list. */
- done_list = report->done_fetches;
- while (done_list)
+ if (handler->done && handler->server_error)
{
- report_fetch_t *done_fetch = done_list->data;
- svn_ra_serf__list_t *next_done = done_list->next;
- report_dir_t *cur_dir;
-
- /* Decrease the refcount in the parent directory of the file
- whose fetch has completed. */
- cur_dir = done_fetch->info->dir;
- cur_dir->ref_count--;
+ svn_error_clear(err);
+ err = svn_ra_serf__server_error_create(handler, iterpool);
- /* Decrement our active fetch count. */
- report->num_active_fetches--;
+ SVN_ERR_ASSERT(err != NULL);
+ }
- /* See if the parent directory of this fetched item (and
- perhaps even parents of that) can be closed now.
+ SVN_ERR(err);
- NOTE: This could delete cur_dir->pool, from which is
- allocated the list item in report->done_fetches.
- */
- SVN_ERR(maybe_close_dir_chain(cur_dir));
+ /* If there is pending REPORT data, process it now. */
+ if (ud->spillbuf)
+ SVN_ERR(process_pending(ud, iterpool));
- done_list = next_done;
+ /* Debugging purposes only! */
+ for (i = 0; i < sess->num_conns; i++)
+ {
+ serf_debug__closed_conn(sess->conns[i]->bkt_alloc);
}
- report->done_fetches = NULL;
+ }
- /* Prune completed directory PROPFINDs. */
- done_list = report->done_dir_propfinds;
- while (done_list)
- {
- svn_ra_serf__list_t *next_done = done_list->next;
+ svn_pool_clear(iterpool);
- report->num_active_propfinds--;
+ /* If we got a complete report, close the edit. Otherwise, abort it. */
+ if (ctx->done)
+ SVN_ERR(ctx->editor->close_edit(ctx->editor_baton, iterpool));
+ else
+ return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+ _("Missing update-report close tag"));
- if (report->active_dir_propfinds)
- {
- svn_ra_serf__list_t *cur, *prev;
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
- prev = NULL;
- cur = report->active_dir_propfinds;
+static svn_error_t *
+finish_report(void *report_baton,
+ apr_pool_t *pool)
+{
+ report_context_t *report = report_baton;
+ svn_ra_serf__session_t *sess = report->sess;
+ svn_ra_serf__handler_t *handler;
+ svn_ra_serf__xml_context_t *xmlctx;
+ const char *report_target;
+ svn_stringbuf_t *buf = NULL;
+ apr_pool_t *scratch_pool = svn_pool_create(pool);
+ svn_error_t *err;
- while (cur)
- {
- report_dir_t *item = cur->data;
+ svn_xml_make_close_tag(&buf, scratch_pool, "S:update-report");
+ SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len));
+ SVN_ERR(svn_stream_close(report->body_template));
- if (item->propfind_handler == done_list->data)
- {
- break;
- }
+ SVN_ERR(svn_ra_serf__report_resource(&report_target, sess, scratch_pool));
- prev = cur;
- cur = cur->next;
- }
- SVN_ERR_ASSERT(cur); /* we expect to find a matching propfind! */
+ xmlctx = svn_ra_serf__xml_context_create(update_ttable,
+ update_opened, update_closed,
+ update_cdata,
+ report,
+ scratch_pool);
+ handler = svn_ra_serf__create_expat_handler(sess, xmlctx, NULL,
+ scratch_pool);
- /* If we found a match, set the new props and remove this
- * propchange from our list.
- */
- if (cur)
- {
- report_dir_t *cur_dir = cur->data;
-
- if (!prev)
- {
- report->active_dir_propfinds = cur->next;
- }
- else
- {
- prev->next = cur->next;
- }
-
- /* See if this directory (and perhaps even parents of that)
- can be closed now.
-
- NOTE: This could delete cur_dir->pool, from which is
- allocated the list item in report->active_dir_propfinds.
- */
- SVN_ERR(maybe_close_dir_chain(cur_dir));
- }
- }
+ handler->method = "REPORT";
+ handler->path = report_target;
+ handler->body_delegate = create_update_report_body;
+ handler->body_delegate_baton = report;
+ handler->body_type = "text/xml";
+ handler->custom_accept_encoding = TRUE;
+ handler->header_delegate = setup_update_report_headers;
+ handler->header_delegate_baton = report;
- done_list = next_done;
- }
- report->done_dir_propfinds = NULL;
-
- /* If the parser is paused, and the number of active requests has
- dropped far enough, then resume parsing. */
- if (parser_ctx->paused
- && (report->num_active_fetches + report->num_active_propfinds
- < REQUEST_COUNT_TO_RESUME))
- parser_ctx->paused = FALSE;
-
- /* If we have not paused the parser and it looks like data MAY be
- present (we can't know for sure because of the private structure),
- then go process the pending content. */
- if (!parser_ctx->paused && parser_ctx->pending != NULL)
- SVN_ERR(svn_ra_serf__process_pending(parser_ctx,
- &report->report_received,
- iterpool_inner));
+ svn_ra_serf__request_create(handler);
- /* Debugging purposes only! */
- for (i = 0; i < sess->num_conns; i++)
- {
- serf_debug__closed_conn(sess->conns[i]->bkt_alloc);
- }
- }
+ err = process_editor_report(report, handler, scratch_pool);
- /* If we got a complete report, close the edit. Otherwise, abort it. */
- if (report->report_completed)
+ if (err)
{
- /* Ensure that we opened and closed our root dir and that we closed
- * all of our children. */
- if (!report->closed_root && report->root_dir != NULL)
- {
- SVN_ERR(close_all_dirs(report->root_dir));
- }
-
- err = report->update_editor->close_edit(report->update_baton, iterpool);
+ err = svn_error_trace(err);
+ err = svn_error_compose_create(
+ err,
+ svn_error_trace(
+ report->editor->abort_edit(report->editor_baton,
+ scratch_pool)));
}
- else
- {
- /* Tell the editor that something failed */
- err = report->update_editor->abort_edit(report->update_baton, iterpool);
- err = svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
- _("Missing update-report close tag"));
- }
+ svn_pool_destroy(scratch_pool);
- svn_pool_destroy(iterpool);
return svn_error_trace(err);
}
@@ -3162,7 +2767,7 @@ make_update_reporter(svn_ra_session_t *ra_session,
update_editor,
update_baton,
depth, has_target,
- sess->pool));
+ result_pool));
update_editor = filter_editor;
update_baton = filter_baton;
}
@@ -3170,7 +2775,6 @@ make_update_reporter(svn_ra_session_t *ra_session,
report = apr_pcalloc(result_pool, sizeof(*report));
report->pool = result_pool;
report->sess = sess;
- report->conn = report->sess->conns[0];
report->target_rev = revision;
report->ignore_ancestry = ignore_ancestry;
report->send_copyfrom_args = send_copyfrom_args;
@@ -3181,16 +2785,18 @@ make_update_reporter(svn_ra_session_t *ra_session,
report->destination = dest_path;
report->update_target = update_target;
- report->update_editor = update_editor;
- report->update_baton = update_baton;
+ report->editor = update_editor;
+ report->editor_baton = update_baton;
report->done = FALSE;
*reporter = &ra_serf_reporter;
*report_baton = report;
- SVN_ERR(svn_io_open_unique_file3(&report->body_file, NULL, NULL,
- svn_io_file_del_on_pool_cleanup,
- report->pool, scratch_pool));
+ report->body = apr_pcalloc(report->pool, sizeof(*report->body));
+ report->body->result_pool = report->pool;
+ report->body_template = svn_stream_create(report->body, report->pool);
+ svn_stream_set_write(report->body_template, body_write_fn);
+ svn_stream_set_close(report->body_template, body_done_fn);
if (sess->bulk_updates == svn_tristate_true)
{
@@ -3231,6 +2837,14 @@ make_update_reporter(svn_ra_session_t *ra_session,
supports inlining properties in update editor report. */
if (sess->supports_inline_props)
{
+ /* NOTE: both inlined properties and server->allows_bulk_update
+ (flag SVN_DAV_ALLOW_BULK_UPDATES) were added in 1.8.0, so
+ this code is never reached with a released version of
+ mod_dav_svn.
+
+ Basically by default a 1.8.0 client connecting to a 1.7.x or
+ older server will always use bulk updates. */
+
/* Inline props supported: do not use bulk updates. */
use_bulk_updates = FALSE;
}
@@ -3248,14 +2862,14 @@ make_update_reporter(svn_ra_session_t *ra_session,
svn_xml_make_open_tag(&buf, scratch_pool, svn_xml_normal,
"S:update-report",
"xmlns:S", SVN_XML_NAMESPACE, "send-all", "true",
- NULL);
+ SVN_VA_NULL);
}
else
{
svn_xml_make_open_tag(&buf, scratch_pool, svn_xml_normal,
"S:update-report",
"xmlns:S", SVN_XML_NAMESPACE,
- NULL);
+ SVN_VA_NULL);
/* Subversion 1.8+ servers can be told to send properties for newly
added items inline even when doing a skelta response. */
make_simple_xml_tag(&buf, "S:include-props", "yes", scratch_pool);
@@ -3315,8 +2929,7 @@ make_update_reporter(svn_ra_session_t *ra_session,
make_simple_xml_tag(&buf, "S:depth", svn_depth_to_word(depth), scratch_pool);
- SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len,
- NULL, scratch_pool));
+ SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len));
return SVN_NO_ERROR;
}
@@ -3367,7 +2980,8 @@ svn_ra_serf__do_diff(svn_ra_session_t *ra_session,
SVN_ERR(make_update_reporter(ra_session, reporter, report_baton,
revision,
session->session_url.path, versus_url, diff_target,
- depth, ignore_ancestry, text_deltas, FALSE,
+ depth, ignore_ancestry, text_deltas,
+ FALSE /* send_copyfrom */,
diff_editor, diff_baton,
pool, scratch_pool));
svn_pool_destroy(scratch_pool);
@@ -3426,195 +3040,3 @@ svn_ra_serf__do_switch(svn_ra_session_t *ra_session,
switch_editor, switch_baton,
result_pool, scratch_pool);
}
-
-/* Helper svn_ra_serf__get_file(). Attempts to fetch file contents
- * using SESSION->wc_callbacks->get_wc_contents() if sha1 property is
- * present in PROPS.
- *
- * Sets *FOUND_P to TRUE if file contents was successfuly fetched.
- *
- * Performs all temporary allocations in POOL.
- */
-static svn_error_t *
-try_get_wc_contents(svn_boolean_t *found_p,
- svn_ra_serf__session_t *session,
- apr_hash_t *props,
- svn_stream_t *dst_stream,
- apr_pool_t *pool)
-{
- apr_hash_t *svn_props;
- const char *sha1_checksum_prop;
- svn_checksum_t *checksum;
- svn_stream_t *wc_stream;
- svn_error_t *err;
-
- /* No contents found by default. */
- *found_p = FALSE;
-
- if (!session->wc_callbacks->get_wc_contents)
- {
- /* No callback, nothing to do. */
- return SVN_NO_ERROR;
- }
-
-
- svn_props = svn_hash_gets(props, SVN_DAV_PROP_NS_DAV);
- if (!svn_props)
- {
- /* No properties -- therefore no checksum property -- in response. */
- return SVN_NO_ERROR;
- }
-
- sha1_checksum_prop = svn_prop_get_value(svn_props, "sha1-checksum");
- if (sha1_checksum_prop == NULL)
- {
- /* No checksum property in response. */
- return SVN_NO_ERROR;
- }
-
- SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1,
- sha1_checksum_prop, pool));
-
- err = session->wc_callbacks->get_wc_contents(
- session->wc_callback_baton, &wc_stream, checksum, pool);
-
- if (err)
- {
- svn_error_clear(err);
-
- /* Ignore errors for now. */
- return SVN_NO_ERROR;
- }
-
- if (wc_stream)
- {
- SVN_ERR(svn_stream_copy3(wc_stream,
- svn_stream_disown(dst_stream, pool),
- NULL, NULL, pool));
- *found_p = TRUE;
- }
-
- return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_ra_serf__get_file(svn_ra_session_t *ra_session,
- const char *path,
- svn_revnum_t revision,
- svn_stream_t *stream,
- svn_revnum_t *fetched_rev,
- apr_hash_t **props,
- apr_pool_t *pool)
-{
- svn_ra_serf__session_t *session = ra_session->priv;
- svn_ra_serf__connection_t *conn;
- const char *fetch_url;
- apr_hash_t *fetch_props;
- svn_node_kind_t res_kind;
- const svn_ra_serf__dav_props_t *which_props;
-
- /* What connection should we go on? */
- conn = session->conns[session->cur_conn];
-
- /* Fetch properties. */
-
- fetch_url = svn_path_url_add_component2(session->session_url.path, path, pool);
-
- /* The simple case is if we want HEAD - then a GET on the fetch_url is fine.
- *
- * Otherwise, we need to get the baseline version for this particular
- * revision and then fetch that file.
- */
- if (SVN_IS_VALID_REVNUM(revision) || fetched_rev)
- {
- SVN_ERR(svn_ra_serf__get_stable_url(&fetch_url, fetched_rev,
- session, conn,
- fetch_url, revision,
- pool, pool));
- revision = SVN_INVALID_REVNUM;
- }
- /* REVISION is always SVN_INVALID_REVNUM */
- SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision));
-
- if (props)
- {
- which_props = all_props;
- }
- else if (stream && session->wc_callbacks->get_wc_contents)
- {
- which_props = type_and_checksum_props;
- }
- else
- {
- which_props = check_path_props;
- }
-
- SVN_ERR(svn_ra_serf__fetch_node_props(&fetch_props, conn, fetch_url,
- SVN_INVALID_REVNUM,
- which_props,
- pool, pool));
-
- /* Verify that resource type is not collection. */
- SVN_ERR(svn_ra_serf__get_resource_type(&res_kind, fetch_props));
- if (res_kind != svn_node_file)
- {
- return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL,
- _("Can't get text contents of a directory"));
- }
-
- /* TODO Filter out all of our props into a usable format. */
- if (props)
- {
- /* ### flatten_props() does not copy PROPVALUE, but fetch_node_props()
- ### put them into POOL, so we're okay. */
- SVN_ERR(svn_ra_serf__flatten_props(props, fetch_props,
- pool, pool));
- }
-
- if (stream)
- {
- svn_boolean_t found;
- SVN_ERR(try_get_wc_contents(&found, session, fetch_props, stream, pool));
-
- /* No contents found in the WC, let's fetch from server. */
- if (!found)
- {
- report_fetch_t *stream_ctx;
- svn_ra_serf__handler_t *handler;
-
- /* Create the fetch context. */
- stream_ctx = apr_pcalloc(pool, sizeof(*stream_ctx));
- stream_ctx->target_stream = stream;
- stream_ctx->sess = session;
- stream_ctx->conn = conn;
- stream_ctx->info = apr_pcalloc(pool, sizeof(*stream_ctx->info));
- stream_ctx->info->name = fetch_url;
-
- handler = apr_pcalloc(pool, sizeof(*handler));
-
- handler->handler_pool = pool;
- handler->method = "GET";
- handler->path = fetch_url;
- handler->conn = conn;
- handler->session = session;
-
- handler->custom_accept_encoding = TRUE;
- handler->header_delegate = headers_fetch;
- handler->header_delegate_baton = stream_ctx;
-
- handler->response_handler = handle_stream;
- handler->response_baton = stream_ctx;
-
- handler->response_error = cancel_fetch;
- handler->response_error_baton = stream_ctx;
-
- stream_ctx->handler = handler;
-
- svn_ra_serf__request_create(handler);
-
- SVN_ERR(svn_ra_serf__context_run_wait(&stream_ctx->done, session, pool));
- }
- }
-
- return SVN_NO_ERROR;
-}
diff --git a/subversion/libsvn_ra_serf/util.c b/subversion/libsvn_ra_serf/util.c
index 8f6c1bb..5490dde 100644
--- a/subversion/libsvn_ra_serf/util.c
+++ b/subversion/libsvn_ra_serf/util.c
@@ -32,83 +32,22 @@
#include <serf.h>
#include <serf_bucket_types.h>
-#include <expat.h>
-
#include "svn_hash.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 "svn_dirent_uri.h"
#include "../libsvn_ra/ra_loader.h"
#include "private/svn_dep_compat.h"
#include "private/svn_fspath.h"
-#include "private/svn_subr_private.h"
#include "private/svn_auth_private.h"
#include "private/svn_cert.h"
#include "ra_serf.h"
-
-/* Fix for older expat 1.95.x's that do not define
- * XML_STATUS_OK/XML_STATUS_ERROR
- */
-#ifndef XML_STATUS_OK
-#define XML_STATUS_OK 1
-#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
-
-/* We will store one megabyte in memory, before switching to store content
- into a temporary file. */
-#define SPILL_SIZE 1000000
-
-
-/* This structure records pending data for the parser in memory blocks,
- and possibly into a temporary file if "too much" content arrives. */
-struct svn_ra_serf__pending_t {
- /* The spillbuf where we record the pending data. */
- svn_spillbuf_t *buf;
-
- /* This flag is set when the network has reached EOF. The PENDING
- processing can then properly detect when parsing has completed. */
- svn_boolean_t network_eof;
-};
-
-#define HAS_PENDING_DATA(p) ((p) != NULL && (p)->buf != NULL \
- && 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 },
@@ -192,6 +131,7 @@ construct_realm(svn_ra_serf__session_t *session,
static char *
convert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool)
{
+ const char *cn = svn_hash_gets(org, "CN");
const char *org_unit = svn_hash_gets(org, "OU");
const char *org_name = svn_hash_gets(org, "O");
const char *locality = svn_hash_gets(org, "L");
@@ -200,6 +140,12 @@ convert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool)
const char *email = svn_hash_gets(org, "E");
svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool);
+ if (cn)
+ {
+ svn_stringbuf_appendcstr(buf, cn);
+ svn_stringbuf_appendcstr(buf, ", ");
+ }
+
if (org_unit)
{
svn_stringbuf_appendcstr(buf, org_unit);
@@ -285,7 +231,6 @@ ssl_server_cert(void *baton, int failures,
### This should really be handled by serf, which should pass an error
for this case, but that has backwards compatibility issues. */
apr_array_header_t *san;
- svn_boolean_t found_san_entry = FALSE;
svn_boolean_t found_matching_hostname = FALSE;
svn_string_t *actual_hostname =
svn_string_create(conn->session->session_url.hostname, scratch_pool);
@@ -293,11 +238,16 @@ ssl_server_cert(void *baton, int failures,
serf_cert = serf_ssl_cert_certificate(cert, scratch_pool);
san = svn_hash_gets(serf_cert, "subjectAltName");
- /* Try to find matching server name via subjectAltName first... */
- if (san)
+ /* Match server certificate CN with the hostname of the server iff
+ * we didn't find any subjectAltName fields and try to match them.
+ * Per RFC 2818 they are authoritative if present and CommonName
+ * should be ignored. NOTE: This isn't 100% correct since serf
+ * only loads the subjectAltName hash with dNSNames, technically
+ * we should ignore the CommonName if any subjectAltName entry
+ * exists even if it is one we don't support. */
+ if (san && san->nelts > 0)
{
int i;
- found_san_entry = san->nelts > 0;
for (i = 0; i < san->nelts; i++)
{
const char *s = APR_ARRAY_IDX(san, i, const char*);
@@ -310,12 +260,7 @@ ssl_server_cert(void *baton, int failures,
}
}
}
-
- /* Match server certificate CN with the hostname of the server iff
- * we didn't find any subjectAltName fields and try to match them.
- * Per RFC 2818 they are authoritative if present and CommonName
- * should be ignored. */
- if (!found_matching_hostname && !found_san_entry)
+ else
{
const char *hostname = NULL;
@@ -368,11 +313,11 @@ ssl_server_cert(void *baton, int failures,
{
svn_error_t *err;
- svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
+ svn_auth_set_parameter(conn->session->auth_baton,
SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
&cert_info);
- svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
+ svn_auth_set_parameter(conn->session->auth_baton,
SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
&svn_failures);
@@ -382,13 +327,13 @@ ssl_server_cert(void *baton, int failures,
err = svn_auth_first_credentials(&creds, &state,
SVN_AUTH_CRED_SSL_SERVER_AUTHORITY,
realmstring,
- conn->session->wc_callbacks->auth_baton,
+ conn->session->auth_baton,
scratch_pool);
- svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
+ svn_auth_set_parameter(conn->session->auth_baton,
SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL);
- svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
+ svn_auth_set_parameter(conn->session->auth_baton,
SVN_AUTH_PARAM_SSL_SERVER_FAILURES, NULL);
if (err)
@@ -415,11 +360,11 @@ ssl_server_cert(void *baton, int failures,
return APR_SUCCESS;
}
- svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
+ svn_auth_set_parameter(conn->session->auth_baton,
SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
&svn_failures);
- svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
+ svn_auth_set_parameter(conn->session->auth_baton,
SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
&cert_info);
@@ -428,7 +373,7 @@ ssl_server_cert(void *baton, int failures,
SVN_ERR(svn_auth_first_credentials(&creds, &state,
SVN_AUTH_CRED_SSL_SERVER_TRUST,
realmstring,
- conn->session->wc_callbacks->auth_baton,
+ conn->session->auth_baton,
scratch_pool));
if (creds)
{
@@ -449,7 +394,7 @@ ssl_server_cert(void *baton, int failures,
}
}
- svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
+ svn_auth_set_parameter(conn->session->auth_baton,
SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL);
/* Are there non accepted failures left? */
@@ -622,6 +567,7 @@ accept_response(serf_request_t *request,
void *acceptor_baton,
apr_pool_t *pool)
{
+ /* svn_ra_serf__handler_t *handler = acceptor_baton; */
serf_bucket_t *c;
serf_bucket_alloc_t *bkt_alloc;
@@ -639,6 +585,7 @@ accept_head(serf_request_t *request,
void *acceptor_baton,
apr_pool_t *pool)
{
+ /* svn_ra_serf__handler_t *handler = acceptor_baton; */
serf_bucket_t *response;
response = accept_response(request, stream, acceptor_baton, pool);
@@ -656,7 +603,7 @@ connection_closed(svn_ra_serf__connection_t *conn,
{
if (why)
{
- return svn_error_wrap_apr(why, NULL);
+ return svn_ra_serf__wrap_err(why, NULL);
}
if (conn->session->using_ssl)
@@ -701,7 +648,7 @@ handle_client_cert(void *data,
&conn->ssl_client_auth_state,
SVN_AUTH_CRED_SSL_CLIENT_CERT,
realm,
- session->wc_callbacks->auth_baton,
+ session->auth_baton,
pool));
}
else
@@ -753,7 +700,7 @@ handle_client_cert_pw(void *data,
&conn->ssl_client_pw_auth_state,
SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
cert_path,
- session->wc_callbacks->auth_baton,
+ session->auth_baton,
pool));
}
else
@@ -804,6 +751,9 @@ apr_status_t svn_ra_serf__handle_client_cert_pw(void *data,
*
* If CONTENT_TYPE is not-NULL, it will be sent as the Content-Type header.
*
+ * If DAV_HEADERS is non-zero, it will add standard DAV capabilites headers
+ * to request.
+ *
* REQUEST_POOL should live for the duration of the request. Serf will
* construct this and provide it to the request_setup callback, so we
* should just use that one.
@@ -816,6 +766,7 @@ setup_serf_req(serf_request_t *request,
const char *method, const char *url,
serf_bucket_t *body_bkt, const char *content_type,
const char *accept_encoding,
+ svn_boolean_t dav_headers,
apr_pool_t *request_pool,
apr_pool_t *scratch_pool)
{
@@ -882,12 +833,86 @@ setup_serf_req(serf_request_t *request,
serf_bucket_headers_setn(*hdrs_bkt, "Accept-Encoding", accept_encoding);
}
- /* These headers need to be sent with every request; see issue #3255
- ("mod_dav_svn does not pass client capabilities to start-commit
- hooks") for why. */
- serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH);
- serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO);
- serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS);
+ /* These headers need to be sent with every request that might need
+ capability processing (e.g. during commit, reports, etc.), see
+ issue #3255 ("mod_dav_svn does not pass client capabilities to
+ start-commit hooks") for why.
+
+ Some request types like GET/HEAD/PROPFIND are unaware of capability
+ handling; and in some cases the responses can even be cached by
+ proxies, so we don't have to send these hearders there. */
+ if (dav_headers)
+ {
+ serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH);
+ serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO);
+ serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra_serf__context_run(svn_ra_serf__session_t *sess,
+ apr_interval_time_t *waittime_left,
+ apr_pool_t *scratch_pool)
+{
+ apr_status_t status;
+ svn_error_t *err;
+ assert(sess->pending_error == SVN_NO_ERROR);
+
+ if (sess->cancel_func)
+ SVN_ERR(sess->cancel_func(sess->cancel_baton));
+
+ status = serf_context_run(sess->context,
+ SVN_RA_SERF__CONTEXT_RUN_DURATION,
+ scratch_pool);
+
+ err = sess->pending_error;
+ sess->pending_error = SVN_NO_ERROR;
+
+ /* If the context duration timeout is up, we'll subtract that
+ duration from the total time alloted for such things. If
+ there's no time left, we fail with a message indicating that
+ the connection timed out. */
+ if (APR_STATUS_IS_TIMEUP(status))
+ {
+ status = 0;
+
+ if (sess->timeout)
+ {
+ if (*waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION)
+ {
+ *waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION;
+ }
+ else
+ {
+ return
+ svn_error_compose_create(
+ err,
+ svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
+ _("Connection timed out")));
+ }
+ }
+ }
+ else
+ {
+ *waittime_left = sess->timeout;
+ }
+
+ SVN_ERR(err);
+ if (status)
+ {
+ /* ### This omits SVN_WARNING, and possibly relies on the fact that
+ ### MAX(SERF_ERROR_*) < SVN_ERR_BAD_CATEGORY_START? */
+ if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST)
+ {
+ /* apr can't translate subversion errors to text */
+ SVN_ERR_W(svn_error_create(status, NULL, NULL),
+ _("Error running context"));
+ }
+
+ return svn_ra_serf__wrap_err(status, _("Error running context"));
+ }
return SVN_NO_ERROR;
}
@@ -905,63 +930,11 @@ svn_ra_serf__context_run_wait(svn_boolean_t *done,
iterpool = svn_pool_create(scratch_pool);
while (!*done)
{
- apr_status_t status;
- svn_error_t *err;
int i;
svn_pool_clear(iterpool);
- if (sess->cancel_func)
- SVN_ERR((*sess->cancel_func)(sess->cancel_baton));
-
- status = serf_context_run(sess->context,
- SVN_RA_SERF__CONTEXT_RUN_DURATION,
- iterpool);
-
- err = sess->pending_error;
- sess->pending_error = SVN_NO_ERROR;
-
- /* If the context duration timeout is up, we'll subtract that
- duration from the total time alloted for such things. If
- there's no time left, we fail with a message indicating that
- the connection timed out. */
- if (APR_STATUS_IS_TIMEUP(status))
- {
- status = 0;
-
- if (sess->timeout)
- {
- if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION)
- {
- waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION;
- }
- else
- {
- return
- svn_error_compose_create(
- err,
- svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
- _("Connection timed out")));
- }
- }
- }
- else
- {
- waittime_left = sess->timeout;
- }
-
- SVN_ERR(err);
- if (status)
- {
- if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST)
- {
- /* apr can't translate subversion errors to text */
- SVN_ERR_W(svn_error_create(status, NULL, NULL),
- _("Error running context"));
- }
-
- return svn_ra_serf__wrap_err(status, _("Error running context"));
- }
+ SVN_ERR(svn_ra_serf__context_run(sess, &waittime_left, iterpool));
/* Debugging purposes only! */
for (i = 0; i < sess->num_conns; i++)
@@ -974,6 +947,22 @@ svn_ra_serf__context_run_wait(svn_boolean_t *done,
return SVN_NO_ERROR;
}
+/* Ensure that a handler is no longer scheduled on the connection.
+
+ Eventually serf will have a reliable way to cancel existing requests,
+ but currently it doesn't even have a way to relyable identify a request
+ after rescheduling, for auth reasons.
+
+ So the only thing we can do today is reset the connection, which
+ will cancel all outstanding requests and prepare the connection
+ for re-use.
+*/
+static void
+svn_ra_serf__unschedule_handler(svn_ra_serf__handler_t *handler)
+{
+ serf_connection_reset(handler->conn->conn);
+ handler->scheduled = FALSE;
+}
svn_error_t *
svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler,
@@ -988,130 +977,18 @@ svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler,
err = svn_ra_serf__context_run_wait(&handler->done, handler->session,
scratch_pool);
- /* A callback invocation has been canceled. In this simple case of
- context_run_one, we can keep the ra-session operational by resetting
- the connection.
-
- If we don't do this, the next context run will notice that the connection
- is still in the error state and will just return SVN_ERR_CEASE_INVOCATION
- (=the last error for the connection) again */
- if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
- {
- apr_status_t status = serf_connection_reset(handler->conn->conn);
-
- if (status)
- err = svn_error_compose_create(err,
- svn_ra_serf__wrap_err(status, NULL));
- }
-
- if (handler->server_error)
+ if (handler->scheduled)
{
- err = svn_error_compose_create(err, handler->server_error->error);
- handler->server_error = NULL;
+ /* We reset the connection (breaking pipelining, etc.), as
+ if we didn't the next data would still be handled by this handler,
+ which is done as far as our caller is concerned. */
+ svn_ra_serf__unschedule_handler(handler);
}
return svn_error_trace(err);
}
-/*
- * Expat callback invoked on a start element tag for an error response.
- */
-static svn_error_t *
-start_error(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- const char **attrs,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__server_error_t *ctx = parser->user_data;
-
- if (!ctx->in_error &&
- strcmp(name.namespace, "DAV:") == 0 &&
- strcmp(name.name, "error") == 0)
- {
- ctx->in_error = TRUE;
- }
- else if (ctx->in_error && strcmp(name.name, "human-readable") == 0)
- {
- const char *err_code;
-
- err_code = svn_xml_get_attr_value("errcode", attrs);
- if (err_code)
- {
- apr_int64_t val;
-
- SVN_ERR(svn_cstring_atoi64(&val, err_code));
- ctx->error->apr_err = (apr_status_t)val;
- }
-
- /* If there's no error code provided, or if the provided code is
- 0 (which can happen sometimes depending on how the error is
- constructed on the server-side), just pick a generic error
- code to run with. */
- if (! ctx->error->apr_err)
- {
- ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
- }
-
- /* Start collecting cdata. */
- svn_stringbuf_setempty(ctx->cdata);
- ctx->collect_cdata = TRUE;
- }
-
- return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on an end element tag for a PROPFIND response.
- */
-static svn_error_t *
-end_error(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__server_error_t *ctx = parser->user_data;
-
- if (ctx->in_error &&
- strcmp(name.namespace, "DAV:") == 0 &&
- strcmp(name.name, "error") == 0)
- {
- ctx->in_error = FALSE;
- }
- if (ctx->in_error && strcmp(name.name, "human-readable") == 0)
- {
- /* On the server dav_error_response_tag() will add a leading
- and trailing newline if DEBUG_CR is defined in mod_dav.h,
- so remove any such characters here. */
- svn_stringbuf_strip_whitespace(ctx->cdata);
-
- ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data,
- ctx->cdata->len);
- ctx->collect_cdata = FALSE;
- }
-
- return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on CDATA elements in an error response.
- *
- * This callback can be called multiple times.
- */
-static svn_error_t *
-cdata_error(svn_ra_serf__xml_parser_t *parser,
- const char *data,
- apr_size_t len,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__server_error_t *ctx = parser->user_data;
-
- if (ctx->collect_cdata)
- {
- svn_stringbuf_appendbytes(ctx->cdata, data, len);
- }
-
- return SVN_NO_ERROR;
-}
static apr_status_t
@@ -1131,28 +1008,7 @@ drain_bucket(serf_bucket_t *bucket)
}
-static svn_ra_serf__server_error_t *
-begin_error_parsing(svn_ra_serf__xml_start_element_t start,
- svn_ra_serf__xml_end_element_t end,
- svn_ra_serf__xml_cdata_chunk_handler_t cdata,
- apr_pool_t *result_pool)
-{
- svn_ra_serf__server_error_t *server_err;
-
- server_err = apr_pcalloc(result_pool, sizeof(*server_err));
- server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL);
- server_err->contains_precondition_error = FALSE;
- server_err->cdata = svn_stringbuf_create_empty(server_err->error->pool);
- server_err->collect_cdata = FALSE;
- server_err->parser.pool = server_err->error->pool;
- server_err->parser.user_data = server_err;
- server_err->parser.start = start;
- server_err->parser.end = end;
- server_err->parser.cdata = cdata;
- server_err->parser.ignore_errors = TRUE;
-
- return server_err;
-}
+
/* Implements svn_ra_serf__response_handler_t */
svn_error_t *
@@ -1241,7 +1097,7 @@ svn_ra_serf__expect_empty_body(serf_request_t *request,
const char *val;
/* This function is just like handle_multistatus_only() except for the
- XML parsing callbacks. We want to look for the human-readable element. */
+ XML parsing callbacks. We want to look for the -readable element. */
/* We should see this just once, in order to initialize SERVER_ERROR.
At that point, the core error processing will take over. If we choose
@@ -1251,21 +1107,22 @@ svn_ra_serf__expect_empty_body(serf_request_t *request,
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)
+ if (val
+ && (handler->sline.code < 200 || handler->sline.code >= 300)
+ && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
{
svn_ra_serf__server_error_t *server_err;
- server_err = begin_error_parsing(start_error, end_error, cdata_error,
- handler->handler_pool);
-
- /* Get the parser to set our DONE flag. */
- server_err->parser.done = &handler->done;
+ SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler,
+ FALSE,
+ handler->handler_pool,
+ handler->handler_pool));
handler->server_error = server_err;
}
else
{
- /* The body was not text/xml, so we don't know what to do with it.
+ /* The body was not text/xml, or we got a success code.
Toss anything that arrives. */
handler->discard_body = TRUE;
}
@@ -1277,631 +1134,6 @@ svn_ra_serf__expect_empty_body(serf_request_t *request,
}
-/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric
- status code into *STATUS_CODE_OUT. Ignores leading whitespace. */
-static svn_error_t *
-parse_dav_status(int *status_code_out, svn_stringbuf_t *buf,
- apr_pool_t *scratch_pool)
-{
- svn_error_t *err;
- const char *token;
- char *tok_status;
- svn_stringbuf_t *temp_buf = svn_stringbuf_dup(buf, scratch_pool);
-
- svn_stringbuf_strip_whitespace(temp_buf);
- token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status);
- if (token)
- token = apr_strtok(NULL, " \t\r\n", &tok_status);
- if (!token)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Malformed DAV:status CDATA '%s'"),
- buf->data);
- err = svn_cstring_atoi(status_code_out, token);
- if (err)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
- _("Malformed DAV:status CDATA '%s'"),
- buf->data);
-
- return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on a start element tag for a 207 response.
- */
-static svn_error_t *
-start_207(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- const char **attrs,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__server_error_t *ctx = parser->user_data;
-
- if (!ctx->in_error &&
- strcmp(name.namespace, "DAV:") == 0 &&
- strcmp(name.name, "multistatus") == 0)
- {
- ctx->in_error = TRUE;
- }
- else if (ctx->in_error && strcmp(name.name, "responsedescription") == 0)
- {
- /* Start collecting cdata. */
- svn_stringbuf_setempty(ctx->cdata);
- ctx->collect_cdata = TRUE;
- }
- else if (ctx->in_error &&
- strcmp(name.namespace, "DAV:") == 0 &&
- strcmp(name.name, "status") == 0)
- {
- /* Start collecting cdata. */
- svn_stringbuf_setempty(ctx->cdata);
- ctx->collect_cdata = TRUE;
- }
-
- return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on an end element tag for a 207 response.
- */
-static svn_error_t *
-end_207(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__server_error_t *ctx = parser->user_data;
-
- if (ctx->in_error &&
- strcmp(name.namespace, "DAV:") == 0 &&
- strcmp(name.name, "multistatus") == 0)
- {
- ctx->in_error = FALSE;
- }
- if (ctx->in_error && strcmp(name.name, "responsedescription") == 0)
- {
- /* Remove leading newline added by DEBUG_CR on server */
- svn_stringbuf_strip_whitespace(ctx->cdata);
-
- ctx->collect_cdata = FALSE;
- ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data,
- ctx->cdata->len);
- if (ctx->contains_precondition_error)
- ctx->error->apr_err = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH;
- else
- ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
- }
- else if (ctx->in_error &&
- strcmp(name.namespace, "DAV:") == 0 &&
- strcmp(name.name, "status") == 0)
- {
- int status_code;
-
- ctx->collect_cdata = FALSE;
-
- SVN_ERR(parse_dav_status(&status_code, ctx->cdata, parser->pool));
- if (status_code == 412)
- ctx->contains_precondition_error = TRUE;
- }
-
- return SVN_NO_ERROR;
-}
-
-/*
- * Expat callback invoked on CDATA elements in a 207 response.
- *
- * This callback can be called multiple times.
- */
-static svn_error_t *
-cdata_207(svn_ra_serf__xml_parser_t *parser,
- const char *data,
- apr_size_t len,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__server_error_t *ctx = parser->user_data;
-
- if (ctx->collect_cdata)
- {
- svn_stringbuf_appendbytes(ctx->cdata, data, len);
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Implements svn_ra_serf__response_handler_t */
-svn_error_t *
-svn_ra_serf__handle_multistatus_only(serf_request_t *request,
- serf_bucket_t *response,
- void *baton,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__handler_t *handler = baton;
-
- /* This function is just like expect_empty_body() except for the
- XML parsing callbacks. We are looking for very limited pieces of
- the multistatus response. */
-
- /* We should see this just once, in order to initialize SERVER_ERROR.
- At that point, the core error processing will take over. If we choose
- not to parse an error, then we'll never return here (because we
- change the response handler). */
- SVN_ERR_ASSERT(handler->server_error == NULL);
-
- {
- serf_bucket_t *hdrs;
- const char *val;
-
- hdrs = serf_bucket_response_get_headers(response);
- val = serf_bucket_headers_get(hdrs, "Content-Type");
- if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
- {
- svn_ra_serf__server_error_t *server_err;
-
- server_err = begin_error_parsing(start_207, end_207, cdata_207,
- handler->handler_pool);
-
- /* Get the parser to set our DONE flag. */
- server_err->parser.done = &handler->done;
-
- handler->server_error = server_err;
- }
- else
- {
- /* The body was not text/xml, so we don't know what to do with it.
- Toss anything that arrives. */
- handler->discard_body = TRUE;
- }
- }
-
- /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it
- to call the response handler again. That will start up the XML parsing,
- or it will be dropped on the floor (per the decision above). */
- return SVN_NO_ERROR;
-}
-
-
-/* Conforms to Expat's XML_StartElementHandler */
-static void
-start_xml(void *userData, const char *raw_name, const char **attrs)
-{
- svn_ra_serf__xml_parser_t *parser = userData;
- svn_ra_serf__dav_props_t name;
- apr_pool_t *scratch_pool;
- svn_error_t *err;
-
- if (parser->error)
- return;
-
- if (!parser->state)
- svn_ra_serf__xml_push_state(parser, 0);
-
- /* ### get a real scratch_pool */
- scratch_pool = parser->state->pool;
-
- svn_ra_serf__define_ns(&parser->state->ns_list, attrs, parser->state->pool);
-
- svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name);
-
- err = parser->start(parser, name, attrs, scratch_pool);
- if (err && !SERF_BUCKET_READ_ERROR(err->apr_err))
- err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
-
- parser->error = err;
-}
-
-
-/* Conforms to Expat's XML_EndElementHandler */
-static void
-end_xml(void *userData, const char *raw_name)
-{
- svn_ra_serf__xml_parser_t *parser = userData;
- svn_ra_serf__dav_props_t name;
- svn_error_t *err;
- apr_pool_t *scratch_pool;
-
- if (parser->error)
- return;
-
- /* ### get a real scratch_pool */
- scratch_pool = parser->state->pool;
-
- svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name);
-
- err = parser->end(parser, name, scratch_pool);
- if (err && !SERF_BUCKET_READ_ERROR(err->apr_err))
- err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
-
- parser->error = err;
-}
-
-
-/* Conforms to Expat's XML_CharacterDataHandler */
-static void
-cdata_xml(void *userData, const char *data, int len)
-{
- svn_ra_serf__xml_parser_t *parser = userData;
- svn_error_t *err;
- apr_pool_t *scratch_pool;
-
- if (parser->error)
- return;
-
- if (!parser->state)
- svn_ra_serf__xml_push_state(parser, 0);
-
- /* ### get a real scratch_pool */
- scratch_pool = parser->state->pool;
-
- err = parser->cdata(parser, data, len, scratch_pool);
- if (err && !SERF_BUCKET_READ_ERROR(err->apr_err))
- err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
-
- parser->error = err;
-}
-
-/* Flip the requisite bits in CTX to indicate that processing of the
- response is complete, adding the current "done item" to the list of
- completed items. */
-static void
-add_done_item(svn_ra_serf__xml_parser_t *ctx)
-{
- /* Make sure we don't add to DONE_LIST twice. */
- if (!*ctx->done)
- {
- *ctx->done = TRUE;
- if (ctx->done_list)
- {
- ctx->done_item->data = ctx->user_data;
- ctx->done_item->next = *ctx->done_list;
- *ctx->done_list = ctx->done_item;
- }
- }
-}
-
-
-static svn_error_t *
-write_to_pending(svn_ra_serf__xml_parser_t *ctx,
- const char *data,
- apr_size_t len,
- apr_pool_t *scratch_pool)
-{
- if (ctx->pending == NULL)
- {
- ctx->pending = apr_pcalloc(ctx->pool, sizeof(*ctx->pending));
- ctx->pending->buf = svn_spillbuf__create(PARSE_CHUNK_SIZE,
- SPILL_SIZE,
- ctx->pool);
- }
-
- /* Copy the data into one or more chunks in the spill buffer. */
- return svn_error_trace(svn_spillbuf__write(ctx->pending->buf,
- data, len,
- scratch_pool));
-}
-
-
-static svn_error_t *
-inject_to_parser(svn_ra_serf__xml_parser_t *ctx,
- const char *data,
- apr_size_t len,
- const serf_status_line *sl)
-{
- int xml_status;
-
- xml_status = XML_Parse(ctx->xmlp, data, (int) len, 0);
-
- if (! ctx->ignore_errors)
- {
- SVN_ERR(ctx->error);
-
- if (xml_status != XML_STATUS_OK)
- {
- if (sl == NULL)
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("XML parsing failed"));
-
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("XML parsing failed: (%d %s)"),
- sl->code, sl->reason);
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Apr pool cleanup handler to release an XML_Parser in success and error
- conditions */
-static apr_status_t
-xml_parser_cleanup(void *baton)
-{
- XML_Parser *xmlp = baton;
-
- if (*xmlp)
- {
- (void) XML_ParserFree(*xmlp);
- *xmlp = NULL;
- }
-
- return APR_SUCCESS;
-}
-
-/* Limit the amount of pending content to parse at once to < 100KB per
- iteration. This number is chosen somewhat arbitrarely. Making it lower
- will have a drastical negative impact on performance, whereas increasing it
- increases the risk for connection timeouts.
- */
-#define PENDING_TO_PARSE PARSE_CHUNK_SIZE * 5
-
-svn_error_t *
-svn_ra_serf__process_pending(svn_ra_serf__xml_parser_t *parser,
- svn_boolean_t *network_eof,
- apr_pool_t *scratch_pool)
-{
- svn_boolean_t pending_empty = FALSE;
- apr_size_t cur_read = 0;
-
- /* Fast path exit: already paused, nothing to do, or already done. */
- if (parser->paused || parser->pending == NULL || *parser->done)
- {
- *network_eof = parser->pending ? parser->pending->network_eof : FALSE;
- return SVN_NO_ERROR;
- }
-
- /* Parsing the pending conten in the spillbuf will result in many disc i/o
- operations. This can be so slow that we don't run the network event
- processing loop often enough, resulting in timed out connections.
-
- So we limit the amounts of bytes parsed per iteration.
- */
- while (cur_read < PENDING_TO_PARSE)
- {
- const char *data;
- apr_size_t len;
-
- /* Get a block of content, stopping the loop when we run out. */
- SVN_ERR(svn_spillbuf__read(&data, &len, parser->pending->buf,
- scratch_pool));
- if (data)
- {
- /* Inject the content into the XML parser. */
- SVN_ERR(inject_to_parser(parser, data, len, NULL));
-
- /* If the XML parsing callbacks paused us, then we're done for now. */
- if (parser->paused)
- break;
-
- cur_read += len;
- }
- else
- {
- /* The buffer is empty. */
- pending_empty = TRUE;
- break;
- }
- }
-
- /* If the PENDING structures are empty *and* we consumed all content from
- the network, then we're completely done with the parsing. */
- if (pending_empty &&
- parser->pending->network_eof)
- {
- int xml_status;
- SVN_ERR_ASSERT(parser->xmlp != NULL);
-
- /* Tell the parser that no more content will be parsed. */
- xml_status = XML_Parse(parser->xmlp, NULL, 0, 1);
-
- apr_pool_cleanup_run(parser->pool, &parser->xmlp, xml_parser_cleanup);
- parser->xmlp = NULL;
-
- if (! parser->ignore_errors)
- {
- SVN_ERR(parser->error);
-
- if (xml_status != XML_STATUS_OK)
- {
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("XML parsing failed"));
- }
- }
-
- add_done_item(parser);
- }
-
- *network_eof = parser->pending ? parser->pending->network_eof : FALSE;
-
- return SVN_NO_ERROR;
-}
-#undef PENDING_TO_PARSE
-
-
-/* ### this is still broken conceptually. just shifting incrementally... */
-static svn_error_t *
-handle_server_error(serf_request_t *request,
- serf_bucket_t *response,
- apr_pool_t *scratch_pool)
-{
- svn_ra_serf__server_error_t server_err = { 0 };
- serf_bucket_t *hdrs;
- const char *val;
- apr_status_t err;
-
- hdrs = serf_bucket_response_get_headers(response);
- val = serf_bucket_headers_get(hdrs, "Content-Type");
- if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
- {
- /* ### we should figure out how to reuse begin_error_parsing */
-
- server_err.error = svn_error_create(APR_SUCCESS, NULL, NULL);
- server_err.contains_precondition_error = FALSE;
- server_err.cdata = svn_stringbuf_create_empty(scratch_pool);
- server_err.collect_cdata = FALSE;
- server_err.parser.pool = server_err.error->pool;
- server_err.parser.user_data = &server_err;
- server_err.parser.start = start_error;
- server_err.parser.end = end_error;
- server_err.parser.cdata = cdata_error;
- server_err.parser.done = &server_err.done;
- server_err.parser.ignore_errors = TRUE;
-
- /* We don't care about any errors except for SERVER_ERR.ERROR */
- svn_error_clear(svn_ra_serf__handle_xml_parser(request,
- response,
- &server_err.parser,
- scratch_pool));
-
- /* ### checking DONE is silly. the above only parses whatever has
- ### been received at the network interface. totally wrong. but
- ### it is what we have for now (maintaining historical code),
- ### until we fully migrate. */
- if (server_err.done && server_err.error->apr_err == APR_SUCCESS)
- {
- svn_error_clear(server_err.error);
- server_err.error = SVN_NO_ERROR;
- }
-
- return svn_error_trace(server_err.error);
- }
-
- /* The only error that we will return is from the XML response body.
- Otherwise, ignore the entire body but allow SUCCESS/EOF/EAGAIN to
- surface. */
- err = drain_bucket(response);
- if (err && !SERF_BUCKET_READ_ERROR(err))
- return svn_ra_serf__wrap_err(err, NULL);
-
- return SVN_NO_ERROR;
-}
-
-
-/* Implements svn_ra_serf__response_handler_t */
-svn_error_t *
-svn_ra_serf__handle_xml_parser(serf_request_t *request,
- serf_bucket_t *response,
- void *baton,
- apr_pool_t *pool)
-{
- serf_status_line sl;
- apr_status_t status;
- svn_ra_serf__xml_parser_t *ctx = baton;
- svn_error_t *err;
-
- /* ### get the HANDLER rather than fetching this. */
- status = serf_bucket_response_status(response, &sl);
- if (SERF_BUCKET_READ_ERROR(status))
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
-
- /* Woo-hoo. Nothing here to see. */
- if (sl.code == 404 && !ctx->ignore_errors)
- {
- err = handle_server_error(request, response, pool);
-
- if (err && APR_STATUS_IS_EOF(err->apr_err))
- add_done_item(ctx);
-
- return svn_error_trace(err);
- }
-
- if (!ctx->xmlp)
- {
- ctx->xmlp = XML_ParserCreate(NULL);
- apr_pool_cleanup_register(ctx->pool, &ctx->xmlp, xml_parser_cleanup,
- apr_pool_cleanup_null);
- XML_SetUserData(ctx->xmlp, ctx);
- XML_SetElementHandler(ctx->xmlp, start_xml, end_xml);
- if (ctx->cdata)
- {
- XML_SetCharacterDataHandler(ctx->xmlp, cdata_xml);
- }
- }
-
- while (1)
- {
- const char *data;
- apr_size_t len;
-
- status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
- if (SERF_BUCKET_READ_ERROR(status))
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
-
- /* Note: once the callbacks invoked by inject_to_parser() sets the
- PAUSED flag, then it will not be cleared. write_to_pending() will
- only save the content. Logic outside of serf_context_run() will
- clear that flag, as appropriate, along with processing the
- content that we have placed into the PENDING buffer.
-
- We want to save arriving content into the PENDING structures if
- the parser has been paused, or we already have data in there (so
- the arriving data is appended, rather than injected out of order) */
- if (ctx->paused || HAS_PENDING_DATA(ctx->pending))
- {
- err = write_to_pending(ctx, data, len, pool);
- }
- else
- {
- err = inject_to_parser(ctx, data, len, &sl);
- if (err)
- {
- /* Should have no errors if IGNORE_ERRORS is set. */
- SVN_ERR_ASSERT(!ctx->ignore_errors);
- }
- }
- if (err)
- {
- SVN_ERR_ASSERT(ctx->xmlp != NULL);
-
- apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup);
- add_done_item(ctx);
- return svn_error_trace(err);
- }
-
- if (APR_STATUS_IS_EAGAIN(status))
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
-
- if (APR_STATUS_IS_EOF(status))
- {
- if (ctx->pending != NULL)
- ctx->pending->network_eof = TRUE;
-
- /* We just hit the end of the network content. If we have nothing
- in the PENDING structures, then we're completely done. */
- if (!HAS_PENDING_DATA(ctx->pending))
- {
- int xml_status;
- SVN_ERR_ASSERT(ctx->xmlp != NULL);
-
- xml_status = XML_Parse(ctx->xmlp, NULL, 0, 1);
-
- apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup);
-
- if (! ctx->ignore_errors)
- {
- SVN_ERR(ctx->error);
-
- if (xml_status != XML_STATUS_OK)
- {
- return svn_error_create(
- SVN_ERR_XML_MALFORMED, NULL,
- _("The XML response contains invalid XML"));
- }
- }
-
- add_done_item(ctx);
- }
-
- return svn_ra_serf__wrap_err(status, NULL);
- }
-
- /* feed me! */
- }
- /* not reached */
-}
-
-
apr_status_t
svn_ra_serf__credentials_callback(char **username, char **password,
serf_request_t *request, void *baton,
@@ -1927,7 +1159,7 @@ svn_ra_serf__credentials_callback(char **username, char **password,
&session->auth_state,
SVN_AUTH_CRED_SIMPLE,
realm,
- session->wc_callbacks->auth_baton,
+ session->auth_baton,
session->pool);
}
else
@@ -2008,6 +1240,8 @@ handle_response(serf_request_t *request,
if (!response)
{
/* Uh-oh. Our connection died. */
+ handler->scheduled = FALSE;
+
if (handler->response_error)
{
/* Give a handler chance to prevent request requeue. */
@@ -2126,10 +1360,7 @@ handle_response(serf_request_t *request,
}
handler->conn->last_status_code = handler->sline.code;
- if (handler->sline.code == 405
- || handler->sline.code == 408
- || handler->sline.code == 409
- || handler->sline.code >= 500)
+ if (handler->sline.code >= 400)
{
/* 405 Method Not allowed.
408 Request Timeout
@@ -2144,35 +1375,22 @@ handle_response(serf_request_t *request,
{
svn_ra_serf__server_error_t *server_err;
- server_err = begin_error_parsing(start_error, end_error, cdata_error,
- handler->handler_pool);
- /* Get the parser to set our DONE flag. */
- server_err->parser.done = &handler->done;
+ SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler,
+ FALSE,
+ handler->handler_pool,
+ handler->handler_pool));
handler->server_error = server_err;
}
else
{
handler->discard_body = TRUE;
-
- if (!handler->session->pending_error)
- {
- apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
-
- /* 405 == Method Not Allowed (Occurs when trying to lock a working
- copy path which no longer exists at HEAD in the repository. */
- if (handler->sline.code == 405
- && strcmp(handler->method, "LOCK") == 0)
- apr_err = SVN_ERR_FS_OUT_OF_DATE;
-
- handler->session->pending_error =
- svn_error_createf(apr_err, NULL,
- _("%s request on '%s' failed: %d %s"),
- handler->method, handler->path,
- handler->sline.code, handler->sline.reason);
- }
}
}
+ else if (handler->sline.code <= 199)
+ {
+ handler->discard_body = TRUE;
+ }
/* Stop processing the above, on every packet arrival. */
handler->reading_body = TRUE;
@@ -2184,13 +1402,6 @@ handle_response(serf_request_t *request,
{
*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;
}
@@ -2198,50 +1409,12 @@ handle_response(serf_request_t *request,
that now. */
if (handler->server_error != NULL)
{
- err = svn_ra_serf__handle_xml_parser(request, response,
- &handler->server_error->parser,
- scratch_pool);
-
- /* If we do not receive an error or it is a non-transient error, return
- immediately.
-
- 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))
- {
- *serf_status = err->apr_err;
- svn_error_clear(err);
- return SVN_NO_ERROR;
- }
-
- /* 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;
+ return svn_error_trace(
+ svn_ra_serf__handle_server_error(handler->server_error,
+ handler,
+ request, response,
+ serf_status,
+ scratch_pool));
}
/* Pass the body along to the registered response handler. */
@@ -2271,12 +1444,13 @@ static apr_status_t
handle_response_cb(serf_request_t *request,
serf_bucket_t *response,
void *baton,
- apr_pool_t *scratch_pool)
+ apr_pool_t *response_pool)
{
svn_ra_serf__handler_t *handler = baton;
svn_error_t *err;
apr_status_t inner_status;
apr_status_t outer_status;
+ apr_pool_t *scratch_pool = response_pool; /* Scratch pool needed? */
err = svn_error_trace(handle_response(request, response,
handler, &inner_status,
@@ -2287,9 +1461,34 @@ handle_response_cb(serf_request_t *request,
if (!outer_status)
outer_status = inner_status;
- /* Make sure the DONE flag is set properly. */
+ /* Make sure the DONE flag is set properly and requests are cleaned up. */
if (APR_STATUS_IS_EOF(outer_status) || APR_STATUS_IS_EOF(inner_status))
- handler->done = TRUE;
+ {
+ svn_ra_serf__session_t *sess = handler->session;
+ handler->done = TRUE;
+ handler->scheduled = FALSE;
+ outer_status = APR_EOF;
+
+ /* We use a cached handler->session here to allow handler to free the
+ memory containing the handler */
+ save_error(sess,
+ handler->done_delegate(request, handler->done_delegate_baton,
+ scratch_pool));
+ }
+ else if (SERF_BUCKET_READ_ERROR(outer_status)
+ && handler->session->pending_error)
+ {
+ handler->discard_body = TRUE; /* Discard further data */
+ handler->done = TRUE; /* Mark as done */
+ /* handler->scheduled is still TRUE, as we still expect data.
+ If we would return an error outer-status the connection
+ would have to be restarted. With scheduled still TRUE
+ destroying the handler's pool will still reset the
+ connection, avoiding the posibility of returning
+ an error for this handler when a new request is
+ scheduled. */
+ outer_status = APR_EAGAIN; /* Exit context loop */
+ }
return outer_status;
}
@@ -2312,9 +1511,8 @@ setup_request(serf_request_t *request,
{
serf_bucket_alloc_t *bkt_alloc = serf_request_get_alloc(request);
- /* ### should pass the scratch_pool */
SVN_ERR(handler->body_delegate(&body_bkt, handler->body_delegate_baton,
- bkt_alloc, request_pool));
+ bkt_alloc, request_pool, scratch_pool));
}
else
{
@@ -2338,17 +1536,17 @@ setup_request(serf_request_t *request,
SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt,
handler->session, handler->method, handler->path,
body_bkt, handler->body_type, accept_encoding,
- request_pool, scratch_pool));
+ !handler->no_dav_headers, request_pool,
+ scratch_pool));
if (handler->header_delegate)
{
- /* ### should pass the scratch_pool */
SVN_ERR(handler->header_delegate(headers_bkt,
handler->header_delegate_baton,
- request_pool));
+ request_pool, scratch_pool));
}
- return APR_SUCCESS;
+ return SVN_NO_ERROR;
}
/* Implements the serf_request_setup_t interface (which sets up both a
@@ -2362,51 +1560,58 @@ setup_request_cb(serf_request_t *request,
void **acceptor_baton,
serf_response_handler_t *s_handler,
void **s_handler_baton,
- apr_pool_t *pool)
+ apr_pool_t *request_pool)
{
svn_ra_serf__handler_t *handler = setup_baton;
+ apr_pool_t *scratch_pool;
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;
+ /* Construct a scratch_pool? serf gives us a pool that will live for
+ the duration of the request. But requests are retried in some cases */
+ scratch_pool = svn_pool_create(request_pool);
if (strcmp(handler->method, "HEAD") == 0)
*acceptor = accept_head;
else
*acceptor = accept_response;
- *acceptor_baton = handler->session;
+ *acceptor_baton = handler;
*s_handler = handle_response_cb;
*s_handler_baton = handler;
err = svn_error_trace(setup_request(request, handler, req_bkt,
- pool /* request_pool */, scratch_pool));
+ request_pool, scratch_pool));
+ svn_pool_destroy(scratch_pool);
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.
+ SVN_ERR_ASSERT_NO_RETURN(handler->handler_pool != NULL
+ && !handler->scheduled);
- ### prior to recent changes, HANDLER was constant. maybe we should
- ### break out these processing fields, apart from the request
- ### definition. */
+ /* In case HANDLER is re-queued, reset the various transient fields. */
handler->done = FALSE;
handler->server_error = NULL;
handler->sline.version = 0;
handler->location = NULL;
handler->reading_body = FALSE;
handler->discard_body = FALSE;
+ handler->scheduled = TRUE;
+
+ /* Keeping track of the returned request object would be nice, but doesn't
+ work the way we would expect in ra_serf..
+
+ Serf sometimes creates a new request for us (and destroys the old one)
+ without telling, like when authentication failed (401/407 response.
- /* ### do we ever alter the >response_handler? */
+ We 'just' trust serf to do the right thing and expect it to tell us
+ when the state of the request changes.
- /* ### do we need to hold onto the returned request object, or just
- ### not worry about it (the serf ctx will manage it). */
+ ### I fixed a request leak in serf in r2258 on auth failures.
+ */
(void) serf_connection_request_create(handler->conn->conn,
setup_request_cb, handler);
}
@@ -2415,8 +1620,7 @@ svn_ra_serf__request_create(svn_ra_serf__handler_t *handler)
svn_error_t *
svn_ra_serf__discover_vcc(const char **vcc_url,
svn_ra_serf__session_t *session,
- svn_ra_serf__connection_t *conn,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
const char *path;
const char *relative_path;
@@ -2429,12 +1633,6 @@ svn_ra_serf__discover_vcc(const char **vcc_url,
return SVN_NO_ERROR;
}
- /* If no connection is provided, use the default one. */
- if (! conn)
- {
- conn = session->conns[0];
- }
-
path = session->session_url.path;
*vcc_url = NULL;
uuid = NULL;
@@ -2444,9 +1642,10 @@ svn_ra_serf__discover_vcc(const char **vcc_url,
apr_hash_t *props;
svn_error_t *err;
- err = svn_ra_serf__fetch_node_props(&props, conn,
+ err = svn_ra_serf__fetch_node_props(&props, session,
path, SVN_INVALID_REVNUM,
- base_props, pool, pool);
+ base_props,
+ scratch_pool, scratch_pool);
if (! err)
{
apr_hash_t *ns_props;
@@ -2474,12 +1673,7 @@ svn_ra_serf__discover_vcc(const char **vcc_url,
svn_error_clear(err);
/* Okay, strip off a component from PATH. */
- path = svn_urlpath__dirname(path, pool);
-
- /* An error occurred on conns. serf 0.4.0 remembers that
- the connection had a problem. We need to reset it, in
- order to use it again. */
- serf_connection_reset(conn->conn);
+ path = svn_urlpath__dirname(path, scratch_pool);
}
}
}
@@ -2505,7 +1699,7 @@ svn_ra_serf__discover_vcc(const char **vcc_url,
{
svn_stringbuf_t *url_buf;
- url_buf = svn_stringbuf_create(path, pool);
+ url_buf = svn_stringbuf_create(path, scratch_pool);
svn_path_remove_components(url_buf,
svn_path_component_count(relative_path));
@@ -2533,7 +1727,6 @@ svn_error_t *
svn_ra_serf__get_relative_path(const char **rel_path,
const char *orig_path,
svn_ra_serf__session_t *session,
- svn_ra_serf__connection_t *conn,
apr_pool_t *pool)
{
const char *decoded_root, *decoded_orig;
@@ -2550,7 +1743,6 @@ svn_ra_serf__get_relative_path(const char **rel_path,
promises to populate the session's root-url cache, and that's
what we really want. */
SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session,
- conn ? conn : session->conns[0],
pool));
}
@@ -2564,7 +1756,6 @@ svn_ra_serf__get_relative_path(const char **rel_path,
svn_error_t *
svn_ra_serf__report_resource(const char **report_target,
svn_ra_serf__session_t *session,
- svn_ra_serf__connection_t *conn,
apr_pool_t *pool)
{
/* If we have HTTP v2 support, we want to report against the 'me'
@@ -2574,7 +1765,7 @@ svn_ra_serf__report_resource(const char **report_target,
/* Otherwise, we'll use the default VCC. */
else
- SVN_ERR(svn_ra_serf__discover_vcc(report_target, session, conn, pool));
+ SVN_ERR(svn_ra_serf__discover_vcc(report_target, session, pool));
return SVN_NO_ERROR;
}
@@ -2588,13 +1779,14 @@ svn_ra_serf__error_on_status(serf_status_line sline,
{
case 301:
case 302:
+ case 303:
case 307:
+ case 308:
return svn_error_createf(SVN_ERR_RA_DAV_RELOCATED, NULL,
(sline.code == 301)
- ? _("Repository moved permanently to '%s';"
- " please relocate")
- : _("Repository moved temporarily to '%s';"
- " please relocate"), location);
+ ? _("Repository moved permanently to '%s'")
+ : _("Repository moved temporarily to '%s'"),
+ location);
case 403:
return svn_error_createf(SVN_ERR_RA_DAV_FORBIDDEN, NULL,
_("Access to '%s' forbidden"), path);
@@ -2602,6 +1794,16 @@ svn_ra_serf__error_on_status(serf_status_line sline,
case 404:
return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
_("'%s' path not found"), path);
+ case 405:
+ return svn_error_createf(SVN_ERR_RA_DAV_METHOD_NOT_ALLOWED, NULL,
+ _("HTTP method is not allowed on '%s'"),
+ path);
+ case 409:
+ return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
+ _("'%s' conflicts"), path);
+ case 412:
+ return svn_error_createf(SVN_ERR_RA_DAV_PRECONDITION_FAILED, NULL,
+ _("Precondition on '%s' failed"), path);
case 423:
return svn_error_createf(SVN_ERR_FS_NO_LOCK_TOKEN, NULL,
_("'%s': no lock token available"), path);
@@ -2612,21 +1814,59 @@ svn_ra_serf__error_on_status(serf_status_line sline,
"server or an intermediate proxy does not accept "
"chunked encoding. Try setting 'http-chunked-requests' "
"to 'auto' or 'no' in your client configuration."));
+ case 500:
+ return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+ _("Unexpected server error %d '%s' on '%s'"),
+ sline.code, sline.reason, path);
case 501:
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("The requested feature is not supported by "
"'%s'"), path);
}
- if (sline.code >= 300)
+ if (sline.code >= 300 || sline.code <= 199)
return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
- _("Unexpected HTTP status %d '%s' on '%s'\n"),
+ _("Unexpected HTTP status %d '%s' on '%s'"),
sline.code, sline.reason, path);
return SVN_NO_ERROR;
}
svn_error_t *
+svn_ra_serf__unexpected_status(svn_ra_serf__handler_t *handler)
+{
+ /* Is it a standard error status? */
+ if (handler->sline.code != 405)
+ SVN_ERR(svn_ra_serf__error_on_status(handler->sline,
+ handler->path,
+ handler->location));
+
+ switch (handler->sline.code)
+ {
+ case 201:
+ return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+ _("Path '%s' unexpectedly created"),
+ handler->path);
+ case 204:
+ return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
+ _("Path '%s' already exists"),
+ handler->path);
+
+ case 405:
+ return svn_error_createf(SVN_ERR_RA_DAV_METHOD_NOT_ALLOWED, NULL,
+ _("The HTTP method '%s' is not allowed"
+ " on '%s'"),
+ handler->method, handler->path);
+ default:
+ return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+ _("Unexpected HTTP status %d '%s' on '%s' "
+ "request to '%s'"),
+ handler->sline.code, handler->sline.reason,
+ handler->method, handler->path);
+ }
+}
+
+svn_error_t *
svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *ra_session,
svn_delta_shim_callbacks_t *callbacks)
{
@@ -2636,185 +1876,102 @@ svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *ra_session,
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)
+/* Shared/standard done_delegate handler */
+static svn_error_t *
+response_done(serf_request_t *request,
+ void *handler_baton,
+ apr_pool_t *scratch_pool)
{
- struct expat_ctx_t *ectx = userData;
+ svn_ra_serf__handler_t *handler = handler_baton;
- 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
-}
+ assert(handler->done);
+ if (handler->no_fail_on_http_failure_status)
+ return SVN_NO_ERROR;
-/* 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 (handler->server_error)
+ return svn_ra_serf__server_error_create(handler, scratch_pool);
- if (!ectx->parser)
+ if (handler->sline.code >= 400 || handler->sline.code <= 199)
{
- 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);
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
}
- /* ### TODO: sline.code < 200 should really be handled by the core */
- if ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300))
+ if ((handler->sline.code >= 300 && handler->sline.code < 399)
+ && !handler->no_fail_on_http_redirect_status)
{
- /* 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));
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
}
- while (1)
- {
- apr_status_t status;
- const char *data;
- apr_size_t len;
- int expat_status;
-
- status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
- if (SERF_BUCKET_READ_ERROR(status))
- return svn_ra_serf__wrap_err(status, NULL);
-
-#if 0
- /* ### move restart/skip into the core handler */
- ectx->handler->read_size += len;
-#endif
-
- /* ### move PAUSED behavior to a new response handler that can feed
- ### an inner handler, or can pause for a while. */
-
- /* ### should we have an IGNORE_ERRORS flag like the v1 parser? */
-
- expat_status = XML_Parse(ectx->parser, data, (int)len, 0 /* isFinal */);
-
- /* We need to check INNER_ERROR first. This is an error from the
- callbacks that has been "dropped off" for us to retrieve. On
- current Expat parsers, we stop the parser when an error occurs,
- so we want to ignore EXPAT_STATUS (which reports the stoppage).
-
- If an error is not present, THEN we go ahead and look for parsing
- errors. */
- if (ectx->inner_error)
- {
- apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
- xml_parser_cleanup);
- return svn_error_trace(ectx->inner_error);
- }
- if (expat_status == XML_STATUS_ERROR)
- return svn_error_createf(SVN_ERR_XML_MALFORMED,
- ectx->inner_error,
- _("The %s response contains invalid XML"
- " (%d %s)"),
- ectx->handler->method,
- ectx->handler->sline.code,
- ectx->handler->sline.reason);
-
- /* The parsing went fine. What has the bucket told us? */
-
- 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 */);
+ return SVN_NO_ERROR;
+}
- svn_ra_serf__xml_context_destroy(ectx->xmlctx);
- apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
- xml_parser_cleanup);
+/* Pool cleanup handler for request handlers.
- /* ### should check XMLCTX to see if it has returned to the
- ### INITIAL state. we may have ended early... */
- }
+ If a serf context run stops for some outside error, like when the user
+ cancels a request via ^C in the context loop, the handler is still
+ registered in the serf context. With the pool cleanup there would be
+ handlers registered in no freed memory.
- if (status && !SERF_BUCKET_READ_ERROR(status))
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
+ This fallback kills the connection for this case, which will make serf
+ unregister any outstanding requests on it. */
+static apr_status_t
+handler_cleanup(void *baton)
+{
+ svn_ra_serf__handler_t *handler = baton;
+ if (handler->scheduled)
+ {
+ svn_ra_serf__unschedule_handler(handler);
}
- /* NOTREACHED */
+ return APR_SUCCESS;
}
-
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__create_handler(svn_ra_serf__session_t *session,
+ 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;
+ apr_pool_cleanup_register(result_pool, handler, handler_cleanup,
+ apr_pool_cleanup_null);
+
+ handler->session = session;
+ handler->conn = session->conns[0];
+
+ /* Setup the default done handler, to handle server errors */
+ handler->done_delegate_baton = handler;
+ handler->done_delegate = response_done;
return handler;
}
+
+svn_error_t *
+svn_ra_serf__uri_parse(apr_uri_t *uri,
+ const char *url_str,
+ apr_pool_t *result_pool)
+{
+ apr_status_t status;
+
+ status = apr_uri_parse(result_pool, url_str, uri);
+ if (status)
+ {
+ /* Do not use returned error status in error message because currently
+ apr_uri_parse() returns APR_EGENERAL for all parsing errors. */
+ return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("Illegal URL '%s'"),
+ url_str);
+ }
+
+ /* Depending the version of apr-util in use, for root paths uri.path
+ will be NULL or "", where serf requires "/". */
+ if (uri->path == NULL || uri->path[0] == '\0')
+ {
+ uri->path = apr_pstrdup(result_pool, "/");
+ }
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/libsvn_ra_serf/util_error.c b/subversion/libsvn_ra_serf/util_error.c
index da66091..bce935a 100644
--- a/subversion/libsvn_ra_serf/util_error.c
+++ b/subversion/libsvn_ra_serf/util_error.c
@@ -88,7 +88,8 @@ svn_ra_serf__wrap_err(apr_status_t status,
}
if (err_msg)
{
- err->message = apr_pstrcat(err->pool, msg, ": ", err_msg, NULL);
+ err->message = apr_pstrcat(err->pool, msg, ": ", err_msg,
+ SVN_VA_NULL);
}
else
{
diff --git a/subversion/libsvn_ra_serf/xml.c b/subversion/libsvn_ra_serf/xml.c
index a95eacc..1a98857 100644
--- a/subversion/libsvn_ra_serf/xml.c
+++ b/subversion/libsvn_ra_serf/xml.c
@@ -24,6 +24,7 @@
#include <apr_uri.h>
+#include <expat.h>
#include <serf.h>
#include "svn_hash.h"
@@ -42,13 +43,35 @@
#include "ra_serf.h"
+/* Fix for older expat 1.95.x's that do not define
+ * XML_STATUS_OK/XML_STATUS_ERROR
+ */
+#ifndef XML_STATUS_OK
+#define XML_STATUS_OK 1
+#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 /* XML_VERSION_AT_LEAST */
+
+/* Read/write chunks of this size into the spillbuf. */
+#define PARSE_CHUNK_SIZE 8000
+
+
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;
+ /* If WAITING >= then we are waiting for an element to close before
+ resuming events. The number stored here is the amount of nested
+ elements open. The Xml parser will make sure the document is well
+ formed. */
+ int waiting;
/* The transition table. */
const svn_ra_serf__xml_transition_t *ttable;
@@ -83,6 +106,16 @@ struct svn_ra_serf__xml_context_t {
};
+/* Structure which represents an XML namespace. */
+typedef struct svn_ra_serf__ns_t {
+ /* The assigned name. */
+ const char *xmlns;
+ /* The full URL for this namespace. */
+ const char *url;
+ /* The next namespace in our list. */
+ struct svn_ra_serf__ns_t *next;
+} svn_ra_serf__ns_t;
+
struct svn_ra_serf__xml_estate_t {
/* The current state value. */
int state;
@@ -114,6 +147,19 @@ struct svn_ra_serf__xml_estate_t {
};
+struct expat_ctx_t {
+ svn_ra_serf__xml_context_t *xmlctx;
+ XML_Parser parser;
+ svn_ra_serf__handler_t *handler;
+ const int *expected_status;
+
+ 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 void
define_namespaces(svn_ra_serf__ns_t **ns_list,
@@ -140,7 +186,7 @@ define_namespaces(svn_ra_serf__ns_t **ns_list,
/* Have we already defined this ns previously? */
for (cur_ns = *ns_list; cur_ns; cur_ns = cur_ns->next)
{
- if (strcmp(cur_ns->namespace, prefix) == 0)
+ if (strcmp(cur_ns->xmlns, prefix) == 0)
{
found = TRUE;
break;
@@ -157,7 +203,7 @@ define_namespaces(svn_ra_serf__ns_t **ns_list,
else
pool = baton;
new_ns = apr_palloc(pool, sizeof(*new_ns));
- new_ns->namespace = apr_pstrdup(pool, prefix);
+ new_ns->xmlns = apr_pstrdup(pool, prefix);
new_ns->url = apr_pstrdup(pool, tmp_attrs[1]);
/* Push into the front of NS_LIST. Parent states will point
@@ -170,22 +216,15 @@ define_namespaces(svn_ra_serf__ns_t **ns_list,
}
}
-
-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.
+ * Look up @a name in the @a ns_list list for previously declared namespace
+ * definitions.
+ *
+ * Return (in @a *returned_prop_name) a #svn_ra_serf__dav_props_t tuple
+ * representing the expanded name.
*/
-void
-svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
+static void
+expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
const svn_ra_serf__ns_t *ns_list,
const char *name)
{
@@ -198,9 +237,9 @@ svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
for (ns = ns_list; ns; ns = ns->next)
{
- if (strncmp(ns->namespace, name, colon - name) == 0)
+ if (strncmp(ns->xmlns, name, colon - name) == 0)
{
- returned_prop_name->namespace = ns->url;
+ returned_prop_name->xmlns = ns->url;
returned_prop_name->name = colon + 1;
return;
}
@@ -212,9 +251,9 @@ svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
for (ns = ns_list; ns; ns = ns->next)
{
- if (! ns->namespace[0])
+ if (! ns->xmlns[0])
{
- returned_prop_name->namespace = ns->url;
+ returned_prop_name->xmlns = ns->url;
returned_prop_name->name = name;
return;
}
@@ -223,7 +262,7 @@ svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
/* If the prefix is not found, then the name is NOT within a
namespace. */
- returned_prop_name->namespace = "";
+ returned_prop_name->xmlns = "";
returned_prop_name->name = name;
}
@@ -285,6 +324,49 @@ svn_ra_serf__add_open_tag_buckets(serf_bucket_t *agg_bucket,
}
void
+svn_ra_serf__add_empty_tag_buckets(serf_bucket_t *agg_bucket,
+ serf_bucket_alloc_t *bkt_alloc,
+ const char *tag, ...)
+{
+ va_list ap;
+ const char *key;
+ serf_bucket_t *tmp;
+
+ tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, bkt_alloc);
+ serf_bucket_aggregate_append(agg_bucket, tmp);
+
+ tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc);
+ serf_bucket_aggregate_append(agg_bucket, tmp);
+
+ va_start(ap, tag);
+ while ((key = va_arg(ap, char *)) != NULL)
+ {
+ const char *val = va_arg(ap, const char *);
+ if (val)
+ {
+ tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" ", 1, bkt_alloc);
+ serf_bucket_aggregate_append(agg_bucket, tmp);
+
+ tmp = SERF_BUCKET_SIMPLE_STRING(key, bkt_alloc);
+ serf_bucket_aggregate_append(agg_bucket, tmp);
+
+ tmp = SERF_BUCKET_SIMPLE_STRING_LEN("=\"", 2, bkt_alloc);
+ serf_bucket_aggregate_append(agg_bucket, tmp);
+
+ tmp = SERF_BUCKET_SIMPLE_STRING(val, bkt_alloc);
+ serf_bucket_aggregate_append(agg_bucket, tmp);
+
+ tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"", 1, bkt_alloc);
+ serf_bucket_aggregate_append(agg_bucket, tmp);
+ }
+ }
+ va_end(ap);
+
+ tmp = SERF_BUCKET_SIMPLE_STRING_LEN("/>", 2, bkt_alloc);
+ serf_bucket_aggregate_append(agg_bucket, tmp);
+}
+
+void
svn_ra_serf__add_close_tag_buckets(serf_bucket_t *agg_bucket,
serf_bucket_alloc_t *bkt_alloc,
const char *tag)
@@ -368,7 +450,7 @@ void svn_ra_serf__add_tag_buckets(serf_bucket_t *agg_bucket, const char *tag,
const char *value,
serf_bucket_alloc_t *bkt_alloc)
{
- svn_ra_serf__add_open_tag_buckets(agg_bucket, bkt_alloc, tag, NULL);
+ svn_ra_serf__add_open_tag_buckets(agg_bucket, bkt_alloc, tag, SVN_VA_NULL);
if (value)
{
@@ -379,54 +461,6 @@ void svn_ra_serf__add_tag_buckets(serf_bucket_t *agg_bucket, const char *tag,
svn_ra_serf__add_close_tag_buckets(agg_bucket, bkt_alloc, tag);
}
-void
-svn_ra_serf__xml_push_state(svn_ra_serf__xml_parser_t *parser,
- int state)
-{
- svn_ra_serf__xml_state_t *new_state;
-
- if (!parser->free_state)
- {
- new_state = apr_palloc(parser->pool, sizeof(*new_state));
- new_state->pool = svn_pool_create(parser->pool);
- }
- else
- {
- new_state = parser->free_state;
- parser->free_state = parser->free_state->prev;
-
- svn_pool_clear(new_state->pool);
- }
-
- if (parser->state)
- {
- new_state->private = parser->state->private;
- new_state->ns_list = parser->state->ns_list;
- }
- else
- {
- new_state->private = NULL;
- new_state->ns_list = NULL;
- }
-
- new_state->current_state = state;
-
- /* Add it to the state chain. */
- new_state->prev = parser->state;
- parser->state = new_state;
-}
-
-void svn_ra_serf__xml_pop_state(svn_ra_serf__xml_parser_t *parser)
-{
- svn_ra_serf__xml_state_t *cur_state;
-
- cur_state = parser->state;
- parser->state = cur_state->prev;
- 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)
@@ -458,11 +492,50 @@ lazy_create_pool(void *baton)
return xes->state_pool;
}
-void
-svn_ra_serf__xml_context_destroy(
- svn_ra_serf__xml_context_t *xmlctx)
+svn_error_t *
+svn_ra_serf__xml_context_done(svn_ra_serf__xml_context_t *xmlctx)
{
+ if (xmlctx->current->prev)
+ {
+ /* Probably unreachable as this would be an xml parser error */
+ return svn_error_createf(SVN_ERR_XML_MALFORMED, NULL,
+ _("XML stream truncated: closing '%s' missing"),
+ xmlctx->current->tag.name);
+ }
+ else if (! xmlctx->free_states)
+ {
+ /* If we have no items on the free_states list, we didn't push anything,
+ which tells us that we found an empty xml body */
+ const svn_ra_serf__xml_transition_t *scan;
+ const svn_ra_serf__xml_transition_t *document = NULL;
+ const char *msg;
+
+ for (scan = xmlctx->ttable; scan->ns != NULL; ++scan)
+ {
+ if (scan->from_state == XML_STATE_INITIAL)
+ {
+ if (document != NULL)
+ {
+ document = NULL; /* Multiple document elements defined */
+ break;
+ }
+ document = scan;
+ }
+ }
+
+ if (document)
+ msg = apr_psprintf(xmlctx->scratch_pool, "'%s' element not found",
+ document->name);
+ else
+ msg = _("document element not found");
+
+ return svn_error_createf(SVN_ERR_XML_MALFORMED, NULL,
+ _("XML stream truncated: %s"),
+ msg);
+ }
+
svn_pool_destroy(xmlctx->scratch_pool);
+ return SVN_NO_ERROR;
}
svn_ra_serf__xml_context_t *
@@ -577,10 +650,10 @@ svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes)
}
-svn_error_t *
-svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
- const char *raw_name,
- const char *const *attrs)
+static svn_error_t *
+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;
@@ -590,14 +663,17 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
/* 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;
+ if (xmlctx->waiting > 0)
+ {
+ xmlctx->waiting++;
+ return SVN_NO_ERROR;
+ }
/* Look for xmlns: attributes. Lazily create the state pool if any
were found. */
define_namespaces(&current->ns_list, attrs, lazy_create_pool, current);
- svn_ra_serf__expand_ns(&elemname, current->ns_list, raw_name);
+ expand_ns(&elemname, current->ns_list, raw_name);
for (scan = xmlctx->ttable; scan->ns != NULL; ++scan)
{
@@ -610,21 +686,20 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
/* Found a specific transition. */
if (strcmp(elemname.name, scan->name) == 0
- && strcmp(elemname.namespace, scan->ns) == 0)
+ && strcmp(elemname.xmlns, scan->ns) == 0)
break;
}
if (scan->ns == NULL)
{
- if (current->state == 0)
+ if (current->state == XML_STATE_INITIAL)
{
return svn_error_createf(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+ SVN_ERR_XML_UNEXPECTED_ELEMENT, NULL,
_("XML Parsing failed: Unexpected root element '%s'"),
elemname.name);
}
- xmlctx->waiting = elemname;
- /* ### return? */
+ xmlctx->waiting++; /* Start waiting for the close tag */
return SVN_NO_ERROR;
}
@@ -677,10 +752,11 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
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);
+ return svn_error_createf(
+ SVN_ERR_XML_ATTRIB_NOT_FOUND,
+ NULL,
+ _("Missing XML attribute '%s' on '%s' element"),
+ name, scan->name);
}
if (value)
@@ -699,7 +775,7 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
/* 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->tag.xmlns = apr_pstrdup(new_pool, elemname.xmlns);
new_xes->custom_close = scan->custom_close;
/* Start with the parent's namespace set. */
@@ -723,39 +799,18 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
}
-svn_error_t *
-svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
- const char *raw_name)
+static svn_error_t *
+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 (xmlctx->waiting > 0)
{
- /* 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;
+ xmlctx->waiting--;
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;
@@ -799,14 +854,14 @@ svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
}
-svn_error_t *
-svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
- const char *data,
- apr_size_t len)
+static svn_error_t *
+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)
+ if (xmlctx->waiting > 0)
return SVN_NO_ERROR;
/* If the current state is collecting cdata, then copy the cdata. */
@@ -831,3 +886,256 @@ svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
return SVN_NO_ERROR;
}
+/* svn_error_t * wrapper around XML_Parse */
+static APR_INLINE svn_error_t *
+parse_xml(struct expat_ctx_t *ectx, const char *data, apr_size_t len, svn_boolean_t is_final)
+{
+ int xml_status = XML_Parse(ectx->parser, data, (int)len, is_final);
+ const char *msg;
+ int xml_code;
+
+ if (xml_status == XML_STATUS_OK)
+ return ectx->inner_error;
+
+ xml_code = XML_GetErrorCode(ectx->parser);
+
+#if XML_VERSION_AT_LEAST(1, 95, 8)
+ /* If we called XML_StopParser() expat will return an abort error. If we
+ have a better error stored we should ignore it as it will not help
+ the end-user to store it in the error chain. */
+ if (xml_code == XML_ERROR_ABORTED && ectx->inner_error)
+ return ectx->inner_error;
+#endif
+
+ msg = XML_ErrorString(xml_code);
+
+ return svn_error_compose_create(
+ ectx->inner_error,
+ svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA,
+ svn_error_createf(SVN_ERR_XML_MALFORMED, NULL,
+ _("Malformed XML: %s"),
+ msg),
+ _("The XML response contains invalid XML")));
+}
+
+/* Apr pool cleanup handler to release an XML_Parser in success and error
+ conditions */
+static apr_status_t
+xml_parser_cleanup(void *baton)
+{
+ XML_Parser *xmlp = baton;
+
+ if (*xmlp)
+ {
+ (void) XML_ParserFree(*xmlp);
+ *xmlp = NULL;
+ }
+
+ return APR_SUCCESS;
+}
+
+/* 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(xml_cb_start(ectx->xmlctx,
+ raw_name, attrs));
+
+#if XML_VERSION_AT_LEAST(1, 95, 8)
+ 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(xml_cb_end(ectx->xmlctx, raw_name));
+
+#if XML_VERSION_AT_LEAST(1, 95, 8)
+ 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(xml_cb_cdata(ectx->xmlctx, data, len));
+
+#if XML_VERSION_AT_LEAST(1, 95, 8)
+ if (ectx->inner_error)
+ (void) XML_StopParser(ectx->parser, 0 /* resumable */);
+#endif
+}
+
+#if XML_VERSION_AT_LEAST(1, 95, 8)
+static void
+expat_entity_declaration(void *userData,
+ const XML_Char *entityName,
+ int is_parameter_entity,
+ const XML_Char *value,
+ int value_length,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId,
+ const XML_Char *notationName)
+{
+ struct expat_ctx_t *ectx = userData;
+
+ /* Stop the parser if an entity declaration is hit. */
+ XML_StopParser(ectx->parser, 0 /* resumable */);
+}
+#else
+/* A noop default_handler. */
+static void
+expat_default_handler(void *userData, const XML_Char *s, int len)
+{
+}
+#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;
+ svn_boolean_t got_expected_status;
+
+ if (ectx->expected_status)
+ {
+ const int *status = ectx->expected_status;
+ got_expected_status = FALSE;
+
+ while (*status && ectx->handler->sline.code != *status)
+ status++;
+
+ got_expected_status = (*status) != 0;
+ }
+ else
+ got_expected_status = (ectx->handler->sline.code == 200);
+
+ if (!ectx->handler->server_error
+ && ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300)
+ || ! got_expected_status))
+ {
+ /* 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. */
+
+ /* ### This handles xml bodies as svn-errors (returned via serf context
+ ### loop), but ignores non-xml errors.
+
+ Current code depends on this behavior and checks itself while other
+ continues, and then verifies if work has been performed.
+
+ ### TODO: Make error checking consistent */
+
+ /* ### If !GOT_EXPECTED_STATUS, this should always produce an error */
+ return svn_error_trace(svn_ra_serf__expect_empty_body(
+ request, response, ectx->handler,
+ scratch_pool));
+ }
+
+ 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);
+
+#if XML_VERSION_AT_LEAST(1, 95, 8)
+ XML_SetEntityDeclHandler(ectx->parser, expat_entity_declaration);
+#else
+ XML_SetDefaultHandler(ectx->parser, expat_default_handler);
+#endif
+ }
+
+ while (1)
+ {
+ apr_status_t status;
+ const char *data;
+ apr_size_t len;
+ svn_error_t *err;
+ svn_boolean_t at_eof = FALSE;
+
+ status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
+ if (SERF_BUCKET_READ_ERROR(status))
+ return svn_ra_serf__wrap_err(status, NULL);
+ else if (APR_STATUS_IS_EOF(status))
+ at_eof = TRUE;
+
+ err = parse_xml(ectx, data, len, at_eof /* isFinal */);
+
+ if (at_eof || err)
+ {
+ /* Release xml parser state/tables. */
+ apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
+ xml_parser_cleanup);
+ }
+
+ SVN_ERR(err);
+
+ /* The parsing went fine. What has the bucket told us? */
+ if (at_eof)
+ {
+ /* Make sure we actually got xml and clean up after parsing */
+ SVN_ERR(svn_ra_serf__xml_context_done(ectx->xmlctx));
+ }
+
+ if (status && !SERF_BUCKET_READ_ERROR(status))
+ {
+ return svn_ra_serf__wrap_err(status, NULL);
+ }
+ }
+
+ /* NOTREACHED */
+}
+
+
+svn_ra_serf__handler_t *
+svn_ra_serf__create_expat_handler(svn_ra_serf__session_t *session,
+ svn_ra_serf__xml_context_t *xmlctx,
+ const int *expected_status,
+ 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->expected_status = expected_status;
+ ectx->cleanup_pool = result_pool;
+
+ handler = svn_ra_serf__create_handler(session, result_pool);
+ handler->response_handler = expat_response_handler;
+ handler->response_baton = ectx;
+
+ ectx->handler = handler;
+
+ return handler;
+}