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;
93 /*file_pool is cleared after completion of each file. */
94 apr_pool_t *file_pool;
96 /* Are we done fetching this file? */
98 svn_ra_serf__list_t **done_list;
99 svn_ra_serf__list_t done_item;
101 /* callback to get an editor */
102 svn_ra_replay_revstart_callback_t revstart_func;
103 svn_ra_replay_revfinish_callback_t revfinish_func;
106 /* replay receiver function and baton */
107 const svn_delta_editor_t *editor;
110 /* Path and revision used to filter replayed changes. If
111 INCLUDE_PATH is non-NULL, REVISION is unnecessary and will not be
112 included in the replay REPORT. (Because the REPORT is being
113 aimed an HTTP v2 revision resource.) */
114 const char *include_path;
115 svn_revnum_t revision;
117 /* Information needed to create the replay report body */
118 svn_revnum_t low_water_mark;
119 svn_boolean_t send_deltas;
121 /* Target and revision to fetch revision properties on */
122 const char *revprop_target;
123 svn_revnum_t revprop_rev;
125 /* Revision properties for this revision. */
126 apr_hash_t *revs_props;
129 /* Keep a reference to the XML parser ctx to report any errors. */
130 svn_ra_serf__xml_parser_t *parser_ctx;
132 /* Handlers for the PROPFIND and REPORT for the current revision. */
133 svn_ra_serf__handler_t *propfind_handler;
134 svn_ra_serf__handler_t *report_handler;
140 push_state(svn_ra_serf__xml_parser_t *parser,
141 replay_context_t *replay_ctx,
142 replay_state_e state)
144 svn_ra_serf__xml_push_state(parser, state);
146 if (state == OPEN_DIR || state == ADD_DIR ||
147 state == OPEN_FILE || state == ADD_FILE)
151 info = apr_palloc(replay_ctx->dst_rev_pool, sizeof(*info));
153 info->pool = replay_ctx->dst_rev_pool;
154 info->parent = parser->state->private;
158 parser->state->private = info;
160 else if (state == CHANGE_PROP)
164 info = apr_pcalloc(replay_ctx->dst_rev_pool, sizeof(*info));
166 info->pool = replay_ctx->dst_rev_pool;
167 info->parent = parser->state->private;
168 info->prop_value = svn_stringbuf_create_empty(info->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);
197 ctx->file_pool = svn_pool_create(ctx->dst_rev_pool);
199 SVN_ERR(svn_ra_serf__select_revprops(&ctx->props,
206 if (ctx->revstart_func)
208 SVN_ERR(ctx->revstart_func(ctx->revision, ctx->replay_baton,
209 &ctx->editor, &ctx->editor_baton,
214 else if (state == REPORT &&
215 strcmp(name.name, "target-revision") == 0)
219 rev = svn_xml_get_attr_value("rev", attrs);
222 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
223 _("Missing revision attr in target-revision element"));
226 SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
230 else if (state == REPORT &&
231 strcmp(name.name, "open-root") == 0)
236 rev = svn_xml_get_attr_value("rev", attrs);
240 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
241 _("Missing revision attr in open-root element"));
244 info = push_state(parser, ctx, OPEN_DIR);
246 SVN_ERR(ctx->editor->open_root(ctx->editor_baton,
251 else if ((state == OPEN_DIR || state == ADD_DIR) &&
252 strcmp(name.name, "delete-entry") == 0)
254 const char *file_name, *rev;
257 file_name = svn_xml_get_attr_value("name", attrs);
260 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
261 _("Missing name attr in delete-entry element"));
263 rev = svn_xml_get_attr_value("rev", attrs);
266 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
267 _("Missing revision attr in delete-entry element"));
270 info = push_state(parser, ctx, DELETE_ENTRY);
272 SVN_ERR(ctx->editor->delete_entry(file_name, SVN_STR_TO_REV(rev),
273 info->baton, scratch_pool));
275 svn_ra_serf__xml_pop_state(parser);
277 else if ((state == OPEN_DIR || state == ADD_DIR) &&
278 strcmp(name.name, "open-directory") == 0)
280 const char *rev, *dir_name;
283 dir_name = svn_xml_get_attr_value("name", attrs);
286 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
287 _("Missing name attr in open-directory element"));
289 rev = svn_xml_get_attr_value("rev", attrs);
292 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
293 _("Missing revision attr in open-directory element"));
296 info = push_state(parser, ctx, OPEN_DIR);
298 SVN_ERR(ctx->editor->open_directory(dir_name, info->parent->baton,
300 ctx->dst_rev_pool, &info->baton));
302 else if ((state == OPEN_DIR || state == ADD_DIR) &&
303 strcmp(name.name, "add-directory") == 0)
305 const char *dir_name, *copyfrom, *copyrev;
309 dir_name = svn_xml_get_attr_value("name", attrs);
312 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
313 _("Missing name attr in add-directory element"));
315 copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
316 copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
319 rev = SVN_STR_TO_REV(copyrev);
321 rev = SVN_INVALID_REVNUM;
323 info = push_state(parser, ctx, ADD_DIR);
325 SVN_ERR(ctx->editor->add_directory(dir_name, info->parent->baton,
327 ctx->dst_rev_pool, &info->baton));
329 else if ((state == OPEN_DIR || state == ADD_DIR) &&
330 strcmp(name.name, "close-directory") == 0)
332 replay_info_t *info = parser->state->private;
334 SVN_ERR(ctx->editor->close_directory(info->baton, scratch_pool));
336 svn_ra_serf__xml_pop_state(parser);
338 else if ((state == OPEN_DIR || state == ADD_DIR) &&
339 strcmp(name.name, "open-file") == 0)
341 const char *file_name, *rev;
344 svn_pool_clear(ctx->file_pool);
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 ctx->file_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 svn_pool_clear(ctx->file_pool);
372 file_name = svn_xml_get_attr_value("name", attrs);
375 return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
376 _("Missing name attr in add-file element"));
378 copyfrom = svn_xml_get_attr_value("copyfrom-path", attrs);
379 copyrev = svn_xml_get_attr_value("copyfrom-rev", attrs);
381 info = push_state(parser, ctx, ADD_FILE);
384 rev = SVN_STR_TO_REV(copyrev);
386 rev = SVN_INVALID_REVNUM;
388 SVN_ERR(ctx->editor->add_file(file_name, info->parent->baton,
390 ctx->file_pool, &info->baton));
392 else if ((state == OPEN_FILE || state == ADD_FILE) &&
393 strcmp(name.name, "apply-textdelta") == 0)
395 const char *checksum;
397 svn_txdelta_window_handler_t textdelta;
398 void *textdelta_baton;
399 svn_stream_t *delta_stream;
401 info = push_state(parser, ctx, APPLY_TEXTDELTA);
403 checksum = svn_xml_get_attr_value("checksum", attrs);
406 checksum = apr_pstrdup(info->pool, checksum);
409 SVN_ERR(ctx->editor->apply_textdelta(info->baton, checksum,
414 delta_stream = svn_txdelta_parse_svndiff(textdelta, textdelta_baton,
416 info->stream = svn_base64_decode(delta_stream, info->pool);
418 else if ((state == OPEN_FILE || state == ADD_FILE) &&
419 strcmp(name.name, "close-file") == 0)
421 replay_info_t *info = parser->state->private;
422 const char *checksum;
424 checksum = svn_xml_get_attr_value("checksum", attrs);
426 SVN_ERR(ctx->editor->close_file(info->baton, checksum, scratch_pool));
428 svn_ra_serf__xml_pop_state(parser);
430 else if (((state == OPEN_FILE || state == ADD_FILE) &&
431 strcmp(name.name, "change-file-prop") == 0) ||
432 ((state == OPEN_DIR || state == ADD_DIR) &&
433 strcmp(name.name, "change-dir-prop") == 0))
435 const char *prop_name;
438 prop_name = svn_xml_get_attr_value("name", attrs);
441 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
442 _("Missing name attr in %s element"),
446 info = push_state(parser, ctx, CHANGE_PROP);
449 if (svn_xml_get_attr_value("del", attrs))
450 info->del_prop = TRUE;
452 info->del_prop = FALSE;
454 if (state == OPEN_FILE || state == ADD_FILE)
456 info->name = apr_pstrdup(ctx->file_pool, prop_name);
457 info->change = ctx->editor->change_file_prop;
461 info->name = apr_pstrdup(ctx->dst_rev_pool, prop_name);
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 if (strcmp(name.name, "change-file-prop") == 0)
542 prop_val = svn_base64_decode_string(morph, ctx->file_pool);
544 prop_val = svn_base64_decode_string(morph, ctx->dst_rev_pool);
547 SVN_ERR(info->change(info->parent->baton, info->name, prop_val,
548 info->parent->pool));
549 svn_ra_serf__xml_pop_state(parser);
556 cdata_replay(svn_ra_serf__xml_parser_t *parser,
559 apr_pool_t *scratch_pool)
561 replay_context_t *replay_ctx = parser->user_data;
562 replay_state_e state;
564 UNUSED_CTX(replay_ctx);
566 state = parser->state->current_state;
568 if (state == APPLY_TEXTDELTA)
570 replay_info_t *info = parser->state->private;
575 SVN_ERR(svn_stream_write(info->stream, data, &written));
578 return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
579 _("Error writing stream: unexpected EOF"));
581 else if (state == CHANGE_PROP)
583 prop_info_t *info = parser->state->private;
585 svn_stringbuf_appendbytes(info->prop_value, data, len);
592 create_replay_body(serf_bucket_t **bkt,
594 serf_bucket_alloc_t *alloc,
597 replay_context_t *ctx = baton;
598 serf_bucket_t *body_bkt;
600 body_bkt = serf_bucket_aggregate_create(alloc);
602 svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
604 "xmlns:S", SVN_XML_NAMESPACE,
607 /* If we have a non-NULL include path, we add it to the body and
608 omit the revision; otherwise, the reverse. */
609 if (ctx->include_path)
611 svn_ra_serf__add_tag_buckets(body_bkt,
618 svn_ra_serf__add_tag_buckets(body_bkt,
620 apr_ltoa(ctx->src_rev_pool, ctx->revision),
623 svn_ra_serf__add_tag_buckets(body_bkt,
625 apr_ltoa(ctx->src_rev_pool, ctx->low_water_mark),
628 svn_ra_serf__add_tag_buckets(body_bkt,
630 apr_ltoa(ctx->src_rev_pool, ctx->send_deltas),
633 svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "S:replay-report");
640 svn_ra_serf__replay(svn_ra_session_t *ra_session,
641 svn_revnum_t revision,
642 svn_revnum_t low_water_mark,
643 svn_boolean_t send_deltas,
644 const svn_delta_editor_t *editor,
648 replay_context_t *replay_ctx;
649 svn_ra_serf__session_t *session = ra_session->priv;
650 svn_ra_serf__handler_t *handler;
651 svn_ra_serf__xml_parser_t *parser_ctx;
653 const char *report_target;
655 SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
657 replay_ctx = apr_pcalloc(pool, sizeof(*replay_ctx));
658 replay_ctx->src_rev_pool = pool;
659 replay_ctx->editor = editor;
660 replay_ctx->editor_baton = edit_baton;
661 replay_ctx->done = FALSE;
662 replay_ctx->revision = revision;
663 replay_ctx->low_water_mark = low_water_mark;
664 replay_ctx->send_deltas = send_deltas;
665 replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
667 handler = apr_pcalloc(pool, sizeof(*handler));
669 handler->handler_pool = pool;
670 handler->method = "REPORT";
671 handler->path = session->session_url.path;
672 handler->body_delegate = create_replay_body;
673 handler->body_delegate_baton = replay_ctx;
674 handler->body_type = "text/xml";
675 handler->conn = session->conns[0];
676 handler->session = session;
678 parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
680 parser_ctx->pool = pool;
681 parser_ctx->user_data = replay_ctx;
682 parser_ctx->start = start_replay;
683 parser_ctx->end = end_replay;
684 parser_ctx->cdata = cdata_replay;
685 parser_ctx->done = &replay_ctx->done;
687 handler->response_handler = svn_ra_serf__handle_xml_parser;
688 handler->response_baton = parser_ctx;
690 /* This is only needed to handle errors during XML parsing. */
691 replay_ctx->parser_ctx = parser_ctx;
692 replay_ctx->report_handler = handler; /* unused */
694 svn_ra_serf__request_create(handler);
696 err = svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool);
698 SVN_ERR(svn_error_compose_create(
699 svn_ra_serf__error_on_status(handler->sline,
707 /* The maximum number of outstanding requests at any time. When this
708 * number is reached, ra_serf will stop sending requests until
709 * responses on the previous requests are received and handled.
711 * Some observations about serf which lead us to the current value.
712 * ----------------------------------------------------------------
714 * We aim to keep serf's outgoing queue filled with enough requests so
715 * the network bandwidth and server capacity is used
716 * optimally. Originally we used 5 as the max. number of outstanding
717 * requests, but this turned out to be too low.
719 * Serf doesn't exit out of the svn_ra_serf__context_run_wait loop as long as
720 * it has data to send or receive. With small responses (revs of a few
721 * kB), serf doesn't come out of this loop at all. So with
722 * MAX_OUTSTANDING_REQUESTS set to a low number, there's a big chance
723 * that serf handles those requests completely in its internal loop,
724 * and only then gives us a chance to create new requests. This
725 * results in hiccups, slowing down the whole process.
727 * With a larger MAX_OUTSTANDING_REQUESTS, like 100 or more, there's
728 * more chance that serf can come out of its internal loop so we can
729 * replenish the outgoing request queue. There's no real disadvantage
730 * of using a large number here, besides the memory used to store the
731 * message, parser and handler objects (approx. 250 bytes).
733 * In my test setup peak performance was reached at max. 30-35
734 * requests. So I added a small margin and chose 50.
736 #define MAX_OUTSTANDING_REQUESTS 50
739 svn_ra_serf__replay_range(svn_ra_session_t *ra_session,
740 svn_revnum_t start_revision,
741 svn_revnum_t end_revision,
742 svn_revnum_t low_water_mark,
743 svn_boolean_t send_deltas,
744 svn_ra_replay_revstart_callback_t revstart_func,
745 svn_ra_replay_revfinish_callback_t revfinish_func,
749 svn_ra_serf__session_t *session = ra_session->priv;
750 svn_revnum_t rev = start_revision;
751 const char *report_target;
752 int active_reports = 0;
753 const char *include_path;
755 SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
757 /* Prior to 1.8, mod_dav_svn expect to get replay REPORT requests
758 aimed at the session URL. But that's incorrect -- these reports
759 aren't about specific resources -- they are above revisions. The
760 path-based filtering offered by this API is just that: a filter
761 applied to the full set of changes made in the revision. As
762 such, the correct target for these REPORT requests is the "me
763 resource" (or, pre-http-v2, the default VCC).
765 Our server should have told us if it supported this protocol
766 correction. If so, we aimed our report at the correct resource
767 and include the filtering path as metadata within the report
768 body. Otherwise, we fall back to the pre-1.8 behavior and just
772 http://subversion.tigris.org/issues/show_bug.cgi?id=4287
774 if (session->supports_rev_rsrc_replay)
776 SVN_ERR(svn_ra_serf__get_relative_path(&include_path,
777 session->session_url.path,
778 session, session->conns[0],
786 while (active_reports || rev <= end_revision)
788 svn_ra_serf__list_t *done_list;
789 svn_ra_serf__list_t *done_reports = NULL;
790 replay_context_t *replay_ctx;
792 if (session->cancel_func)
793 SVN_ERR(session->cancel_func(session->cancel_baton));
795 /* Send pending requests, if any. Limit the number of outstanding
796 requests to MAX_OUTSTANDING_REQUESTS. */
797 if (rev <= end_revision && active_reports < MAX_OUTSTANDING_REQUESTS)
799 svn_ra_serf__handler_t *handler;
800 svn_ra_serf__xml_parser_t *parser_ctx;
801 apr_pool_t *ctx_pool = svn_pool_create(pool);
802 const char *replay_target;
804 replay_ctx = apr_pcalloc(ctx_pool, sizeof(*replay_ctx));
805 replay_ctx->src_rev_pool = ctx_pool;
806 replay_ctx->revstart_func = revstart_func;
807 replay_ctx->revfinish_func = revfinish_func;
808 replay_ctx->replay_baton = replay_baton;
809 replay_ctx->done = FALSE;
810 replay_ctx->include_path = include_path;
811 replay_ctx->revision = rev;
812 replay_ctx->low_water_mark = low_water_mark;
813 replay_ctx->send_deltas = send_deltas;
814 replay_ctx->done_item.data = replay_ctx;
816 /* Request all properties of a certain revision. */
817 replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
819 if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
821 replay_ctx->revprop_target = apr_psprintf(pool, "%s/%ld",
822 session->rev_stub, rev);
823 replay_ctx->revprop_rev = SVN_INVALID_REVNUM;
827 replay_ctx->revprop_target = report_target;
828 replay_ctx->revprop_rev = rev;
831 SVN_ERR(svn_ra_serf__deliver_props(&replay_ctx->propfind_handler,
832 replay_ctx->revs_props, session,
834 replay_ctx->revprop_target,
835 replay_ctx->revprop_rev,
838 replay_ctx->src_rev_pool));
840 /* Spin up the serf request for the PROPFIND. */
841 svn_ra_serf__request_create(replay_ctx->propfind_handler);
843 /* Send the replay REPORT request. */
844 if (session->supports_rev_rsrc_replay)
846 replay_target = apr_psprintf(pool, "%s/%ld",
847 session->rev_stub, rev);
851 replay_target = session->session_url.path;
854 handler = apr_pcalloc(replay_ctx->src_rev_pool, sizeof(*handler));
856 handler->handler_pool = replay_ctx->src_rev_pool;
857 handler->method = "REPORT";
858 handler->path = replay_target;
859 handler->body_delegate = create_replay_body;
860 handler->body_delegate_baton = replay_ctx;
861 handler->conn = session->conns[0];
862 handler->session = session;
864 parser_ctx = apr_pcalloc(replay_ctx->src_rev_pool,
865 sizeof(*parser_ctx));
867 /* Setup the XML parser context.
868 Because we have not one but a list of requests, the 'done' property
869 on the replay_ctx is not of much use. Instead, use 'done_list'.
870 On each handled response (succesfully or not), the parser will add
871 done_item to done_list, so by keeping track of the state of
872 done_list we know how many requests have been handled completely.
874 parser_ctx->pool = replay_ctx->src_rev_pool;
875 parser_ctx->user_data = replay_ctx;
876 parser_ctx->start = start_replay;
877 parser_ctx->end = end_replay;
878 parser_ctx->cdata = cdata_replay;
879 parser_ctx->done = &replay_ctx->done;
880 parser_ctx->done_list = &done_reports;
881 parser_ctx->done_item = &replay_ctx->done_item;
882 handler->response_handler = svn_ra_serf__handle_xml_parser;
883 handler->response_baton = parser_ctx;
884 replay_ctx->report_handler = handler;
886 /* This is only needed to handle errors during XML parsing. */
887 replay_ctx->parser_ctx = parser_ctx;
889 svn_ra_serf__request_create(handler);
895 /* Run the serf loop. */
896 SVN_ERR(svn_ra_serf__context_run_wait(&replay_ctx->done, session, pool));
898 /* Substract the number of completely handled responses from our
899 total nr. of open requests', so we'll know when to stop this loop.
900 Since the message is completely handled, we can destroy its pool. */
901 done_list = done_reports;
904 replay_context_t *ctx = (replay_context_t *)done_list->data;
905 svn_ra_serf__handler_t *done_handler = ctx->report_handler;
907 done_list = done_list->next;
908 SVN_ERR(svn_ra_serf__error_on_status(done_handler->sline,
910 done_handler->location));
911 svn_pool_destroy(ctx->src_rev_pool);
920 #undef MAX_OUTSTANDING_REQUESTS