diff options
author | Peter Wemm <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 |
---|---|---|
committer | Peter Wemm <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 |
commit | 32547653cc5376642e1231fb644db99933ac8db4 (patch) | |
tree | 135691142dc0e75a5e5d97b5074d03436435b8e0 /subversion/libsvn_ra_serf/replay.c | |
download | src-32547653cc5376642e1231fb644db99933ac8db4.tar.gz src-32547653cc5376642e1231fb644db99933ac8db4.zip |
Import trimmed svn-1.8.0-rc3vendor/subversion/subversion-1.8.0-rc3
Notes
Notes:
svn path=/vendor/subversion/dist/; revision=251881
svn path=/vendor/subversion/subversion-1.8.0-rc3/; revision=251882; tag=vendor/subversion/subversion-1.8.0-rc3
Diffstat (limited to 'subversion/libsvn_ra_serf/replay.c')
-rw-r--r-- | subversion/libsvn_ra_serf/replay.c | 920 |
1 files changed, 920 insertions, 0 deletions
diff --git a/subversion/libsvn_ra_serf/replay.c b/subversion/libsvn_ra_serf/replay.c new file mode 100644 index 000000000000..1fcecf43f878 --- /dev/null +++ b/subversion/libsvn_ra_serf/replay.c @@ -0,0 +1,920 @@ +/* + * replay.c : entry point for replay 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_pools.h" +#include "svn_ra.h" +#include "svn_dav.h" +#include "svn_xml.h" +#include "../libsvn_ra/ra_loader.h" +#include "svn_config.h" +#include "svn_delta.h" +#include "svn_base64.h" +#include "svn_path.h" +#include "svn_private_config.h" + +#include "private/svn_string_private.h" + +#include "ra_serf.h" + + +/* + * 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 +} replay_state_e; + +typedef struct replay_info_t replay_info_t; + +struct replay_info_t { + apr_pool_t *pool; + + void *baton; + svn_stream_t *stream; + + replay_info_t *parent; +}; + +typedef svn_error_t * +(*change_prop_t)(void *baton, + const char *name, + const svn_string_t *value, + apr_pool_t *pool); + +typedef struct prop_info_t { + apr_pool_t *pool; + + change_prop_t change; + + const char *name; + svn_boolean_t del_prop; + + svn_stringbuf_t *prop_value; + + replay_info_t *parent; +} prop_info_t; + +typedef struct replay_context_t { + apr_pool_t *src_rev_pool; + apr_pool_t *dst_rev_pool; + /*file_pool is cleared after completion of each file. */ + apr_pool_t *file_pool; + + /* Are we done fetching this file? */ + svn_boolean_t done; + svn_ra_serf__list_t **done_list; + svn_ra_serf__list_t done_item; + + /* callback to get an editor */ + svn_ra_replay_revstart_callback_t revstart_func; + svn_ra_replay_revfinish_callback_t revfinish_func; + void *replay_baton; + + /* replay receiver function and baton */ + const svn_delta_editor_t *editor; + void *editor_baton; + + /* Path and revision used to filter replayed changes. If + INCLUDE_PATH is non-NULL, REVISION is unnecessary and will not be + included in the replay REPORT. (Because the REPORT is being + aimed an HTTP v2 revision resource.) */ + const char *include_path; + svn_revnum_t revision; + + /* Information needed to create the replay report body */ + svn_revnum_t low_water_mark; + svn_boolean_t send_deltas; + + /* Target and revision to fetch revision properties on */ + const char *revprop_target; + 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; + + /* 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; + + info = apr_palloc(replay_ctx->dst_rev_pool, sizeof(*info)); + + info->pool = replay_ctx->dst_rev_pool; + info->parent = parser->state->private; + info->baton = NULL; + info->stream = NULL; + + parser->state->private = info; + } + else if (state == CHANGE_PROP) + { + prop_info_t *info; + + info = apr_pcalloc(replay_ctx->dst_rev_pool, sizeof(*info)); + + info->pool = replay_ctx->dst_rev_pool; + info->parent = parser->state->private; + info->prop_value = svn_stringbuf_create_empty(info->pool); + + parser->state->private = info; + } + + return parser->state->private; +} + +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_context_t *ctx = parser->user_data; + replay_state_e state; + + state = parser->state->current_state; + + if (state == NONE && + strcmp(name.name, "editor-report") == 0) + { + 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); + ctx->file_pool = svn_pool_create(ctx->dst_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)); + + 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)); + } + } + else if (state == REPORT && + strcmp(name.name, "target-revision") == 0) + { + 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)); + } + 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")); + } + + info = push_state(parser, ctx, OPEN_DIR); + + 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) + { + const char *file_name, *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 delete-entry 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 delete-entry element")); + } + + 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); + } + else if ((state == OPEN_DIR || state == ADD_DIR) && + strcmp(name.name, "open-directory") == 0) + { + const char *rev, *dir_name; + replay_info_t *info; + + 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")); + } + + info = push_state(parser, ctx, OPEN_DIR); + + SVN_ERR(ctx->editor->open_directory(dir_name, info->parent->baton, + SVN_STR_TO_REV(rev), + ctx->dst_rev_pool, &info->baton)); + } + else if ((state == OPEN_DIR || state == ADD_DIR) && + strcmp(name.name, "add-directory") == 0) + { + 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) + { + return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + _("Missing name attr in add-directory element")); + } + copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs); + copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs); + + if (copyrev) + rev = SVN_STR_TO_REV(copyrev); + 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); + } + else if ((state == OPEN_DIR || state == ADD_DIR) && + strcmp(name.name, "open-file") == 0) + { + const char *file_name, *rev; + replay_info_t *info; + + svn_pool_clear(ctx->file_pool); + 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 open-file 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-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), + ctx->file_pool, &info->baton)); + } + else if ((state == OPEN_DIR || state == ADD_DIR) && + strcmp(name.name, "add-file") == 0) + { + const char *file_name, *copyfrom, *copyrev; + svn_revnum_t rev; + replay_info_t *info; + + svn_pool_clear(ctx->file_pool); + 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); + + if (copyrev) + rev = SVN_STR_TO_REV(copyrev); + else + rev = SVN_INVALID_REVNUM; + + SVN_ERR(ctx->editor->add_file(file_name, info->parent->baton, + copyfrom, rev, + ctx->file_pool, &info->baton)); + } + else if ((state == OPEN_FILE || state == ADD_FILE) && + strcmp(name.name, "apply-textdelta") == 0) + { + 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); + } + + SVN_ERR(ctx->editor->apply_textdelta(info->baton, checksum, + ctx->file_pool, + &textdelta, + &textdelta_baton)); + + delta_stream = svn_txdelta_parse_svndiff(textdelta, textdelta_baton, + TRUE, info->pool); + info->stream = svn_base64_decode(delta_stream, info->pool); + } + else if ((state == OPEN_FILE || state == ADD_FILE) && + strcmp(name.name, "close-file") == 0) + { + 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); + } + 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)) + { + const char *prop_name; + prop_info_t *info; + + 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 (svn_xml_get_attr_value("del", attrs)) + info->del_prop = TRUE; + else + info->del_prop = FALSE; + + if (state == OPEN_FILE || state == ADD_FILE) + { + info->name = apr_pstrdup(ctx->file_pool, prop_name); + info->change = ctx->editor->change_file_prop; + } + else + { + info->name = apr_pstrdup(ctx->dst_rev_pool, prop_name); + 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_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)) + { + prop_info_t *info = parser->state->private; + const svn_string_t *prop_val; + + 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 (strcmp(name.name, "change-file-prop") == 0) + prop_val = svn_base64_decode_string(morph, ctx->file_pool); + else + prop_val = svn_base64_decode_string(morph, ctx->dst_rev_pool); + } + + SVN_ERR(info->change(info->parent->baton, info->name, prop_val, + info->parent->pool)); + svn_ra_serf__xml_pop_state(parser); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +cdata_replay(svn_ra_serf__xml_parser_t *parser, + 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); + + state = parser->state->current_state; + + if (state == APPLY_TEXTDELTA) + { + replay_info_t *info = parser->state->private; + apr_size_t written; + + written = len; + + SVN_ERR(svn_stream_write(info->stream, data, &written)); + + 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; + + svn_stringbuf_appendbytes(info->prop_value, data, len); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +create_replay_body(serf_bucket_t **bkt, + void *baton, + serf_bucket_alloc_t *alloc, + apr_pool_t *pool) +{ + replay_context_t *ctx = baton; + serf_bucket_t *body_bkt; + + body_bkt = serf_bucket_aggregate_create(alloc); + + svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, + "S:replay-report", + "xmlns:S", SVN_XML_NAMESPACE, + NULL); + + /* If we have a non-NULL include path, we add it to the body and + omit the revision; otherwise, the reverse. */ + if (ctx->include_path) + { + svn_ra_serf__add_tag_buckets(body_bkt, + "S:include-path", + ctx->include_path, + alloc); + } + else + { + svn_ra_serf__add_tag_buckets(body_bkt, + "S:revision", + apr_ltoa(ctx->src_rev_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), + alloc); + + svn_ra_serf__add_tag_buckets(body_bkt, + "S:send-deltas", + apr_ltoa(ctx->src_rev_pool, ctx->send_deltas), + alloc); + + svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "S:replay-report"); + + *bkt = body_bkt; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_serf__replay(svn_ra_session_t *ra_session, + svn_revnum_t revision, + svn_revnum_t low_water_mark, + svn_boolean_t send_deltas, + const svn_delta_editor_t *editor, + void *edit_baton, + apr_pool_t *pool) +{ + replay_context_t *replay_ctx; + 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; + const char *report_target; + + SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, 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); + + handler = apr_pcalloc(pool, sizeof(*handler)); + + 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_type = "text/xml"; + handler->conn = session->conns[0]; + handler->session = session; + + parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx)); + + 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; + + 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( + svn_ra_serf__error_on_status(handler->sline.code, + handler->path, + handler->location), + err)); + + return SVN_NO_ERROR; +} + +/* The maximum number of outstanding requests at any time. When this + * number is reached, ra_serf will stop sending requests until + * responses on the previous requests are received and handled. + * + * Some observations about serf which lead us to the current value. + * ---------------------------------------------------------------- + * + * We aim to keep serf's outgoing queue filled with enough requests so + * the network bandwidth and server capacity is used + * optimally. Originally we used 5 as the max. number of outstanding + * requests, but this turned out to be too low. + * + * Serf doesn't exit out of the svn_ra_serf__context_run_wait loop as long as + * it has data to send or receive. With small responses (revs of a few + * kB), serf doesn't come out of this loop at all. So with + * MAX_OUTSTANDING_REQUESTS set to a low number, there's a big chance + * that serf handles those requests completely in its internal loop, + * and only then gives us a chance to create new requests. This + * results in hiccups, slowing down the whole process. + * + * With a larger MAX_OUTSTANDING_REQUESTS, like 100 or more, there's + * more chance that serf can come out of its internal loop so we can + * replenish the outgoing request queue. There's no real disadvantage + * of using a large number here, besides the memory used to store the + * message, parser and handler objects (approx. 250 bytes). + * + * In my test setup peak performance was reached at max. 30-35 + * requests. So I added a small margin and chose 50. + */ +#define MAX_OUTSTANDING_REQUESTS 50 + +svn_error_t * +svn_ra_serf__replay_range(svn_ra_session_t *ra_session, + svn_revnum_t start_revision, + svn_revnum_t end_revision, + svn_revnum_t low_water_mark, + svn_boolean_t send_deltas, + svn_ra_replay_revstart_callback_t revstart_func, + svn_ra_replay_revfinish_callback_t revfinish_func, + void *replay_baton, + apr_pool_t *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_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, 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 + aren't about specific resources -- they are above revisions. The + path-based filtering offered by this API is just that: a filter + applied to the full set of changes made in the revision. As + such, the correct target for these REPORT requests is the "me + resource" (or, pre-http-v2, the default VCC). + + Our server should have told us if it supported this protocol + correction. If so, we aimed our report at the correct resource + and include the filtering path as metadata within the report + body. Otherwise, we fall back to the pre-1.8 behavior and just + wish for the best. + + See issue #4287: + http://subversion.tigris.org/issues/show_bug.cgi?id=4287 + */ + if (session->supports_rev_rsrc_replay) + { + SVN_ERR(svn_ra_serf__get_relative_path(&include_path, + session->session_url.path, + session, session->conns[0], + pool)); + } + else + { + include_path = NULL; + } + + 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)); + + /* Send pending requests, if any. Limit the number of outstanding + requests to MAX_OUTSTANDING_REQUESTS. */ + if (rev <= end_revision && active_reports < MAX_OUTSTANDING_REQUESTS) + { + svn_ra_serf__handler_t *handler; + svn_ra_serf__xml_parser_t *parser_ctx; + apr_pool_t *ctx_pool = svn_pool_create(pool); + 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; + + /* Request all properties of a certain revision. */ + replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_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; + } + else + { + replay_ctx->revprop_target = report_target; + replay_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)); + + /* Spin up the serf request for the PROPFIND. */ + svn_ra_serf__request_create(replay_ctx->propfind_handler); + + /* Send the replay REPORT request. */ + if (session->supports_rev_rsrc_replay) + { + replay_target = apr_psprintf(pool, "%s/%ld", + session->rev_stub, rev); + } + else + { + replay_target = session->session_url.path; + } + + handler = apr_pcalloc(replay_ctx->src_rev_pool, sizeof(*handler)); + + 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; + + svn_ra_serf__request_create(handler); + + rev++; + active_reports++; + } + + /* 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.code, + done_handler->path, + done_handler->location)); + svn_pool_destroy(ctx->src_rev_pool); + active_reports--; + } + + done_reports = NULL; + } + + return SVN_NO_ERROR; +} +#undef MAX_OUTSTANDING_REQUESTS |