Alien-SVN
view release on metacpan or search on metacpan
src/subversion/subversion/libsvn_ra_serf/update.c view on Meta::CPAN
FALSE, { "rev", NULL }, TRUE },
{ OPEN_DIR, S_, "open-directory", OPEN_DIR,
FALSE, { "rev", "name", NULL }, TRUE },
{ OPEN_DIR, S_, "add-directory", ADD_DIR,
FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
{ ADD_DIR, S_, "add-directory", ADD_DIR,
FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
{ OPEN_DIR, S_, "open-file", OPEN_FILE,
FALSE, { "rev", "name", NULL }, TRUE },
{ OPEN_DIR, S_, "add-file", ADD_FILE,
FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
{ ADD_DIR, S_, "add-file", ADD_FILE,
FALSE, { "rev", "name", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
{ OPEN_DIR, S_, "delete-entry", OPEN_FILE,
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 },
{ 0 }
};
/* 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;
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)
{
report_context_t *ctx = baton;
if (leaving_state == TARGET_REVISION)
{
const char *rev = svn_hash_gets(attrs, "rev");
SVN_ERR(ctx->update_editor->set_target_revision(ctx->update_baton,
SVN_STR_TO_REV(rev),
ctx->sess->pool));
}
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)
{
report_context_t *ctx = baton;
return SVN_NO_ERROR;
}
#endif /* NOT_USED_YET */
/* Returns best connection for fetching files/properties. */
static svn_ra_serf__connection_t *
get_best_connection(report_context_t *ctx)
{
svn_ra_serf__connection_t *conn;
int first_conn = 1;
/* Skip the first connection if the REPORT response hasn't been completely
received yet or if we're being told to limit our connections to
2 (because this could be an attempt to ensure that we do all our
auxiliary GETs/PROPFINDs on a single connection).
### FIXME: This latter requirement (max_connections > 2) is
### really just a hack to work around the fact that some update
### editor implementations (such as svnrdump's dump editor)
### simply can't handle the way ra_serf violates the editor v1
### drive ordering requirements.
###
### See http://subversion.tigris.org/issues/show_bug.cgi?id=4116.
*/
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 (ctx->sess->num_conns - first_conn == 1)
{
conn = ctx->sess->conns[first_conn];
}
else
{
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;
}
return conn;
}
/** Report state management helper **/
src/subversion/subversion/libsvn_ra_serf/update.c view on Meta::CPAN
}
if (strcmp(name.name, "remove-prop") != 0)
{
props = info->props;
pool = info->pool;
}
else
{
props = dir->removed_props;
pool = dir->pool;
svn_stringbuf_setempty(info->prop_value);
}
if (info->prop_encoding)
{
if (strcmp(info->prop_encoding, "base64") == 0)
{
svn_string_t tmp;
/* 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;
set_val_str = svn_base64_decode_string(&tmp, pool);
}
else
{
return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
NULL,
_("Got unrecognized encoding '%s'"),
info->prop_encoding);
}
}
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);
}
return SVN_NO_ERROR;
}
static svn_error_t *
cdata_report(svn_ra_serf__xml_parser_t *parser,
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;
svn_stringbuf_appendbytes(info->prop_value, data, len);
}
else if (parser->state->current_state == TXDELTA)
{
/* 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);
}
}
}
return SVN_NO_ERROR;
}
/** Editor callbacks given to callers to create request body */
/* Helper to create simple xml tag without attributes. */
static void
make_simple_xml_tag(svn_stringbuf_t **buf_p,
const char *tagname,
const char *cdata,
apr_pool_t *pool)
{
svn_xml_make_open_tag(buf_p, pool, svn_xml_protect_pcdata, tagname, NULL);
svn_xml_escape_cdata_cstring(buf_p, cdata, pool);
svn_xml_make_close_tag(buf_p, pool, tagname);
}
static svn_error_t *
set_path(void *report_baton,
const char *path,
svn_revnum_t revision,
svn_depth_t depth,
svn_boolean_t start_empty,
const char *lock_token,
apr_pool_t *pool)
{
report_context_t *report = report_baton;
svn_stringbuf_t *buf = NULL;
svn_xml_make_open_tag(&buf, pool, svn_xml_protect_pcdata, "S:entry",
"rev", apr_ltoa(pool, revision),
"lock-token", lock_token,
"depth", svn_depth_to_word(depth),
"start-empty", start_empty ? "true" : NULL,
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));
return SVN_NO_ERROR;
}
static svn_error_t *
delete_path(void *report_baton,
const char *path,
apr_pool_t *pool)
{
report_context_t *report = report_baton;
svn_stringbuf_t *buf = NULL;
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));
return SVN_NO_ERROR;
}
static svn_error_t *
link_path(void *report_baton,
const char *path,
const char *url,
svn_revnum_t revision,
svn_depth_t depth,
svn_boolean_t start_empty,
const char *lock_token,
apr_pool_t *pool)
{
report_context_t *report = report_baton;
const char *link, *report_target;
apr_uri_t uri;
apr_status_t status;
svn_stringbuf_t *buf = NULL;
/* We need to pass in the baseline relative path.
*
* TODO Confirm that it's on the same server?
*/
status = apr_uri_parse(pool, url, &uri);
if (status)
{
return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
_("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));
link = apr_pstrcat(pool, "/", link, (char *)NULL);
svn_xml_make_open_tag(&buf, pool, svn_xml_protect_pcdata, "S:entry",
"rev", apr_ltoa(pool, revision),
"lock-token", lock_token,
"depth", svn_depth_to_word(depth),
"linkpath", link,
"start-empty", start_empty ? "true" : NULL,
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));
/* 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));
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))
{
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;
}
/* Serf callback to create update request body bucket. */
static svn_error_t *
src/subversion/subversion/libsvn_ra_serf/update.c view on Meta::CPAN
}
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_parser_t *parser_ctx;
const char *report_target;
svn_stringbuf_t *buf = NULL;
apr_pool_t *iterpool = svn_pool_create(pool);
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));
/* 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
SVN_ERR(svn_ra_serf__report_resource(&report_target, sess, NULL, pool));
/* create and deliver request */
report->path = report_target;
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;
parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
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;
handler->response_handler = svn_ra_serf__handle_xml_parser;
handler->response_baton = parser_ctx;
report->parser_ctx = parser_ctx;
svn_ra_serf__request_create(handler);
/* Open the first extra connection. */
SVN_ERR(open_connection_if_needed(sess, 0));
sess->cur_conn = 1;
/* 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)
{
apr_pool_t *iterpool_inner;
svn_ra_serf__list_t *done_list;
int i;
apr_status_t status;
/* Note: this throws out the old ITERPOOL_INNER. */
svn_pool_clear(iterpool);
if (sess->cancel_func)
SVN_ERR(sess->cancel_func(sess->cancel_baton));
/* We need to be careful between the outer and inner ITERPOOLs,
and what items are allocated within. */
iterpool_inner = svn_pool_create(iterpool);
status = serf_context_run(sess->context,
SVN_RA_SERF__CONTEXT_RUN_DURATION,
iterpool_inner);
err = sess->pending_error;
sess->pending_error = SVN_NO_ERROR;
if (!err && handler->done && handler->server_error)
{
err = handler->server_error->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))
{
svn_error_clear(err);
err = SVN_NO_ERROR;
status = 0;
( run in 0.944 second using v1.01-cache-2.11-cpan-13bb782fe5a )