Modified: subversion/branches/javahl-ra/subversion/libsvn_ra_serf/serf.c URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_ra_serf/serf.c?rev=1343447&r1=1343446&r2=1343447&view=diff ============================================================================== --- subversion/branches/javahl-ra/subversion/libsvn_ra_serf/serf.c (original) +++ subversion/branches/javahl-ra/subversion/libsvn_ra_serf/serf.c Tue May 29 01:39:41 2012 @@ -27,9 +27,6 @@ #include <apr_want.h> #include <apr_uri.h> - -#include <expat.h> - #include <serf.h> #include "svn_pools.h" @@ -48,11 +45,13 @@ #include "private/svn_dav_protocol.h" #include "private/svn_dep_compat.h" #include "private/svn_fspath.h" +#include "private/svn_subr_private.h" #include "svn_private_config.h" #include "ra_serf.h" +/* Implements svn_ra__vtable_t.get_version(). */ static const svn_version_t * ra_serf_version(void) { @@ -62,12 +61,14 @@ ra_serf_version(void) #define RA_SERF_DESCRIPTION \ N_("Module for accessing a repository via WebDAV protocol using serf.") +/* Implements svn_ra__vtable_t.get_description(). */ static const char * ra_serf_get_description(void) { return _(RA_SERF_DESCRIPTION); } +/* Implements svn_ra__vtable_t.get_schemes(). */ static const char * const * ra_serf_get_schemes(apr_pool_t *pool) { @@ -334,6 +335,7 @@ svn_ra_serf__progress(void *progress_bat } } +/* Implements svn_ra__vtable_t.open_session(). */ static svn_error_t * svn_ra_serf__open(svn_ra_session_t *session, const char **corrected_url, @@ -440,6 +442,7 @@ svn_ra_serf__open(svn_ra_session_t *sess return svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, pool); } +/* Implements svn_ra__vtable_t.reparent(). */ static svn_error_t * svn_ra_serf__reparent(svn_ra_session_t *ra_session, const char *url, @@ -482,6 +485,7 @@ svn_ra_serf__reparent(svn_ra_session_t * return SVN_NO_ERROR; } +/* Implements svn_ra__vtable_t.get_session_url(). */ static svn_error_t * svn_ra_serf__get_session_url(svn_ra_session_t *ra_session, const char **url, @@ -492,20 +496,19 @@ svn_ra_serf__get_session_url(svn_ra_sess return SVN_NO_ERROR; } +/* Implements svn_ra__vtable_t.get_latest_revnum(). */ static svn_error_t * svn_ra_serf__get_latest_revnum(svn_ra_session_t *ra_session, svn_revnum_t *latest_revnum, apr_pool_t *pool) { - const char *relative_url, *basecoll_url; svn_ra_serf__session_t *session = ra_session->priv; - return svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url, session, - NULL, session->session_url.path, - SVN_INVALID_REVNUM, latest_revnum, - pool); + return svn_error_trace(svn_ra_serf__get_youngest_revnum( + latest_revnum, session, pool)); } +/* Implements svn_ra__vtable_t.rev_proplist(). */ static svn_error_t * svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session, svn_revnum_t rev, @@ -532,6 +535,7 @@ svn_ra_serf__rev_proplist(svn_ra_session 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)); @@ -542,6 +546,7 @@ svn_ra_serf__rev_proplist(svn_ra_session 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, @@ -559,71 +564,43 @@ svn_ra_serf__rev_prop(svn_ra_session_t * } static svn_error_t * -fetch_path_props(svn_ra_serf__propfind_context_t **ret_prop_ctx, - apr_hash_t **ret_props, - const char **ret_path, - svn_revnum_t *ret_revision, +fetch_path_props(apr_hash_t **props, svn_ra_serf__session_t *session, - const char *rel_path, + const char *session_relpath, svn_revnum_t revision, const svn_ra_serf__dav_props_t *desired_props, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - svn_ra_serf__propfind_context_t *prop_ctx; - apr_hash_t *props; - const char *path; + const char *url; - path = session->session_url.path; + url = session->session_url.path; /* If we have a relative path, append it. */ - if (rel_path) - { - path = svn_path_url_add_component2(path, rel_path, pool); - } - - props = apr_hash_make(pool); - - /* If we were given a specific revision, we have to fetch the VCC and - * do a PROPFIND off of that. - */ - if (!SVN_IS_VALID_REVNUM(revision)) - { - SVN_ERR(svn_ra_serf__deliver_props(&prop_ctx, props, session, - session->conns[0], path, revision, - "0", desired_props, NULL, - pool)); - } - else - { - const char *relative_url, *basecoll_url; + if (session_relpath) + url = svn_path_url_add_component2(url, session_relpath, scratch_pool); - SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url, - session, NULL, path, - revision, NULL, pool)); - - /* We will try again with our new path; however, we're now - * technically an unversioned resource because we are accessing - * the revision's baseline-collection. - */ - path = svn_path_url_add_component2(basecoll_url, relative_url, pool); - revision = SVN_INVALID_REVNUM; - SVN_ERR(svn_ra_serf__deliver_props(&prop_ctx, props, session, - session->conns[0], path, revision, - "0", desired_props, NULL, - pool)); - } - - /* ### switch to svn_ra_serf__retrieve_props? */ - SVN_ERR(svn_ra_serf__wait_for_props(prop_ctx, session, pool)); - - *ret_path = path; - *ret_prop_ctx = prop_ctx; - *ret_props = props; - *ret_revision = revision; + /* 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, @@ -633,13 +610,10 @@ svn_ra_serf__check_path(svn_ra_session_t { svn_ra_serf__session_t *session = ra_session->priv; apr_hash_t *props; - svn_ra_serf__propfind_context_t *prop_ctx; - const char *path; - svn_revnum_t fetched_rev; - svn_error_t *err = fetch_path_props(&prop_ctx, &props, &path, &fetched_rev, - session, rel_path, - revision, check_path_props, pool); + 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) { @@ -648,11 +622,14 @@ svn_ra_serf__check_path(svn_ra_session_t } else { + svn_kind_t res_kind; + /* Any other error, raise to caller. */ if (err) - return err; + return svn_error_trace(err); - SVN_ERR(svn_ra_serf__get_resource_type(kind, props, path, fetched_rev)); + SVN_ERR(svn_ra_serf__get_resource_type(&res_kind, props)); + *kind = svn__node_kind_from_kind(res_kind); } return SVN_NO_ERROR; @@ -862,6 +839,7 @@ get_dirent_props(apr_uint32_t dirent_fie return (svn_ra_serf__dav_props_t *) props->elts; } +/* Implements svn_ra__vtable_t.stat(). */ static svn_error_t * svn_ra_serf__stat(svn_ra_session_t *ra_session, const char *rel_path, @@ -871,17 +849,14 @@ svn_ra_serf__stat(svn_ra_session_t *ra_s { svn_ra_serf__session_t *session = ra_session->priv; apr_hash_t *props; - svn_ra_serf__propfind_context_t *prop_ctx; - const char *path; - svn_revnum_t fetched_rev; svn_error_t *err; struct dirent_walker_baton_t dwb; svn_tristate_t deadprop_count = svn_tristate_unknown; - err = fetch_path_props(&prop_ctx, &props, &path, &fetched_rev, + err = fetch_path_props(&props, session, rel_path, revision, get_dirent_props(SVN_DIRENT_ALL, session, pool), - pool); + pool, pool); if (err) { if (err->apr_err == SVN_ERR_FS_NOT_FOUND) @@ -897,9 +872,7 @@ svn_ra_serf__stat(svn_ra_session_t *ra_s dwb.entry = apr_pcalloc(pool, sizeof(*dwb.entry)); dwb.supports_deadprop_count = &deadprop_count; dwb.result_pool = pool; - SVN_ERR(svn_ra_serf__walk_all_props(props, path, fetched_rev, - dirent_walker, &dwb, - pool)); + SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool)); if (deadprop_count == svn_tristate_false && session->supports_deadprop_count == svn_tristate_unknown @@ -909,14 +882,12 @@ svn_ra_serf__stat(svn_ra_session_t *ra_s information */ session->supports_deadprop_count = svn_tristate_false; - SVN_ERR(fetch_path_props(&prop_ctx, &props, &path, &fetched_rev, - session, rel_path, fetched_rev, + SVN_ERR(fetch_path_props(&props, + session, rel_path, SVN_INVALID_REVNUM, get_dirent_props(SVN_DIRENT_ALL, session, pool), - pool)); + pool, pool)); - SVN_ERR(svn_ra_serf__walk_all_props(props, path, fetched_rev, - dirent_walker, &dwb, - pool)); + SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool)); } if (deadprop_count != svn_tristate_unknown) @@ -932,15 +903,13 @@ svn_ra_serf__stat(svn_ra_session_t *ra_s * SVN_ERR_FS_NOT_DIRECTORY if not. */ static svn_error_t * -resource_is_directory(apr_hash_t *props, - const char *path, - svn_revnum_t revision) +resource_is_directory(apr_hash_t *props) { - svn_node_kind_t kind; + svn_kind_t kind; - SVN_ERR(svn_ra_serf__get_resource_type(&kind, props, path, revision)); + SVN_ERR(svn_ra_serf__get_resource_type(&kind, props)); - if (kind != svn_node_dir) + if (kind != svn_kind_dir) { return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, _("Can't get entries of non-directory")); @@ -949,6 +918,7 @@ resource_is_directory(apr_hash_t *props, 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, @@ -975,34 +945,37 @@ svn_ra_serf__get_dir(svn_ra_session_t *r public url. */ if (SVN_IS_VALID_REVNUM(revision) || fetched_rev) { - const char *relative_url, *basecoll_url; - - SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url, - session, NULL, path, revision, - fetched_rev, pool)); - - path = svn_path_url_add_component2(basecoll_url, relative_url, pool); + 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) { 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, revision, "1", + path, SVN_INVALID_REVNUM, "1", get_dirent_props(dirent_fields, session, pool), pool, pool)); /* Check if the path is really a directory. */ - SVN_ERR(resource_is_directory(props, path, revision)); + 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 @@ -1014,8 +987,9 @@ svn_ra_serf__get_dir(svn_ra_session_t *r dirent_walk.supports_deadprop_count = svn_tristate_unknown; dirent_walk.result_pool = pool; - SVN_ERR(svn_ra_serf__walk_all_paths(props, revision, path_dirent_walker, - &dirent_walk, 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 @@ -1026,7 +1000,7 @@ svn_ra_serf__get_dir(svn_ra_session_t *r session->supports_deadprop_count = svn_tristate_false; SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0], - path, revision, "1", + path, SVN_INVALID_REVNUM, "1", get_dirent_props(dirent_fields, session, pool), pool, pool)); @@ -1034,7 +1008,7 @@ svn_ra_serf__get_dir(svn_ra_session_t *r SVN_ERR(svn_hash__clear(dirent_walk.full_paths, pool)); SVN_ERR(svn_hash__clear(dirent_walk.base_paths, pool)); - SVN_ERR(svn_ra_serf__walk_all_paths(props, revision, + SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM, path_dirent_walker, &dirent_walk, pool)); } @@ -1050,14 +1024,17 @@ svn_ra_serf__get_dir(svn_ra_session_t *r { apr_hash_t *props; - SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0], - path, revision, "0", all_props, - pool, pool)); + SVN_ERR(svn_ra_serf__fetch_node_props(&props, session->conns[0], + path, SVN_INVALID_REVNUM, + all_props, + pool, pool)); + /* Check if the path is really a directory. */ - SVN_ERR(resource_is_directory(props, path, revision)); + SVN_ERR(resource_is_directory(props)); - SVN_ERR(svn_ra_serf__flatten_props(ret_props, props, path, revision, - pool, pool)); + /* ### 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)); } return SVN_NO_ERROR; @@ -1088,6 +1065,8 @@ svn_ra_serf__get_repos_root(svn_ra_sessi case where the root of the repository is not readable. However, it does not handle the case where we're fetching path not existing in HEAD of a repository with unreadable root directory. + + Implements svn_ra__vtable_t.get_uuid(). */ static svn_error_t * svn_ra_serf__get_uuid(svn_ra_session_t *ra_session,
Modified: subversion/branches/javahl-ra/subversion/libsvn_ra_serf/update.c URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_ra_serf/update.c?rev=1343447&r1=1343446&r2=1343447&view=diff ============================================================================== --- subversion/branches/javahl-ra/subversion/libsvn_ra_serf/update.c (original) +++ subversion/branches/javahl-ra/subversion/libsvn_ra_serf/update.c Tue May 29 01:39:41 2012 @@ -126,17 +126,10 @@ typedef struct report_dir_t /* Our base revision - SVN_INVALID_REVNUM if we're adding this dir. */ svn_revnum_t base_rev; - /* The target revision we're retrieving. */ - svn_revnum_t target_rev; - /* controlling dir baton - this is only created in open_dir() */ void *dir_baton; apr_pool_t *dir_baton_pool; - /* Our master update editor and baton. */ - const svn_delta_editor_t *update_editor; - void *update_baton; - /* How many references to this directory do we still have open? */ apr_size_t ref_count; @@ -200,9 +193,6 @@ typedef struct report_info_t /* Our base revision - SVN_INVALID_REVNUM if we're adding this file. */ svn_revnum_t base_rev; - /* The target revision we're retrieving. */ - svn_revnum_t target_rev; - /* our delta base, if present (NULL if we're adding the file) */ const char *delta_base; @@ -237,6 +227,10 @@ typedef struct report_info_t /* 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. */ @@ -252,6 +246,9 @@ typedef struct report_info_t */ typedef struct report_fetch_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; @@ -349,7 +346,7 @@ struct report_context_t { /* number of pending PROPFIND requests */ unsigned int active_propfinds; - /* completed PROPFIND requests (contains propfind_context_t) */ + /* completed PROPFIND requests (contains svn_ra_serf__propfind_context_t) */ svn_ra_serf__list_t *done_propfinds; /* list of files that only have prop changes (contains report_info_t) */ @@ -407,9 +404,6 @@ push_state(svn_ra_serf__xml_parser_t *pa new_info->props = new_info->dir->props; new_info->dir->removed_props = apr_hash_make(new_info->pool); - /* Point to the update_editor */ - new_info->dir->update_editor = ctx->update_editor; - new_info->dir->update_baton = ctx->update_baton; new_info->dir->report_context = ctx; if (info) @@ -467,7 +461,7 @@ set_file_props(void *baton, apr_pool_t *scratch_pool) { report_info_t *info = baton; - const svn_delta_editor_t *editor = info->dir->update_editor; + const svn_delta_editor_t *editor = info->dir->report_context->update_editor; const char *prop_name; if (strcmp(name, "md5-checksum") == 0 @@ -492,7 +486,7 @@ set_dir_props(void *baton, apr_pool_t *scratch_pool) { report_dir_t *dir = baton; - const svn_delta_editor_t *editor = dir->update_editor; + 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); @@ -513,7 +507,7 @@ remove_file_props(void *baton, apr_pool_t *scratch_pool) { report_info_t *info = baton; - const svn_delta_editor_t *editor = info->dir->update_editor; + 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); @@ -534,7 +528,7 @@ remove_dir_props(void *baton, apr_pool_t *scratch_pool) { report_dir_t *dir = baton; - const svn_delta_editor_t *editor = dir->update_editor; + 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); @@ -552,6 +546,8 @@ remove_dir_props(void *baton, static svn_error_t* open_dir(report_dir_t *dir) { + report_context_t *ctx = dir->report_context; + /* if we're already open, return now */ if (dir->dir_baton) { @@ -562,16 +558,16 @@ open_dir(report_dir_t *dir) { dir->dir_baton_pool = svn_pool_create(dir->pool); - if (dir->report_context->destination && - dir->report_context->sess->wc_callbacks->invalidate_wc_props) + if (ctx->destination + && ctx->sess->wc_callbacks->invalidate_wc_props) { - SVN_ERR(dir->report_context->sess->wc_callbacks->invalidate_wc_props( - dir->report_context->sess->wc_callback_baton, - dir->report_context->update_target, + 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_ERR(dir->update_editor->open_root(dir->update_baton, dir->base_rev, + SVN_ERR(ctx->update_editor->open_root(ctx->update_baton, dir->base_rev, dir->dir_baton_pool, &dir->dir_baton)); } @@ -583,7 +579,7 @@ open_dir(report_dir_t *dir) if (SVN_IS_VALID_REVNUM(dir->base_rev)) { - SVN_ERR(dir->update_editor->open_directory(dir->name, + SVN_ERR(ctx->update_editor->open_directory(dir->name, dir->parent_dir->dir_baton, dir->base_rev, dir->dir_baton_pool, @@ -591,7 +587,7 @@ open_dir(report_dir_t *dir) } else { - SVN_ERR(dir->update_editor->add_directory(dir->name, + SVN_ERR(ctx->update_editor->add_directory(dir->name, dir->parent_dir->dir_baton, NULL, SVN_INVALID_REVNUM, dir->dir_baton_pool, @@ -625,12 +621,13 @@ close_dir(report_dir_t *dir) if (dir->fetch_props) { SVN_ERR(svn_ra_serf__walk_all_props(dir->props, dir->url, - dir->target_rev, + dir->report_context->target_rev, set_dir_props, dir, scratch_pool)); } - SVN_ERR(dir->update_editor->close_directory(dir->dir_baton, scratch_pool)); + SVN_ERR(dir->report_context->update_editor->close_directory( + dir->dir_baton, scratch_pool)); /* remove us from our parent's children list */ if (dir->parent_dir) @@ -694,7 +691,7 @@ check_lock(report_info_t *info) const char *lock_val; lock_val = svn_ra_serf__get_ver_prop(info->props, info->url, - info->target_rev, + info->dir->report_context->target_rev, "DAV:", "lockdiscovery"); if (lock_val) @@ -801,6 +798,105 @@ error_fetch(serf_request_t *request, 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 + (or if FORCE_APPLY_TEXTDELTA is set). + + 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, + apr_pool_t *scratch_pool) +{ + report_context_t *ctx = info->dir->report_context; + const svn_delta_editor_t *update_editor = ctx->update_editor; + + /* Ensure our parent is open. */ + SVN_ERR(open_dir(info->dir)); + info->editor_pool = svn_pool_create(info->dir->dir_baton_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->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); + + /* 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) + { + SVN_ERR(svn_ra_serf__walk_all_props(info->props, + info->url, + ctx->target_rev, + set_file_props, info, + 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)); + } + + return SVN_NO_ERROR; +} + +/* Close the file associated with INFO->file_baton, and cleanup other + bits of that structure managed by open_updated_file(). */ +static svn_error_t * +close_updated_file(report_info_t *info, + apr_pool_t *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)); + + /* We're done with our editor pool. */ + svn_pool_destroy(info->editor_pool); + + return SVN_NO_ERROR; +} + /* Implements svn_ra_serf__response_handler_t */ static svn_error_t * handle_fetch(serf_request_t *request, @@ -813,7 +909,9 @@ handle_fetch(serf_request_t *request, apr_status_t status; report_fetch_t *fetch_ctx = handler_baton; svn_error_t *err; - serf_status_line sl; + + /* ### new field. make sure we didn't miss some initialization. */ + SVN_ERR_ASSERT(fetch_ctx->handler != NULL); if (fetch_ctx->read_headers == FALSE) { @@ -825,50 +923,8 @@ handle_fetch(serf_request_t *request, val = serf_bucket_headers_get(hdrs, "Content-Type"); info = fetch_ctx->info; - err = open_dir(info->dir); - if (err) - { - return error_fetch(request, fetch_ctx, err); - } - - info->editor_pool = svn_pool_create(info->dir->dir_baton_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->editor_pool); - } - - if (SVN_IS_VALID_REVNUM(info->base_rev)) - { - err = info->dir->update_editor->open_file(info->name, - info->dir->dir_baton, - info->base_rev, - info->editor_pool, - &info->file_baton); - } - else - { - err = info->dir->update_editor->add_file(info->name, - info->dir->dir_baton, - info->copyfrom_path, - info->copyfrom_rev, - info->editor_pool, - &info->file_baton); - } - - if (err) - { - return error_fetch(request, fetch_ctx, err); - } - - err = info->dir->update_editor->apply_textdelta(info->file_baton, - info->base_checksum, - info->editor_pool, - &info->textdelta, - &info->textdelta_baton); - + /* Open the file for editing. */ + err = open_updated_file(info, FALSE, info->pool); if (err) { return error_fetch(request, fetch_ctx, err); @@ -891,16 +947,12 @@ handle_fetch(serf_request_t *request, /* 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. */ - status = serf_bucket_response_status(response, &sl); - if (SERF_BUCKET_READ_ERROR(status)) - { - return svn_error_wrap_apr(status, NULL); - } - if (sl.code != 200) + if (fetch_ctx->handler->sline.code != 200) { err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, _("GET request failed: %d %s"), - sl.code, sl.reason); + fetch_ctx->handler->sline.code, + fetch_ctx->handler->sline.reason); return error_fetch(request, fetch_ctx, err); } @@ -983,51 +1035,17 @@ handle_fetch(serf_request_t *request, { report_info_t *info = fetch_ctx->info; - /* ### this doesn't feel quite right. but it gets tossed at the - ### end of this block, so it will work for now. */ - apr_pool_t *scratch_pool = info->editor_pool; - if (fetch_ctx->delta_stream) err = svn_error_trace(svn_stream_close(fetch_ctx->delta_stream)); else err = svn_error_trace(info->textdelta(NULL, info->textdelta_baton)); - if (err) { return error_fetch(request, fetch_ctx, err); } - if (info->lock_token) - check_lock(info); - - /* set all of the properties we received */ - err = svn_ra_serf__walk_all_props(info->props, - info->base_name, - info->base_rev, - set_file_props, info, - scratch_pool); - - if (!err) - err = svn_ra_serf__walk_all_props(info->dir->removed_props, - info->base_name, - info->base_rev, - remove_file_props, info, - scratch_pool); - if (!err && info->fetch_props) - { - err = svn_ra_serf__walk_all_props(info->props, - info->url, - info->target_rev, - set_file_props, info, - scratch_pool); - } - - if (!err) - err = info->dir->update_editor->close_file(info->file_baton, - info->final_checksum, - scratch_pool); - + err = close_updated_file(info, info->pool); if (err) { return svn_error_trace(error_fetch(request, fetch_ctx, err)); @@ -1039,8 +1057,7 @@ handle_fetch(serf_request_t *request, fetch_ctx->done_item.next = *fetch_ctx->done_list; *fetch_ctx->done_list = &fetch_ctx->done_item; - /* We're done with our pools. */ - svn_pool_destroy(info->editor_pool); + /* We're done with our pool. */ svn_pool_destroy(info->pool); if (status) @@ -1062,26 +1079,22 @@ handle_stream(serf_request_t *request, apr_pool_t *pool) { report_fetch_t *fetch_ctx = handler_baton; - serf_status_line sl; const char *location; svn_error_t *err; apr_status_t status; - status = serf_bucket_response_status(response, &sl); - if (SERF_BUCKET_READ_ERROR(status)) - { - return svn_error_wrap_apr(status, NULL); - } + /* ### new field. make sure we didn't miss some initialization. */ + SVN_ERR_ASSERT(fetch_ctx->handler != NULL); /* Woo-hoo. Nothing here to see. */ location = svn_ra_serf__response_get_location(response, pool); - err = svn_ra_serf__error_on_status(sl.code, + err = svn_ra_serf__error_on_status(fetch_ctx->handler->sline.code, fetch_ctx->info->name, location); if (err) { - fetch_ctx->done = TRUE; + fetch_ctx->handler->done = TRUE; err = svn_error_compose_create( err, @@ -1157,78 +1170,100 @@ static svn_error_t * handle_propchange_only(report_info_t *info, apr_pool_t *scratch_pool) { - /* Ensure our parent is open. */ - SVN_ERR(open_dir(info->dir)); + /* Open the file for editing (without forcing an apply_textdelta), + pass along any propchanges we've recorded for it, and then close + the file. */ + SVN_ERR(open_updated_file(info, FALSE, scratch_pool)); + SVN_ERR(close_updated_file(info, scratch_pool)); + + /* We're done with our pool. */ + svn_pool_destroy(info->pool); - info->editor_pool = svn_pool_create(info->dir->dir_baton_pool); + info->dir->ref_count--; - /* 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); - } + return SVN_NO_ERROR; +} - if (SVN_IS_VALID_REVNUM(info->base_rev)) - { - SVN_ERR(info->dir->update_editor->open_file(info->name, - info->dir->dir_baton, - info->base_rev, - info->editor_pool, - &info->file_baton)); - } - else - { - SVN_ERR(info->dir->update_editor->add_file(info->name, - info->dir->dir_baton, - info->copyfrom_path, - info->copyfrom_rev, - info->editor_pool, - &info->file_baton)); - } +/* "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. */ +static svn_error_t * +local_fetch(report_info_t *info, + apr_pool_t *scratch_pool) +{ + SVN_ERR(open_updated_file(info, TRUE, 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)); - if (info->fetch_file) - { - SVN_ERR(info->dir->update_editor->apply_textdelta(info->file_baton, - info->base_checksum, - info->editor_pool, - &info->textdelta, - &info->textdelta_baton)); - } + /* We're done with our pool. */ + svn_pool_destroy(info->pool); - if (info->lock_token) - check_lock(info); + return SVN_NO_ERROR; +} - /* 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) - { - SVN_ERR(svn_ra_serf__walk_all_props(info->props, info->url, - info->target_rev, - set_file_props, info, - scratch_pool)); - } +/* Implements svn_ra_serf__response_handler_t */ +static svn_error_t * +handle_local_fetch(serf_request_t *request, + serf_bucket_t *response, + void *handler_baton, + apr_pool_t *pool) +{ + report_fetch_t *fetch_ctx = handler_baton; + apr_status_t status; + svn_error_t *err; + const char *data; + apr_size_t len; - SVN_ERR(info->dir->update_editor->close_file(info->file_baton, - info->final_checksum, - scratch_pool)); + /* ### new field. make sure we didn't miss some initialization. */ + SVN_ERR_ASSERT(fetch_ctx->handler != NULL); - /* We're done with our pools. */ - svn_pool_destroy(info->editor_pool); - svn_pool_destroy(info->pool); + /* 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, + _("HEAD request failed: %d %s"), + fetch_ctx->handler->sline.code, + fetch_ctx->handler->sline.reason); + return error_fetch(request, fetch_ctx, err); + } - info->dir->ref_count--; + while (1) + { + status = serf_bucket_read(response, 8000, &data, &len); + if (SERF_BUCKET_READ_ERROR(status)) + { + return svn_error_wrap_apr(status, NULL); + } + if (APR_STATUS_IS_EOF(status)) + { + err = local_fetch(fetch_ctx->info, fetch_ctx->info->pool); + if (err) + { + return error_fetch(request, fetch_ctx, err); + } - return SVN_NO_ERROR; + 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; + return svn_error_wrap_apr(status, NULL); + } + if (APR_STATUS_IS_EAGAIN(status)) + { + return svn_error_wrap_apr(status, NULL); + } + } + /* not reached */ } +/* --------------------------------------------------------- */ + static svn_error_t * fetch_file(report_context_t *ctx, report_info_t *info) { @@ -1239,9 +1274,8 @@ fetch_file(report_context_t *ctx, report conn = ctx->sess->conns[ctx->sess->cur_conn]; /* 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"); + info->url = svn_ra_serf__get_ver_prop(info->props, info->base_name, + info->base_rev, "DAV:", "checked-in"); if (!info->url) { @@ -1256,7 +1290,7 @@ fetch_file(report_context_t *ctx, report { SVN_ERR(svn_ra_serf__deliver_props(&info->propfind, info->props, ctx->sess, conn, info->url, - info->target_rev, "0", all_props, + ctx->target_rev, "0", all_props, &ctx->done_propfinds, info->dir->pool)); SVN_ERR_ASSERT(info->propfind); @@ -1269,34 +1303,104 @@ fetch_file(report_context_t *ctx, report */ if (info->fetch_file && ctx->text_deltas) { - report_fetch_t *fetch_ctx; + svn_stream_t *contents = NULL; - 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; + if (ctx->sess->wc_callbacks->get_wc_contents + && info->final_sha1_checksum) + { + svn_error_t *err; + svn_checksum_t *sha1_checksum; - handler = apr_pcalloc(info->dir->pool, sizeof(*handler)); + err = svn_checksum_parse_hex(&sha1_checksum, svn_checksum_sha1, + info->final_sha1_checksum, info->pool); + if (!err) + { + err = ctx->sess->wc_callbacks->get_wc_contents( + ctx->sess->wc_callback_baton, &contents, + sha1_checksum, info->pool); + } - handler->method = "GET"; - handler->path = fetch_ctx->info->url; + if (err) + { + /* Meh. Maybe we'll care one day why we're in an + errorful state, but this codepath is optional. */ + svn_error_clear(err); + } + else + { + info->cached_contents = contents; + } + } - handler->conn = conn; - handler->session = ctx->sess; + /* If the working copy can provided cached contents for this + file, we'll send a simple HEAD request (which I'll claim is + to verify readability, but really is just so I can provide a + Serf-queued-request-compliant way of processing the contents + after the PROPFIND for the file's properties ... ugh). - handler->header_delegate = headers_fetch; - handler->header_delegate_baton = fetch_ctx; + Otherwise, we use a GET request for the file's contents. */ + if (info->cached_contents) + { + report_fetch_t *fetch_ctx; - handler->response_handler = handle_fetch; - handler->response_baton = 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->response_error = cancel_fetch; - handler->response_error_baton = fetch_ctx; + handler = apr_pcalloc(info->dir->pool, sizeof(*handler)); - svn_ra_serf__request_create(handler); + handler->handler_pool = info->dir->pool; + handler->method = "HEAD"; + handler->path = fetch_ctx->info->url; + + handler->conn = conn; + handler->session = ctx->sess; + + handler->response_handler = handle_local_fetch; + handler->response_baton = fetch_ctx; + + fetch_ctx->handler = handler; + + svn_ra_serf__request_create(handler); + + ctx->active_fetches++; + } + else + { + report_fetch_t *fetch_ctx; - ctx->active_fetches++; + 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 = apr_pcalloc(info->dir->pool, sizeof(*handler)); + + handler->handler_pool = info->dir->pool; + handler->method = "GET"; + handler->path = fetch_ctx->info->url; + + handler->conn = conn; + handler->session = ctx->sess; + + handler->header_delegate = headers_fetch; + handler->header_delegate_baton = fetch_ctx; + + handler->response_handler = handle_fetch; + handler->response_baton = fetch_ctx; + + handler->response_error = cancel_fetch; + handler->response_error_baton = fetch_ctx; + + fetch_ctx->handler = handler; + + svn_ra_serf__request_create(handler); + + ctx->active_fetches++; + } } else if (info->propfind) { @@ -1373,7 +1477,6 @@ start_report(svn_ra_serf__xml_parser_t * info->base_rev = SVN_STR_TO_REV(rev); info->dir->base_rev = info->base_rev; - info->dir->target_rev = ctx->target_rev; info->fetch_props = TRUE; info->dir->base_name = ""; @@ -1426,7 +1529,6 @@ start_report(svn_ra_serf__xml_parser_t * info->base_rev = SVN_STR_TO_REV(rev); dir->base_rev = info->base_rev; - dir->target_rev = ctx->target_rev; info->fetch_props = FALSE; @@ -1480,7 +1582,6 @@ start_report(svn_ra_serf__xml_parser_t * /* Mark that we don't have a base. */ info->base_rev = SVN_INVALID_REVNUM; dir->base_rev = info->base_rev; - dir->target_rev = ctx->target_rev; dir->fetch_props = TRUE; dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath, @@ -1513,7 +1614,6 @@ start_report(svn_ra_serf__xml_parser_t * info = push_state(parser, ctx, OPEN_FILE); info->base_rev = SVN_STR_TO_REV(rev); - info->target_rev = ctx->target_rev; info->fetch_props = FALSE; info->base_name = apr_pstrdup(info->pool, file_name); @@ -1539,7 +1639,6 @@ start_report(svn_ra_serf__xml_parser_t * info = push_state(parser, ctx, ADD_FILE); info->base_rev = SVN_INVALID_REVNUM; - info->target_rev = ctx->target_rev; info->fetch_props = TRUE; info->fetch_file = TRUE; @@ -1586,10 +1685,10 @@ start_report(svn_ra_serf__xml_parser_t * full_path = svn_relpath_join(info->dir->name, file_name, tmppool); - SVN_ERR(info->dir->update_editor->delete_entry(full_path, - delete_rev, - info->dir->dir_baton, - tmppool)); + SVN_ERR(ctx->update_editor->delete_entry(full_path, + delete_rev, + info->dir->dir_baton, + tmppool)); svn_pool_destroy(tmppool); } @@ -1852,7 +1951,7 @@ end_report(svn_ra_serf__xml_parser_t *pa info->dir->props, ctx->sess, ctx->sess->conns[ctx->sess->cur_conn], info->dir->url, - info->dir->target_rev, "0", + ctx->target_rev, "0", all_props, &ctx->done_propfinds, info->dir->pool)); @@ -2295,7 +2394,6 @@ finish_report(void *report_baton, svn_ra_serf__xml_parser_t *parser_ctx; const char *report_target; svn_boolean_t closed_root; - int status_code; svn_stringbuf_t *buf = NULL; apr_pool_t *iterpool = svn_pool_create(pool); svn_error_t *err; @@ -2325,6 +2423,7 @@ finish_report(void *report_baton, handler = apr_pcalloc(pool, sizeof(*handler)); + handler->handler_pool = pool; handler->method = "REPORT"; handler->path = report->path; handler->body_delegate = create_update_report_body; @@ -2344,9 +2443,6 @@ finish_report(void *report_baton, parser_ctx->end = end_report; parser_ctx->cdata = cdata_report; parser_ctx->done = &report->done; - /* While we provide a location here to store the status code, we don't - do anything with it. The error in parser_ctx->error is sufficient. */ - parser_ctx->status_code = &status_code; handler->response_handler = svn_ra_serf__handle_xml_parser; handler->response_baton = parser_ctx; @@ -2788,7 +2884,7 @@ svn_ra_serf__get_file(svn_ra_session_t * svn_ra_serf__connection_t *conn; const char *fetch_url; apr_hash_t *fetch_props; - svn_node_kind_t res_kind; + svn_kind_t res_kind; /* What connection should we go on? */ conn = session->conns[session->cur_conn]; @@ -2804,24 +2900,23 @@ svn_ra_serf__get_file(svn_ra_session_t * */ if (SVN_IS_VALID_REVNUM(revision) || fetched_rev) { - const char *baseline_url, *rel_path; - - SVN_ERR(svn_ra_serf__get_baseline_info(&baseline_url, &rel_path, - session, conn, fetch_url, - revision, fetched_rev, pool)); - fetch_url = svn_path_url_add_component2(baseline_url, rel_path, pool); + 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)); - SVN_ERR(svn_ra_serf__retrieve_props(&fetch_props, session, conn, fetch_url, - revision, "0", - props ? all_props : check_path_props, - pool, pool)); - - /* Verify that resource type is not colelction. */ - SVN_ERR(svn_ra_serf__get_resource_type(&res_kind, fetch_props, fetch_url, - revision)); - if (res_kind != svn_node_file) + SVN_ERR(svn_ra_serf__fetch_node_props(&fetch_props, conn, fetch_url, + SVN_INVALID_REVNUM, + props ? all_props : check_path_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_kind_file) { return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL, _("Can't get text contents of a directory")); @@ -2830,8 +2925,10 @@ svn_ra_serf__get_file(svn_ra_session_t * /* TODO Filter out all of our props into a usable format. */ if (props) { - SVN_ERR(svn_ra_serf__flatten_props(props, fetch_props, fetch_url, - revision, pool, pool)); + /* ### 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) @@ -2848,6 +2945,8 @@ svn_ra_serf__get_file(svn_ra_session_t * 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; @@ -2862,6 +2961,8 @@ svn_ra_serf__get_file(svn_ra_session_t * 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));
