2 * replay.c : entry point for replay RA functions for ra_serf
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
29 #include "svn_pools.h"
33 #include "../libsvn_ra/ra_loader.h"
34 #include "svn_config.h"
35 #include "svn_delta.h"
36 #include "svn_base64.h"
38 #include "svn_private_config.h"
40 #include "private/svn_string_private.h"
46 * This enum represents the current state of our XML parsing.
48 typedef enum replay_state_e {
60 typedef struct replay_info_t replay_info_t;
62 struct replay_info_t {
68 replay_info_t *parent;
72 (*change_prop_t)(void *baton,
74 const svn_string_t *value,
77 typedef struct prop_info_t {
83 svn_boolean_t del_prop;
85 svn_stringbuf_t *prop_value;
87 replay_info_t *parent;
90 typedef struct replay_context_t {
91 apr_pool_t *src_rev_pool;
92 apr_pool_t *dst_rev_pool;
94 /* Are we done fetching this file? */
96 svn_ra_serf__list_t **done_list;
97 svn_ra_serf__list_t done_item;
99 /* callback to get an editor */
100 svn_ra_replay_revstart_callback_t revstart_func;
101 svn_ra_replay_revfinish_callback_t revfinish_func;
104 /* replay receiver function and baton */
105 const svn_delta_editor_t *editor;
108 /* Path and revision used to filter replayed changes. If
109 INCLUDE_PATH is non-NULL, REVISION is unnecessary and will not be
110 included in the replay REPORT. (Because the REPORT is being
111 aimed an HTTP v2 revision resource.) */
112 const char *include_path;
113 svn_revnum_t revision;
115 /* Information needed to create the replay report body */
116 svn_revnum_t low_water_mark;
117 svn_boolean_t send_deltas;
119 /* Target and revision to fetch revision properties on */
120 const char *revprop_target;
121 svn_revnum_t revprop_rev;
123 /* Revision properties for this revision. */
124 apr_hash_t *revs_props;
127 /* Keep a reference to the XML parser ctx to report any errors. */
128 svn_ra_serf__xml_parser_t *parser_ctx;
130 /* Handlers for the PROPFIND and REPORT for the current revision. */
131 svn_ra_serf__handler_t *propfind_handler;
132 svn_ra_serf__handler_t *report_handler;
138 push_state(svn_ra_serf__xml_parser_t *parser,
139 replay_context_t *replay_ctx,
140 replay_state_e state)
142 svn_ra_serf__xml_push_state(parser, state);
144 if (state == OPEN_DIR || state == ADD_DIR ||
145 state == OPEN_FILE || state == ADD_FILE)
148 apr_pool_t *pool = svn_pool_create(replay_ctx->dst_rev_pool);
150 info = apr_palloc(pool, sizeof(*info));
153 info->parent = parser->state->private;
157 parser->state->private = info;
159 else if (state == CHANGE_PROP)
162 apr_pool_t *pool = svn_pool_create(replay_ctx->dst_rev_pool);
164 info = apr_pcalloc(pool, sizeof(*info));
167 info->parent = parser->state->private;
168 info->prop_value = svn_stringbuf_create_empty(pool);
170 parser->state->private = info;
173 return parser->state->private;
177 start_replay(svn_ra_serf__xml_parser_t *parser,
178 svn_ra_serf__dav_props_t name,
180 apr_pool_t *scratch_pool)
182 replay_context_t *ctx = parser->user_data;
183 replay_state_e state;
185 state = parser->state->current_state;
188 strcmp(name.name, "editor-report") == 0)
190 push_state(parser, ctx, REPORT);
192 /* Before we can continue, we need the revision properties. */
193 SVN_ERR_ASSERT(!ctx->propfind_handler || ctx->propfind_handler->done);
195 /* Create a pool for the commit editor. */
196 ctx->dst_rev_pool = svn_pool_create(ctx->src_rev_pool);
198 SVN_ERR(svn_ra_serf__select_revprops(&ctx->props,
205 if (ctx->revstart_func)
207 SVN_ERR(ctx->revstart_func(ctx->revision, ctx->replay_baton,
208 &ctx->editor, &ctx->editor_baton,
213 else if (state == REPORT &&
214 strcmp(name.name, "target-revision") == 0)
218 rev = svn_xml_get_attr_value("rev", attrs);
221 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
222 _("Missing revision attr in target-revision element"));
225 SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
229 else if (state == REPORT &&
230 strcmp(name.name, "open-root") == 0)
235 rev = svn_xml_get_attr_value("rev", attrs);
239 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
240 _("Missing revision attr in open-root element"));
243 info = push_state(parser, ctx, OPEN_DIR);
245 SVN_ERR(ctx->editor->open_root(ctx->editor_baton,
250 else if ((state == OPEN_DIR || state == ADD_DIR) &&
251 strcmp(name.name, "delete-entry") == 0)
253 const char *file_name, *rev;
256 file_name = svn_xml_get_attr_value("name", attrs);
259 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
260 _("Missing name attr in delete-entry element"));
262 rev = svn_xml_get_attr_value("rev", attrs);
265 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
266 _("Missing revision attr in delete-entry element"));
269 info = push_state(parser, ctx, DELETE_ENTRY);
271 SVN_ERR(ctx->editor->delete_entry(file_name, SVN_STR_TO_REV(rev),
272 info->baton, scratch_pool));
274 svn_ra_serf__xml_pop_state(parser);
276 else if ((state == OPEN_DIR || state == ADD_DIR) &&
277 strcmp(name.name, "open-directory") == 0)
279 const char *rev, *dir_name;
282 dir_name = svn_xml_get_attr_value("name", attrs);
285 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
286 _("Missing name attr in open-directory element"));
288 rev = svn_xml_get_attr_value("rev", attrs);
291 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
292 _("Missing revision attr in open-directory element"));
295 info = push_state(parser, ctx, OPEN_DIR);
297 SVN_ERR(ctx->editor->open_directory(dir_name, info->parent->baton,
299 ctx->dst_rev_pool, &info->baton));
301 else if ((state == OPEN_DIR || state == ADD_DIR) &&
302 strcmp(name.name, "add-directory") == 0)
304 const char *dir_name, *copyfrom, *copyrev;
308 dir_name = svn_xml_get_attr_value("name", attrs);
311 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
312 _("Missing name attr in add-directory element"));
314 copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
315 copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
318 rev = SVN_STR_TO_REV(copyrev);
320 rev = SVN_INVALID_REVNUM;
322 info = push_state(parser, ctx, ADD_DIR);
324 SVN_ERR(ctx->editor->add_directory(dir_name, info->parent->baton,
326 ctx->dst_rev_pool, &info->baton));
328 else if ((state == OPEN_DIR || state == ADD_DIR) &&
329 strcmp(name.name, "close-directory") == 0)
331 replay_info_t *info = parser->state->private;
333 SVN_ERR(ctx->editor->close_directory(info->baton, scratch_pool));
335 svn_ra_serf__xml_pop_state(parser);
337 svn_pool_destroy(info->pool);
339 else if ((state == OPEN_DIR || state == ADD_DIR) &&
340 strcmp(name.name, "open-file") == 0)
342 const char *file_name, *rev;
345 file_name = svn_xml_get_attr_value("name", attrs);
348 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
349 _("Missing name attr in open-file element"));
351 rev = svn_xml_get_attr_value("rev", attrs);
354 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
355 _("Missing revision attr in open-file element"));
358 info = push_state(parser, ctx, OPEN_FILE);
360 SVN_ERR(ctx->editor->open_file(file_name, info->parent->baton,
362 info->pool, &info->baton));
364 else if ((state == OPEN_DIR || state == ADD_DIR) &&
365 strcmp(name.name, "add-file") == 0)
367 const char *file_name, *copyfrom, *copyrev;
371 file_name = svn_xml_get_attr_value("name", attrs);
374 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
375 _("Missing name attr in add-file element"));
377 copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
378 copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
380 info = push_state(parser, ctx, ADD_FILE);
383 rev = SVN_STR_TO_REV(copyrev);
385 rev = SVN_INVALID_REVNUM;
387 SVN_ERR(ctx->editor->add_file(file_name, info->parent->baton,
389 info->pool, &info->baton));
391 else if ((state == OPEN_FILE || state == ADD_FILE) &&
392 strcmp(name.name, "apply-textdelta") == 0)
394 const char *checksum;
396 svn_txdelta_window_handler_t textdelta;
397 void *textdelta_baton;
398 svn_stream_t *delta_stream;
400 info = push_state(parser, ctx, APPLY_TEXTDELTA);
402 checksum = svn_xml_get_attr_value("checksum", attrs);
405 checksum = apr_pstrdup(info->pool, checksum);
408 SVN_ERR(ctx->editor->apply_textdelta(info->baton, checksum,
413 delta_stream = svn_txdelta_parse_svndiff(textdelta, textdelta_baton,
415 info->stream = svn_base64_decode(delta_stream, info->pool);
417 else if ((state == OPEN_FILE || state == ADD_FILE) &&
418 strcmp(name.name, "close-file") == 0)
420 replay_info_t *info = parser->state->private;
421 const char *checksum;
423 checksum = svn_xml_get_attr_value("checksum", attrs);
425 SVN_ERR(ctx->editor->close_file(info->baton, checksum, scratch_pool));
427 svn_ra_serf__xml_pop_state(parser);
429 svn_pool_destroy(info->pool);
431 else if (((state == OPEN_FILE || state == ADD_FILE) &&
432 strcmp(name.name, "change-file-prop") == 0) ||
433 ((state == OPEN_DIR || state == ADD_DIR) &&
434 strcmp(name.name, "change-dir-prop") == 0))
436 const char *prop_name;
439 prop_name = svn_xml_get_attr_value("name", attrs);
442 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
443 _("Missing name attr in %s element"),
447 info = push_state(parser, ctx, CHANGE_PROP);
450 if (svn_xml_get_attr_value("del", attrs))
451 info->del_prop = TRUE;
453 info->del_prop = FALSE;
455 info->name = apr_pstrdup(info->pool, prop_name);
456 if (state == OPEN_FILE || state == ADD_FILE)
458 info->change = ctx->editor->change_file_prop;
462 info->change = ctx->editor->change_dir_prop;
471 end_replay(svn_ra_serf__xml_parser_t *parser,
472 svn_ra_serf__dav_props_t name,
473 apr_pool_t *scratch_pool)
475 replay_context_t *ctx = parser->user_data;
476 replay_state_e state;
478 state = parser->state->current_state;
480 if (state == REPORT &&
481 strcmp(name.name, "editor-report") == 0)
483 svn_ra_serf__xml_pop_state(parser);
484 if (ctx->revfinish_func)
486 SVN_ERR(ctx->revfinish_func(ctx->revision, ctx->replay_baton,
487 ctx->editor, ctx->editor_baton,
491 svn_pool_destroy(ctx->dst_rev_pool);
493 else if (state == OPEN_DIR && strcmp(name.name, "open-directory") == 0)
495 /* Don't do anything. */
497 else if (state == ADD_DIR && strcmp(name.name, "add-directory") == 0)
499 /* Don't do anything. */
501 else if (state == OPEN_FILE && strcmp(name.name, "open-file") == 0)
503 /* Don't do anything. */
505 else if (state == ADD_FILE && strcmp(name.name, "add-file") == 0)
507 /* Don't do anything. */
509 else if ((state == OPEN_FILE || state == ADD_FILE) &&
510 strcmp(name.name, "close-file") == 0)
512 /* Don't do anything. */
514 else if ((state == APPLY_TEXTDELTA) &&
515 strcmp(name.name, "apply-textdelta") == 0)
517 replay_info_t *info = parser->state->private;
518 SVN_ERR(svn_stream_close(info->stream));
519 svn_ra_serf__xml_pop_state(parser);
521 else if (state == CHANGE_PROP &&
522 (strcmp(name.name, "change-file-prop") == 0 ||
523 strcmp(name.name, "change-dir-prop") == 0))
525 prop_info_t *info = parser->state->private;
526 const svn_string_t *prop_val;
534 const svn_string_t *morph;
536 morph = svn_stringbuf__morph_into_string(info->prop_value);
538 info->prop_value = NULL; /* morph killed the stringbuf. */
541 prop_val = svn_base64_decode_string(morph, info->pool);
544 SVN_ERR(info->change(info->parent->baton, info->name, prop_val,
545 info->parent->pool));
546 svn_ra_serf__xml_pop_state(parser);
548 svn_pool_destroy(info->pool);
555 cdata_replay(svn_ra_serf__xml_parser_t *parser,
558 apr_pool_t *scratch_pool)
560 replay_context_t *replay_ctx = parser->user_data;
561 replay_state_e state;
563 UNUSED_CTX(replay_ctx);
565 state = parser->state->current_state;
567 if (state == APPLY_TEXTDELTA)
569 replay_info_t *info = parser->state->private;
574 SVN_ERR(svn_stream_write(info->stream, data, &written));
577 return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
578 _("Error writing stream: unexpected EOF"));
580 else if (state == CHANGE_PROP)
582 prop_info_t *info = parser->state->private;
584 svn_stringbuf_appendbytes(info->prop_value, data, len);
591 create_replay_body(serf_bucket_t **bkt,
593 serf_bucket_alloc_t *alloc,
596 replay_context_t *ctx = baton;
597 serf_bucket_t *body_bkt;
599 body_bkt = serf_bucket_aggregate_create(alloc);
601 svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
603 "xmlns:S", SVN_XML_NAMESPACE,
606 /* If we have a non-NULL include path, we add it to the body and
607 omit the revision; otherwise, the reverse. */
608 if (ctx->include_path)
610 svn_ra_serf__add_tag_buckets(body_bkt,
617 svn_ra_serf__add_tag_buckets(body_bkt,
619 apr_ltoa(ctx->src_rev_pool, ctx->revision),
622 svn_ra_serf__add_tag_buckets(body_bkt,
624 apr_ltoa(ctx->src_rev_pool, ctx->low_water_mark),
627 svn_ra_serf__add_tag_buckets(body_bkt,
629 apr_ltoa(ctx->src_rev_pool, ctx->send_deltas),
632 svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "S:replay-report");
639 svn_ra_serf__replay(svn_ra_session_t *ra_session,
640 svn_revnum_t revision,
641 svn_revnum_t low_water_mark,
642 svn_boolean_t send_deltas,
643 const svn_delta_editor_t *editor,
647 replay_context_t *replay_ctx;
648 svn_ra_serf__session_t *session = ra_session->priv;
649 svn_ra_serf__handler_t *handler;
650 svn_ra_serf__xml_parser_t *parser_ctx;
652 const char *report_target;
654 SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
656 replay_ctx = apr_pcalloc(pool, sizeof(*replay_ctx));
657 replay_ctx->src_rev_pool = pool;
658 replay_ctx->editor = editor;
659 replay_ctx->editor_baton = edit_baton;
660 replay_ctx->done = FALSE;
661 replay_ctx->revision = revision;
662 replay_ctx->low_water_mark = low_water_mark;
663 replay_ctx->send_deltas = send_deltas;
664 replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
666 handler = apr_pcalloc(pool, sizeof(*handler));
668 handler->handler_pool = pool;
669 handler->method = "REPORT";
670 handler->path = session->session_url.path;
671 handler->body_delegate = create_replay_body;
672 handler->body_delegate_baton = replay_ctx;
673 handler->body_type = "text/xml";
674 handler->conn = session->conns[0];
675 handler->session = session;
677 parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
679 parser_ctx->pool = pool;
680 parser_ctx->user_data = replay_ctx;
681 parser_ctx->start = start_replay;
682 parser_ctx->end = end_replay;
683 parser_ctx->cdata = cdata_replay;
684 parser_ctx->done = &replay_ctx->done;
686 handler->response_handler = svn_ra_serf__handle_xml_parser;
687 handler->response_baton = parser_ctx;
689 /* This is only needed to handle errors during XML parsing. */
690 replay_ctx->parser_ctx = parser_ctx;
691 replay_ctx->report_handler = handler; /* unused */
693 svn_ra_serf__request_create(handler);
695 err = svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool);
697 SVN_ERR(svn_error_compose_create(
698 svn_ra_serf__error_on_status(handler->sline,
706 /* The maximum number of outstanding requests at any time. When this
707 * number is reached, ra_serf will stop sending requests until
708 * responses on the previous requests are received and handled.
710 * Some observations about serf which lead us to the current value.
711 * ----------------------------------------------------------------
713 * We aim to keep serf's outgoing queue filled with enough requests so
714 * the network bandwidth and server capacity is used
715 * optimally. Originally we used 5 as the max. number of outstanding
716 * requests, but this turned out to be too low.
718 * Serf doesn't exit out of the svn_ra_serf__context_run_wait loop as long as
719 * it has data to send or receive. With small responses (revs of a few
720 * kB), serf doesn't come out of this loop at all. So with
721 * MAX_OUTSTANDING_REQUESTS set to a low number, there's a big chance
722 * that serf handles those requests completely in its internal loop,
723 * and only then gives us a chance to create new requests. This
724 * results in hiccups, slowing down the whole process.
726 * With a larger MAX_OUTSTANDING_REQUESTS, like 100 or more, there's
727 * more chance that serf can come out of its internal loop so we can
728 * replenish the outgoing request queue. There's no real disadvantage
729 * of using a large number here, besides the memory used to store the
730 * message, parser and handler objects (approx. 250 bytes).
732 * In my test setup peak performance was reached at max. 30-35
733 * requests. So I added a small margin and chose 50.
735 #define MAX_OUTSTANDING_REQUESTS 50
738 svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
739 svn_revnum_t start_revision,
740 svn_revnum_t end_revision,
741 svn_revnum_t low_water_mark,
742 svn_boolean_t send_deltas,
743 svn_ra_replay_revstart_callback_t revstart_func,
744 svn_ra_replay_revfinish_callback_t revfinish_func,
748 svn_ra_serf__session_t *session = ra_session->priv;
749 svn_revnum_t rev = start_revision;
750 const char *report_target;
751 int active_reports = 0;
752 const char *include_path;
754 SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
756 /* Prior to 1.8, mod_dav_svn expect to get replay REPORT requests
757 aimed at the session URL. But that's incorrect -- these reports
758 aren't about specific resources -- they are above revisions. The
759 path-based filtering offered by this API is just that: a filter
760 applied to the full set of changes made in the revision. As
761 such, the correct target for these REPORT requests is the "me
762 resource" (or, pre-http-v2, the default VCC).
764 Our server should have told us if it supported this protocol
765 correction. If so, we aimed our report at the correct resource
766 and include the filtering path as metadata within the report
767 body. Otherwise, we fall back to the pre-1.8 behavior and just
771 http://subversion.tigris.org/issues/show_bug.cgi?id=4287
773 if (session->supports_rev_rsrc_replay)
775 SVN_ERR(svn_ra_serf__get_relative_path(&include_path,
776 session->session_url.path,
777 session, session->conns[0],
785 while (active_reports || rev <= end_revision)
787 svn_ra_serf__list_t *done_list;
788 svn_ra_serf__list_t *done_reports = NULL;
789 replay_context_t *replay_ctx;
791 if (session->cancel_func)
792 SVN_ERR(session->cancel_func(session->cancel_baton));
794 /* Send pending requests, if any. Limit the number of outstanding
795 requests to MAX_OUTSTANDING_REQUESTS. */
796 if (rev <= end_revision && active_reports < MAX_OUTSTANDING_REQUESTS)
798 svn_ra_serf__handler_t *handler;
799 svn_ra_serf__xml_parser_t *parser_ctx;
800 apr_pool_t *ctx_pool = svn_pool_create(pool);
801 const char *replay_target;
803 replay_ctx = apr_pcalloc(ctx_pool, sizeof(*replay_ctx));
804 replay_ctx->src_rev_pool = ctx_pool;
805 replay_ctx->revstart_func = revstart_func;
806 replay_ctx->revfinish_func = revfinish_func;
807 replay_ctx->replay_baton = replay_baton;
808 replay_ctx->done = FALSE;
809 replay_ctx->include_path = include_path;
810 replay_ctx->revision = rev;
811 replay_ctx->low_water_mark = low_water_mark;
812 replay_ctx->send_deltas = send_deltas;
813 replay_ctx->done_item.data = replay_ctx;
815 /* Request all properties of a certain revision. */
816 replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
818 if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
820 replay_ctx->revprop_target = apr_psprintf(pool, "%s/%ld",
821 session->rev_stub, rev);
822 replay_ctx->revprop_rev = SVN_INVALID_REVNUM;
826 replay_ctx->revprop_target = report_target;
827 replay_ctx->revprop_rev = rev;
830 SVN_ERR(svn_ra_serf__deliver_props(&replay_ctx->propfind_handler,
831 replay_ctx->revs_props, session,
833 replay_ctx->revprop_target,
834 replay_ctx->revprop_rev,
837 replay_ctx->src_rev_pool));
839 /* Spin up the serf request for the PROPFIND. */
840 svn_ra_serf__request_create(replay_ctx->propfind_handler);
842 /* Send the replay REPORT request. */
843 if (session->supports_rev_rsrc_replay)
845 replay_target = apr_psprintf(pool, "%s/%ld",
846 session->rev_stub, rev);
850 replay_target = session->session_url.path;
853 handler = apr_pcalloc(replay_ctx->src_rev_pool, sizeof(*handler));
855 handler->handler_pool = replay_ctx->src_rev_pool;
856 handler->method = "REPORT";
857 handler->path = replay_target;
858 handler->body_delegate = create_replay_body;
859 handler->body_delegate_baton = replay_ctx;
860 handler->conn = session->conns[0];
861 handler->session = session;
863 parser_ctx = apr_pcalloc(replay_ctx->src_rev_pool,
864 sizeof(*parser_ctx));
866 /* Setup the XML parser context.
867 Because we have not one but a list of requests, the 'done' property
868 on the replay_ctx is not of much use. Instead, use 'done_list'.
869 On each handled response (succesfully or not), the parser will add
870 done_item to done_list, so by keeping track of the state of
871 done_list we know how many requests have been handled completely.
873 parser_ctx->pool = replay_ctx->src_rev_pool;
874 parser_ctx->user_data = replay_ctx;
875 parser_ctx->start = start_replay;
876 parser_ctx->end = end_replay;
877 parser_ctx->cdata = cdata_replay;
878 parser_ctx->done = &replay_ctx->done;
879 parser_ctx->done_list = &done_reports;
880 parser_ctx->done_item = &replay_ctx->done_item;
881 handler->response_handler = svn_ra_serf__handle_xml_parser;
882 handler->response_baton = parser_ctx;
883 replay_ctx->report_handler = handler;
885 /* This is only needed to handle errors during XML parsing. */
886 replay_ctx->parser_ctx = parser_ctx;
888 svn_ra_serf__request_create(handler);
894 /* Run the serf loop. */
895 SVN_ERR(svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool));
897 /* Substract the number of completely handled responses from our
898 total nr. of open requests', so we'll know when to stop this loop.
899 Since the message is completely handled, we can destroy its pool. */
900 done_list = done_reports;
903 replay_context_t *ctx = (replay_context_t *)done_list->data;
904 svn_ra_serf__handler_t *done_handler = ctx->report_handler;
906 done_list = done_list->next;
907 SVN_ERR(svn_ra_serf__error_on_status(done_handler->sline,
909 done_handler->location));
910 svn_pool_destroy(ctx->src_rev_pool);
919 #undef MAX_OUTSTANDING_REQUESTS