You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2014/01/10 19:10:33 UTC
svn commit: r1557207 [2/2] -
/subversion/trunk/subversion/libsvn_ra_serf/update.c
Modified: subversion/trunk/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/update.c?rev=1557207&r1=1557206&r2=1557207&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/update.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/update.c Fri Jan 10 18:10:32 2014
@@ -49,14 +49,6 @@
#include "ra_serf.h"
#include "../libsvn_ra/ra_loader.h"
-/*
-#define USE_TRANSITION_PARSER
-*/
-
-#define SPILLBUF_BLOCKSIZE 4096
-#define SPILLBUF_MAXBUFFSIZE 131072
-
-#define PARSE_CHUNK_SIZE 8000 /* Copied from utils.c ### Needs tuning */
/*
@@ -72,34 +64,159 @@
* the tag is 'closed', the pool will be reused.
*/
typedef enum report_state_e {
- NONE = 0,
- INITIAL = XML_STATE_INITIAL /* = 0 */,
- UPDATE_REPORT,
- TARGET_REVISION,
- OPEN_DIR,
- ADD_DIR,
- ABSENT_DIR,
- OPEN_FILE,
- ADD_FILE,
- ABSENT_FILE,
- PROP,
- IGNORE_PROP_NAME,
- NEED_PROP_NAME,
- TXDELTA
-
-#ifdef NOT_USED_YET
- ,
+ INITIAL = XML_STATE_INITIAL /* = 0 */,
+ UPDATE_REPORT,
+ TARGET_REVISION,
- CHECKED_IN,
- CHECKED_IN_HREF,
+ OPEN_DIR,
+ ADD_DIR,
- SET_PROP,
+ OPEN_FILE,
+ ADD_FILE,
- MD5_CHECKSUM
-#endif
+ DELETE_ENTRY,
+ ABSENT_DIR,
+ ABSENT_FILE,
+
+ SET_PROP,
+ REMOVE_PROP,
+
+ PROP,
+
+ FETCH_FILE,
+ TXDELTA,
+
+ CHECKED_IN,
+ CHECKED_IN_HREF,
+
+ MD5_CHECKSUM
} report_state_e;
+#define D_ "DAV:"
+#define S_ SVN_XML_NAMESPACE
+#define V_ SVN_DAV_PROP_NS_DAV
+static const svn_ra_serf__xml_transition_t update_ttable[] = {
+ { INITIAL, S_, "update-report", UPDATE_REPORT,
+ FALSE, { "?inline-props", "?send-all", NULL }, TRUE },
+
+ { UPDATE_REPORT, S_, "target-revision", TARGET_REVISION,
+ FALSE, { "rev", NULL }, TRUE },
+
+ { UPDATE_REPORT, S_, "open-directory", OPEN_DIR,
+ FALSE, { "rev", NULL }, TRUE },
+
+ { OPEN_DIR, S_, "open-directory", OPEN_DIR,
+ FALSE, { "rev", "name", NULL }, TRUE },
+
+ { ADD_DIR, S_, "open-directory", OPEN_DIR,
+ FALSE, { "rev", "name", NULL }, TRUE },
+
+ { OPEN_DIR, S_, "add-directory", ADD_DIR,
+ FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", /*"?bc-url",*/
+ NULL }, TRUE },
+
+ { ADD_DIR, S_, "add-directory", ADD_DIR,
+ FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", /*"?bc-url",*/
+ NULL }, TRUE },
+
+ { OPEN_DIR, S_, "open-file", OPEN_FILE,
+ FALSE, { "rev", "name", NULL }, TRUE },
+
+ { ADD_DIR, S_, "open-file", OPEN_FILE,
+ FALSE, { "rev", "name", NULL }, TRUE },
+
+ { OPEN_DIR, S_, "add-file", ADD_FILE,
+ FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev",
+ "?sha1-checksum", /*"?bc-url",*/ NULL }, TRUE },
+
+ { ADD_DIR, S_, "add-file", ADD_FILE,
+ FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev",
+ "?sha1-checksum", /*"?bc-url",*/ NULL }, TRUE },
+
+ { OPEN_DIR, S_, "delete-entry", DELETE_ENTRY,
+ FALSE, { "?rev", "name", NULL }, TRUE },
+
+ { ADD_DIR, S_, "delete-entry", DELETE_ENTRY,
+ 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 },
+
+
+ { OPEN_DIR, D_, "checked-in", CHECKED_IN,
+ FALSE, { NULL }, FALSE },
+
+ { ADD_DIR, D_, "checked-in", CHECKED_IN,
+ FALSE, { NULL }, FALSE },
+
+ { OPEN_FILE, D_, "checked-in", CHECKED_IN,
+ FALSE, { NULL }, FALSE },
+
+ { ADD_FILE, D_, "checked-in", CHECKED_IN,
+ FALSE, { NULL }, FALSE },
+
+
+ { OPEN_DIR, S_, "set-prop", SET_PROP,
+ TRUE, { "name", "?encoding", NULL }, TRUE },
+
+ { ADD_DIR, S_, "set-prop", SET_PROP,
+ TRUE, { "name", "?encoding", NULL }, TRUE },
+
+ { OPEN_FILE, S_, "set-prop", SET_PROP,
+ TRUE, { "name", "?encoding", NULL }, TRUE },
+
+ { ADD_FILE, S_, "set-prop", SET_PROP,
+ TRUE, { "name", "?encoding", NULL }, TRUE },
+
+
+ { OPEN_DIR, S_, "remove-prop", REMOVE_PROP,
+ TRUE, { "name", NULL }, TRUE },
+
+ { ADD_DIR, S_, "remove-prop", REMOVE_PROP,
+ TRUE, { "name", NULL }, TRUE },
+
+ { OPEN_FILE, S_, "remove-prop", REMOVE_PROP,
+ TRUE, { "name", NULL }, TRUE },
+
+ { ADD_FILE, S_, "remove-prop", REMOVE_PROP,
+ TRUE, { "name", NULL }, TRUE },
+
+ { OPEN_FILE, S_, "prop", PROP,
+ FALSE, { NULL }, FALSE },
+ { ADD_FILE, S_, "prop", PROP,
+ FALSE, { NULL }, FALSE },
+
+ { OPEN_FILE, S_, "txdelta", TXDELTA,
+ FALSE, { "?base-checksum" }, TRUE },
+
+ { ADD_FILE, S_, "txdelta", TXDELTA,
+ FALSE, { "?base-checksum" }, TRUE },
+
+ { OPEN_FILE, S_, "fetch-file", FETCH_FILE,
+ FALSE, { "?base-checksum", "?sha1-checksum", NULL }, TRUE},
+
+ { ADD_FILE, S_, "fetch-file", FETCH_FILE,
+ FALSE, { "?base-checksum", "?sha1-checksum", NULL }, TRUE },
+
+ { CHECKED_IN, D_, "href", CHECKED_IN_HREF,
+ TRUE, { NULL }, TRUE },
+
+ { PROP, V_, "md5-checksum", MD5_CHECKSUM,
+ TRUE, { NULL }, TRUE },
+
+ { 0 }
+};
+
/* While we process the REPORT response, we will queue up GET and PROPFIND
requests. For a very large checkout, it is very easy to queue requests
faster than they are resolved. Thus, we need to pause the XML processing
@@ -117,6 +234,10 @@ typedef enum report_state_e {
#define REQUEST_COUNT_TO_PAUSE 50
#define REQUEST_COUNT_TO_RESUME 40
+#define SPILLBUF_BLOCKSIZE 4096
+#define SPILLBUF_MAXBUFFSIZE 131072
+
+#define PARSE_CHUNK_SIZE 8000 /* Copied from utils.c ### Needs tuning */
/* Forward-declare our report context. */
typedef struct report_context_t report_context_t;
@@ -124,156 +245,102 @@ typedef struct report_context_t report_c
/*
* This structure represents the information for a directory.
*/
-typedef struct report_dir_t
+typedef struct dir_baton_t
{
- /* Our parent directory.
- *
- * This value is NULL when we are the root.
- */
- struct report_dir_t *parent_dir;
+ struct dir_baton_t *parent_dir; /* NULL when root */
- apr_pool_t *pool;
+ apr_pool_t *pool; /* Subpool for this directory */
/* Pointer back to our original report context. */
- report_context_t *report_context;
-
- /* Our name sans any parents. */
- const char *base_name;
+ report_context_t *ctx;
- /* the expanded directory name (including all parent names) */
- const char *name;
+ const char *relpath; /* session relative path */
+ const char *base_name; /* Name of item "" for root */
/* the canonical url for this directory after updating. (received) */
const char *url;
- /* The original repos_relpath of this url (from the working copy)
- or NULL if the repos_relpath can be calculated from the edit root. */
+ /* The original repos_relpath of this url (via the reporter)
+ directly, or via an ancestor. */
const char *repos_relpath;
- /* Our base revision - SVN_INVALID_REVNUM if we're adding this dir. */
- svn_revnum_t base_rev;
+ svn_revnum_t base_rev; /* base revision or NULL for Add */
+
+ const char *copyfrom_path; /* NULL for open */
+ svn_revnum_t copyfrom_rev; /* SVN_INVALID_REVNUM for open */
/* controlling dir baton - this is only created in ensure_dir_opened() */
+ svn_boolean_t dir_opened;
void *dir_baton;
- apr_pool_t *dir_baton_pool;
/* How many references to this directory do we still have open? */
apr_size_t ref_count;
- /* Namespace list allocated out of this ->pool. */
- svn_ra_serf__ns_t *ns_list;
-
- /* hashtable for all of the properties (shared within a dir) */
- apr_hash_t *props;
-
- /* hashtable for all to-be-removed properties (shared within a dir) */
- apr_hash_t *removed_props;
-
- /* The propfind request for our current directory */
+ svn_boolean_t fetch_props; /* Use PROPFIND request? */
svn_ra_serf__handler_t *propfind_handler;
+ apr_hash_t *propfind_props;
+ apr_hash_t *remove_props;
- /* Has the server told us to fetch the dir props? */
- svn_boolean_t fetch_props;
-
- /* Have we closed the directory tag (meaning no more additions)? */
- svn_boolean_t tag_closed;
-
- /* The children of this directory */
- struct report_dir_t *children;
-
- /* The next sibling of this directory */
- struct report_dir_t *sibling;
-} report_dir_t;
+} dir_baton_t;
/*
- * This structure represents the information for a file.
- *
- * A directory may have a report_info_t associated with it as well.
- *
- * This structure is created as we parse the REPORT response and
- * once the element is completed, we create a report_fetch_t structure
- * to give to serf to retrieve this file.
- */
-typedef struct report_info_t
+* This structure represents the information for a file.
+*
+* This structure is created as we parse the REPORT response and
+* once the element is completed, we may create a fetch_ctx_t structure
+* to give to serf to retrieve this file.
+*/
+typedef struct file_baton_t
{
- apr_pool_t *pool;
+ dir_baton_t *parent_dir; /* The parent */
+ apr_pool_t *pool; /* Subpool for this file*/
- /* The enclosing directory.
- *
- * If this structure refers to a directory, the dir it points to will be
- * itself.
- */
- report_dir_t *dir;
-
- /* Our name sans any directory info. */
+ const char *relpath; /* session relative path */
const char *base_name;
- /* the expanded file name (including all parent directory names) */
- const char *name;
-
- /* the canonical url for this file. */
+ /* the canonical url for this directory after updating. (received) */
const char *url;
+ /* The original repos_relpath of this url as reported. */
+ const char *repos_relpath;
+
/* lock token, if we had one to start off with. */
const char *lock_token;
- /* Our base revision - SVN_INVALID_REVNUM if we're adding this file. */
- svn_revnum_t base_rev;
-
- /* our delta base, if present (NULL if we're adding the file) */
- const char *delta_base;
+ svn_revnum_t base_rev; /* SVN_INVALID_REVNUM for Add */
- /* Path of original item if add with history */
- const char *copyfrom_path;
+ const char *copyfrom_path; /* NULL for open */
+ svn_revnum_t copyfrom_rev; /* SVN_INVALID_REVNUM for open */
- /* Revision of original item if add with history */
- svn_revnum_t copyfrom_rev;
+ /* controlling dir baton - this is only created in ensure_file_opened() */
+ svn_boolean_t file_opened;
+ void *file_baton;
- /* The propfind request for our current file (if present) */
+ svn_boolean_t fetch_props; /* Use PROPFIND request? */
svn_ra_serf__handler_t *propfind_handler;
-
- /* Has the server told us to fetch the file props? */
- svn_boolean_t fetch_props;
+ apr_hash_t *propfind_props;
+ apr_hash_t *remove_props;
/* Has the server told us to go fetch - only valid if we had it already */
svn_boolean_t fetch_file;
- /* The properties for this file */
- apr_hash_t *props;
-
- /* pool passed to update->add_file, etc. */
- apr_pool_t *editor_pool;
-
/* controlling file_baton and textdelta handler */
- void *file_baton;
- const char *base_checksum;
- const char *final_sha1_checksum;
- svn_txdelta_window_handler_t textdelta;
- void *textdelta_baton;
- svn_stream_t *svndiff_decoder;
- svn_stream_t *base64_decoder;
-
- /* 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;
+ svn_txdelta_window_handler_t txdelta;
+ void *txdelta_baton;
- /* temporary property for this file which is currently being parsed
- * It will eventually be stored in our parent directory's property hash.
- */
- const char *prop_ns;
- const char *prop_name;
- svn_stringbuf_t *prop_value;
- const char *prop_encoding;
-} report_info_t;
+ svn_checksum_t *base_md5_checksum;
+ svn_checksum_t *final_md5_checksum;
+ svn_checksum_t *final_sha1_checksum;
+
+ svn_stream_t *txdelta_stream; /* Stream that feeds windows when
+ written to within txdelta*/
+} file_baton_t;
/*
* This structure represents a single request to GET (fetch) a file with
* its associated Serf session/connection.
*/
-typedef struct report_fetch_t {
+typedef struct fetch_ctx_t {
/* The handler representing this particular fetch. */
svn_ra_serf__handler_t *handler;
@@ -285,7 +352,7 @@ typedef struct report_fetch_t {
svn_ra_serf__connection_t *conn;
/* Stores the information for the file we want to fetch. */
- report_info_t *info;
+ file_baton_t *file;
/* Have we read our response headers yet? */
svn_boolean_t read_headers;
@@ -299,22 +366,13 @@ typedef struct report_fetch_t {
/* This is the amount of data that we have read so far. */
apr_off_t read_size;
- /* If we're receiving an svndiff, this will be non-NULL. */
- svn_stream_t *delta_stream;
-
/* If we're writing this file to a stream, this will be non-NULL. */
- svn_stream_t *target_stream;
-
- /* Are we done fetching this file? */
- svn_boolean_t done;
-
- /* Discard the rest of the content? */
- svn_boolean_t discard;
+ svn_stream_t *result_stream;
- svn_ra_serf__list_t **done_list;
- svn_ra_serf__list_t done_item;
+ /* The base-rev header */
+ const char *delta_base;
-} report_fetch_t;
+} fetch_ctx_t;
/*
* The master structure for a REPORT request and response.
@@ -335,6 +393,10 @@ struct report_context_t {
/* What is the target revision that we want for this REPORT? */
svn_revnum_t target_rev;
+ /* Where are we (used while parsing) */
+ dir_baton_t *cur_dir;
+ file_baton_t *cur_file;
+
/* Have we been asked to ignore ancestry or textdeltas? */
svn_boolean_t ignore_ancestry;
svn_boolean_t text_deltas;
@@ -355,12 +417,8 @@ struct report_context_t {
/* Path -> const char *repos_relpath mapping */
apr_hash_t *switched_paths;
- /* Boolean indicating whether "" is switched.
- (This indicates that the we are updating a single file) */
- svn_boolean_t root_is_switched;
-
/* Our master update editor and baton. */
- const svn_delta_editor_t *update_editor;
+ const svn_delta_editor_t *editor;
void *update_baton;
/* The file holding request body for the REPORT.
@@ -370,136 +428,154 @@ struct report_context_t {
*/
apr_file_t *body_file;
- /* root directory object */
- report_dir_t *root_dir;
-
/* number of pending GET requests */
unsigned int num_active_fetches;
- /* completed fetches (contains report_fetch_t) */
- svn_ra_serf__list_t *done_fetches;
-
/* number of pending PROPFIND requests */
unsigned int num_active_propfinds;
- /* completed PROPFIND requests (contains svn_ra_serf__handler_t) */
- svn_ra_serf__list_t *done_propfinds;
- svn_ra_serf__list_t *done_dir_propfinds;
-
- /* list of outstanding prop changes (contains report_dir_t) */
- svn_ra_serf__list_t *active_dir_propfinds;
-
- /* list of files that only have prop changes (contains report_info_t) */
- svn_ra_serf__list_t *file_propchanges_only;
-
- /* The path to the REPORT request */
- const char *path;
-
/* Are we done parsing the REPORT response? */
svn_boolean_t done;
/* Did we receive all data from the network? */
svn_boolean_t report_received;
- /* Did we get a complete (non-truncated) report? */
- svn_boolean_t report_completed;
-
- /* The XML parser context for the REPORT response. */
- svn_ra_serf__xml_parser_t *parser_ctx;
-
/* Did we close the root directory? */
svn_boolean_t closed_root;
};
+static svn_error_t *
+create_dir_baton(dir_baton_t **new_dir,
+ report_context_t *ctx,
+ const char *name,
+ apr_pool_t *scratch_pool)
+{
+ dir_baton_t *parent = ctx->cur_dir;
+ apr_pool_t *dir_pool;
+ dir_baton_t *dir;
-#ifdef USE_TRANSITION_PARSER
+ if (parent)
+ dir_pool = svn_pool_create(parent->pool);
+ else
+ dir_pool = svn_pool_create(ctx->pool);
-#define D_ "DAV:"
-#define S_ SVN_XML_NAMESPACE
-#define V_ SVN_DAV_PROP_NS_DAV
-static const svn_ra_serf__xml_transition_t update_ttable[] = {
- { INITIAL, S_, "update-report", UPDATE_REPORT,
- FALSE, { NULL }, FALSE },
+ dir = apr_pcalloc(dir_pool, sizeof(*dir));
+ dir->pool = dir_pool;
+ dir->ctx = ctx;
- { UPDATE_REPORT, S_, "target-revision", TARGET_REVISION,
- FALSE, { "rev", NULL }, TRUE },
+ if (parent)
+ {
+ dir->parent_dir = parent;
+ parent->ref_count++;
+ }
- { UPDATE_REPORT, S_, "open-directory", OPEN_DIR,
- FALSE, { "rev", NULL }, TRUE },
+ dir->relpath = parent ? svn_relpath_join(parent->relpath, name, dir_pool)
+ : apr_pstrdup(dir_pool, name);
+ dir->base_name = svn_relpath_basename(dir->relpath, NULL);
- { OPEN_DIR, S_, "open-directory", OPEN_DIR,
- FALSE, { "rev", "name", NULL }, TRUE },
+ dir->repos_relpath = svn_hash_gets(ctx->switched_paths, dir->relpath);
+ if (!dir->repos_relpath)
+ {
+ if (parent)
+ dir->repos_relpath = svn_relpath_join(parent->repos_relpath, name,
+ dir_pool);
+ else
+ dir->repos_relpath = svn_uri_skip_ancestor(ctx->sess->repos_root_str,
+ ctx->sess->session_url_str,
+ dir_pool);
+ }
- { OPEN_DIR, S_, "add-directory", ADD_DIR,
- FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+ dir->base_rev = SVN_INVALID_REVNUM;
+ dir->copyfrom_rev = SVN_INVALID_REVNUM;
- { ADD_DIR, S_, "add-directory", ADD_DIR,
- FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+ dir->ref_count = 1;
- { OPEN_DIR, S_, "open-file", OPEN_FILE,
- FALSE, { "rev", "name", NULL }, TRUE },
+ ctx->cur_dir = dir;
- { OPEN_DIR, S_, "add-file", ADD_FILE,
- FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+ *new_dir = dir;
+ return SVN_NO_ERROR;
+}
- { ADD_DIR, S_, "add-file", ADD_FILE,
- FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+static svn_error_t *
+create_file_baton(file_baton_t **new_file,
+ report_context_t *ctx,
+ const char *name,
+ apr_pool_t *scratch_pool)
+{
+ dir_baton_t *parent = ctx->cur_dir;
+ apr_pool_t *file_pool;
+ file_baton_t *file;
- { OPEN_DIR, S_, "delete-entry", OPEN_FILE,
- FALSE, { "?rev", "name", NULL }, TRUE },
+ file_pool = svn_pool_create(parent->pool);
- { OPEN_DIR, S_, "absent-directory", ABSENT_DIR,
- FALSE, { "name", NULL }, TRUE },
+ file = apr_pcalloc(file_pool, sizeof(*file));
+ file->pool = file_pool;
- { ADD_DIR, S_, "absent-directory", ABSENT_DIR,
- FALSE, { "name", NULL }, TRUE },
+ file->parent_dir = parent;
+ parent->ref_count++;
- { OPEN_DIR, S_, "absent-file", ABSENT_FILE,
- FALSE, { "name", NULL }, TRUE },
+ file->relpath = svn_relpath_join(parent->relpath, name, file_pool);
+ file->base_name = svn_relpath_basename(file->relpath, NULL);
- { ADD_DIR, S_, "absent-file", ABSENT_FILE,
- FALSE, { "name", NULL }, TRUE },
+ file->repos_relpath = svn_hash_gets(ctx->switched_paths, file->relpath);
+ if (!file->repos_relpath)
+ file->repos_relpath = svn_relpath_join(parent->repos_relpath, name,
+ file_pool);
+ /* Sane defaults */
+ file->base_rev = SVN_INVALID_REVNUM;
+ file->copyfrom_rev = SVN_INVALID_REVNUM;
- { OPEN_DIR, D_, "checked-in", CHECKED_IN,
- FALSE, { NULL }, FALSE },
+ file->lock_token = svn_hash_gets(parent->ctx->lock_path_tokens,
+ file->relpath);
- { ADD_DIR, D_, "checked-in", CHECKED_IN,
- FALSE, { NULL }, FALSE },
+ *new_file = file;
- { ADD_FILE, D_, "checked-in", CHECKED_IN,
- FALSE, { NULL }, FALSE },
+ ctx->cur_file = file;
+ return SVN_NO_ERROR;
+}
- { OPEN_DIR, S_, "set-prop", SET_PROP,
- TRUE, { "name", "?encoding", NULL }, TRUE },
+/** Minimum nr. of outstanding requests needed before a new connection is
+ * opened. */
+#define REQS_PER_CONN 8
- { ADD_DIR, S_, "set-prop", SET_PROP,
- TRUE, { "name", "?encoding", NULL }, TRUE },
+/** 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;
- { OPEN_FILE, S_, "set-prop", SET_PROP,
- TRUE, { "name", "?encoding", NULL }, TRUE },
+ 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);
- { ADD_FILE, S_, "set-prop", SET_PROP,
- TRUE, { "name", "?encoding", NULL }, TRUE },
-
-
- { OPEN_FILE, S_, "prop", PROP,
- FALSE, { NULL }, FALSE },
- { ADD_FILE, S_, "prop", PROP,
- FALSE, { NULL }, FALSE },
-
- { CHECKED_IN, D_, "href", CHECKED_IN_HREF,
- TRUE, { NULL }, TRUE },
-
- { PROP, V_, "md5-checksum", MD5_CHECKSUM,
- TRUE, { NULL }, TRUE },
-
- { 0 }
-};
-
-#endif /* USE_TRANSITION_PARSER */
+ sess->num_conns++;
+ }
+ return SVN_NO_ERROR;
+}
/* Returns best connection for fetching files/properties. */
static svn_ra_serf__connection_t *
@@ -545,94 +621,7 @@ get_best_connection(report_context_t *ct
}
-/** Report state management helper **/
-
-static report_info_t *
-push_state(svn_ra_serf__xml_parser_t *parser,
- report_context_t *ctx,
- report_state_e state)
-{
- report_info_t *info;
- apr_pool_t *info_parent_pool;
-
- svn_ra_serf__xml_push_state(parser, state);
-
- info = parser->state->private;
-
- /* Our private pool needs to be disjoint from the state pool. */
- if (!info)
- {
- info_parent_pool = ctx->pool;
- }
- else
- {
- info_parent_pool = info->pool;
- }
-
- if (state == OPEN_DIR || state == ADD_DIR)
- {
- report_info_t *new_info;
-
- new_info = apr_pcalloc(info_parent_pool, sizeof(*new_info));
- new_info->pool = svn_pool_create(info_parent_pool);
- new_info->lock_token = NULL;
- new_info->prop_value = svn_stringbuf_create_empty(new_info->pool);
-
- new_info->dir = apr_pcalloc(new_info->pool, sizeof(*new_info->dir));
- new_info->dir->pool = new_info->pool;
-
- /* Create the root property tree. */
- new_info->dir->props = apr_hash_make(new_info->pool);
- new_info->props = new_info->dir->props;
- new_info->dir->removed_props = apr_hash_make(new_info->pool);
-
- new_info->dir->report_context = ctx;
-
- if (info)
- {
- info->dir->ref_count++;
-
- new_info->dir->parent_dir = info->dir;
-
- /* Point our ns_list at our parents to try to reuse it. */
- new_info->dir->ns_list = info->dir->ns_list;
-
- /* Add ourselves to our parent's list */
- new_info->dir->sibling = info->dir->children;
- info->dir->children = new_info->dir;
- }
- else
- {
- /* Allow us to be found later. */
- ctx->root_dir = new_info->dir;
- }
-
- parser->state->private = new_info;
- }
- else if (state == OPEN_FILE || state == ADD_FILE)
- {
- report_info_t *new_info;
-
- new_info = apr_pcalloc(info_parent_pool, sizeof(*new_info));
- new_info->pool = svn_pool_create(info_parent_pool);
- new_info->file_baton = NULL;
- new_info->lock_token = NULL;
- new_info->fetch_file = FALSE;
- new_info->prop_value = svn_stringbuf_create_empty(new_info->pool);
-
- /* Point at our parent's directory state. */
- new_info->dir = info->dir;
- info->dir->ref_count++;
-
- new_info->props = apr_hash_make(new_info->pool);
-
- parser->state->private = new_info;
- }
- return parser->state->private;
-}
-
-
/** Wrappers around our various property walkers **/
static svn_error_t *
@@ -642,13 +631,13 @@ set_file_props(void *baton,
const svn_string_t *val,
apr_pool_t *scratch_pool)
{
- report_info_t *info = baton;
- const svn_delta_editor_t *editor = info->dir->report_context->update_editor;
+ file_baton_t *file = baton;
+ const svn_delta_editor_t *editor = file->parent_dir->ctx->editor;
const char *prop_name;
prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
if (prop_name != NULL)
- return svn_error_trace(editor->change_file_prop(info->file_baton,
+ return svn_error_trace(editor->change_file_prop(file->file_baton,
prop_name,
val,
scratch_pool));
@@ -663,8 +652,8 @@ set_dir_props(void *baton,
const svn_string_t *val,
apr_pool_t *scratch_pool)
{
- report_dir_t *dir = baton;
- const svn_delta_editor_t *editor = dir->report_context->update_editor;
+ dir_baton_t *dir = baton;
+ const svn_delta_editor_t *editor = dir->ctx->editor;
const char *prop_name;
prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
@@ -675,181 +664,140 @@ set_dir_props(void *baton,
scratch_pool));
return SVN_NO_ERROR;
}
-
-
-static svn_error_t *
-remove_file_props(void *baton,
- const char *ns,
- const char *name,
- const svn_string_t *val,
- apr_pool_t *scratch_pool)
-{
- report_info_t *info = baton;
- 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);
- if (prop_name != NULL)
- return svn_error_trace(editor->change_file_prop(info->file_baton,
- prop_name,
- NULL,
- scratch_pool));
- return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *
-remove_dir_props(void *baton,
- const char *ns,
- const char *name,
- const svn_string_t *val,
- apr_pool_t *scratch_pool)
-{
- report_dir_t *dir = baton;
- 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);
- if (prop_name != NULL)
- return svn_error_trace(editor->change_dir_prop(dir->dir_baton,
- prop_name,
- NULL,
- scratch_pool));
- return SVN_NO_ERROR;
-}
-
/** Helpers to open and close directories */
static svn_error_t*
-ensure_dir_opened(report_dir_t *dir)
+ensure_dir_opened(dir_baton_t *dir,
+ apr_pool_t *scratch_pool)
{
- report_context_t *ctx = dir->report_context;
+ report_context_t *ctx = dir->ctx;
- /* if we're already open, return now */
- if (dir->dir_baton)
- {
- return SVN_NO_ERROR;
- }
+ if (dir->dir_opened)
+ return SVN_NO_ERROR;
if (dir->base_name[0] == '\0')
{
- dir->dir_baton_pool = svn_pool_create(dir->pool);
-
if (ctx->destination
&& ctx->sess->wc_callbacks->invalidate_wc_props)
{
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_RA_SERF__WC_CHECKED_IN_URL, scratch_pool));
}
- SVN_ERR(ctx->update_editor->open_root(ctx->update_baton, dir->base_rev,
- dir->dir_baton_pool,
- &dir->dir_baton));
+ SVN_ERR(ctx->editor->open_root(ctx->update_baton, dir->base_rev,
+ dir->pool,
+ &dir->dir_baton));
}
else
{
- SVN_ERR(ensure_dir_opened(dir->parent_dir));
-
- dir->dir_baton_pool = svn_pool_create(dir->parent_dir->dir_baton_pool);
+ SVN_ERR(ensure_dir_opened(dir->parent_dir, scratch_pool));
if (SVN_IS_VALID_REVNUM(dir->base_rev))
{
- SVN_ERR(ctx->update_editor->open_directory(dir->name,
- dir->parent_dir->dir_baton,
- dir->base_rev,
- dir->dir_baton_pool,
- &dir->dir_baton));
+ SVN_ERR(ctx->editor->open_directory(dir->relpath,
+ dir->parent_dir->dir_baton,
+ dir->base_rev,
+ dir->pool,
+ &dir->dir_baton));
}
else
{
- SVN_ERR(ctx->update_editor->add_directory(dir->name,
- dir->parent_dir->dir_baton,
- NULL, SVN_INVALID_REVNUM,
- dir->dir_baton_pool,
- &dir->dir_baton));
+ SVN_ERR(ctx->editor->add_directory(dir->relpath,
+ dir->parent_dir->dir_baton,
+ dir->copyfrom_path,
+ dir->copyfrom_rev,
+ dir->pool,
+ &dir->dir_baton));
}
}
+ dir->dir_opened = TRUE;
+
return SVN_NO_ERROR;
}
static svn_error_t *
-close_dir(report_dir_t *dir)
+maybe_close_dir(dir_baton_t *dir)
{
- report_dir_t *prev;
- report_dir_t *sibling;
-
- /* ### is there a better pool... this is tossed at end-of-func */
- apr_pool_t *scratch_pool = dir->dir_baton_pool;
+ apr_pool_t *scratch_pool = dir->pool;
+ dir_baton_t *parent = dir->parent_dir;
+ report_context_t *ctx = dir->ctx;
- SVN_ERR_ASSERT(! dir->ref_count);
-
- SVN_ERR(svn_ra_serf__walk_all_props(dir->props, dir->base_name,
- dir->base_rev,
- set_dir_props, dir,
- scratch_pool));
+ if (--dir->ref_count)
+ {
+ return SVN_NO_ERROR;
+ }
- SVN_ERR(svn_ra_serf__walk_all_props(dir->removed_props, dir->base_name,
- dir->base_rev, remove_dir_props, dir,
- scratch_pool));
+ SVN_ERR(ensure_dir_opened(dir, dir->pool));
- if (dir->fetch_props)
+ if (dir->propfind_props)
{
- SVN_ERR(svn_ra_serf__walk_all_props(dir->props, dir->url,
- dir->report_context->target_rev,
+ apr_hash_index_t *hi;
+ SVN_ERR(svn_ra_serf__walk_all_props(dir->propfind_props,
+ dir->url,
+ ctx->target_rev,
set_dir_props, dir,
scratch_pool));
+
+ if (dir->remove_props)
+ for (hi = apr_hash_first(scratch_pool, dir->remove_props);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ SVN_ERR(ctx->editor->change_file_prop(dir->dir_baton,
+ svn__apr_hash_index_key(hi),
+ NULL /* value */,
+ scratch_pool));
+ }
}
- SVN_ERR(dir->report_context->update_editor->close_directory(
- dir->dir_baton, scratch_pool));
+ SVN_ERR(dir->ctx->editor->close_directory(dir->dir_baton, scratch_pool));
- /* remove us from our parent's children list */
- if (dir->parent_dir)
- {
- prev = NULL;
- sibling = dir->parent_dir->children;
+ svn_pool_destroy(dir->pool /* scratch_pool */);
- while (sibling != dir)
- {
- prev = sibling;
- sibling = sibling->sibling;
- if (!sibling)
- SVN_ERR_MALFUNCTION();
- }
+ if (parent)
+ return svn_error_trace(maybe_close_dir(parent));
+ else
+ return SVN_NO_ERROR;
+}
- if (!prev)
- {
- dir->parent_dir->children = dir->sibling;
- }
- else
- {
- prev->sibling = dir->sibling;
- }
- }
+static svn_error_t *
+ensure_file_opened(file_baton_t *file,
+ apr_pool_t *scratch_pool)
+{
+ const svn_delta_editor_t *editor = file->parent_dir->ctx->editor;
- svn_pool_destroy(dir->dir_baton_pool);
- svn_pool_destroy(dir->pool);
+ if (file->file_opened)
+ return SVN_NO_ERROR;
- return SVN_NO_ERROR;
-}
+ /* Ensure our parent is open. */
+ SVN_ERR(ensure_dir_opened(file->parent_dir, scratch_pool));
-static svn_error_t *close_all_dirs(report_dir_t *dir)
-{
- while (dir->children)
+ /* Open (or add) the file. */
+ if (SVN_IS_VALID_REVNUM(file->base_rev))
{
- SVN_ERR(close_all_dirs(dir->children));
- dir->ref_count--;
+ SVN_ERR(editor->open_file(file->relpath,
+ file->parent_dir->dir_baton,
+ file->base_rev,
+ file->pool,
+ &file->file_baton));
+ }
+ else
+ {
+ SVN_ERR(editor->add_file(file->relpath,
+ file->parent_dir->dir_baton,
+ file->copyfrom_path,
+ file->copyfrom_rev,
+ file->pool,
+ &file->file_baton));
}
- SVN_ERR_ASSERT(! dir->ref_count);
-
- SVN_ERR(ensure_dir_opened(dir));
+ file->file_opened = TRUE;
- return close_dir(dir);
+ return SVN_NO_ERROR;
}
@@ -863,33 +811,39 @@ static svn_error_t *close_all_dirs(repor
* an active lock. If not, then we'll assume there isn't a lock
* anymore.
*/
-static void
-check_lock(report_info_t *info)
+static svn_error_t *
+check_lock(file_baton_t *file,
+ apr_pool_t *scratch_pool)
{
const char *lock_val;
- lock_val = svn_ra_serf__get_ver_prop(info->props, info->url,
- info->dir->report_context->target_rev,
+ if (!file->propfind_handler || !file->lock_token)
+ return SVN_NO_ERROR;
+
+ lock_val = svn_ra_serf__get_ver_prop(file->propfind_props, file->url,
+ file->parent_dir->ctx->target_rev,
"DAV:", "lockdiscovery");
if (lock_val)
{
char *new_lock;
- new_lock = apr_pstrdup(info->editor_pool, lock_val);
+ new_lock = apr_pstrdup(scratch_pool, lock_val);
apr_collapse_spaces(new_lock, new_lock);
lock_val = new_lock;
}
if (!lock_val || lock_val[0] == '\0')
{
- svn_string_t *str;
+ SVN_ERR(ensure_file_opened(file, scratch_pool));
- str = svn_string_ncreate("", 1, info->editor_pool);
-
- svn_ra_serf__set_ver_prop(info->dir->removed_props, info->base_name,
- info->base_rev, "DAV:", "lock-token",
- str, info->dir->pool);
+ SVN_ERR(file->parent_dir->ctx->editor->change_file_prop(
+ file->file_baton,
+ SVN_PROP_ENTRY_LOCK_TOKEN,
+ NULL,
+ scratch_pool));
}
+
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -897,14 +851,13 @@ headers_fetch(serf_bucket_t *headers,
void *baton,
apr_pool_t *pool)
{
- report_fetch_t *fetch_ctx = baton;
+ fetch_ctx_t *fetch_ctx = baton;
/* note that we have old VC URL */
- if (SVN_IS_VALID_REVNUM(fetch_ctx->info->base_rev) &&
- fetch_ctx->info->delta_base)
+ if (fetch_ctx->delta_base)
{
serf_bucket_headers_setn(headers, SVN_DAV_DELTA_BASE_HEADER,
- fetch_ctx->info->delta_base);
+ fetch_ctx->delta_base);
serf_bucket_headers_setn(headers, "Accept-Encoding",
"svndiff1;q=0.9,svndiff;q=0.8");
}
@@ -922,7 +875,7 @@ cancel_fetch(serf_request_t *request,
int status_code,
void *baton)
{
- report_fetch_t *fetch_ctx = baton;
+ fetch_ctx_t *fetch_ctx = baton;
/* Uh-oh. Our connection died on us.
*
@@ -952,30 +905,6 @@ cancel_fetch(serf_request_t *request,
SVN_ERR_MALFUNCTION();
}
-static svn_error_t *
-error_fetch(serf_request_t *request,
- report_fetch_t *fetch_ctx,
- svn_error_t *err)
-{
- 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;
-
- /* Discard the rest of this request
- (This makes sure it doesn't error when the request is aborted later) */
- serf_request_set_handler(request,
- svn_ra_serf__response_discard_handler, NULL);
-
- /* Some errors would be handled by serf; make sure they really make
- the update fail by wrapping it in a different error. */
- if (!SERF_BUCKET_READ_ERROR(err->apr_err))
- return svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
-
- 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
@@ -984,95 +913,82 @@ error_fetch(serf_request_t *request,
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,
+open_file_txdelta(file_baton_t *file,
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(ensure_dir_opened(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);
- }
+ const svn_delta_editor_t *editor = file->parent_dir->ctx->editor;
- /* 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));
- }
+ SVN_ERR_ASSERT(file->txdelta == NULL);
- /* Check for lock information. */
- if (info->lock_token)
- check_lock(info);
+ SVN_ERR(ensure_file_opened(file, 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));
- }
+ SVN_ERR(editor->apply_textdelta(file->file_baton,
+ svn_checksum_to_cstring(
+ file->base_md5_checksum,
+ scratch_pool),
+ file->pool,
+ &file->txdelta,
+ &file->txdelta_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(). */
+/* Close the file, handling loose ends and cleanup */
static svn_error_t *
-close_updated_file(report_info_t *info,
- apr_pool_t *scratch_pool)
+close_file(file_baton_t *file,
+ apr_pool_t *scratch_pool)
{
- report_context_t *ctx = info->dir->report_context;
+ dir_baton_t *parent_dir = file->parent_dir;
+ report_context_t *ctx = parent_dir->ctx;
+
+ SVN_ERR(ensure_file_opened(file, scratch_pool));
/* 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)
+ if (file->propfind_props)
{
- SVN_ERR(svn_ra_serf__walk_all_props(info->props,
- info->url,
+ apr_hash_index_t *hi;
+ SVN_ERR(svn_ra_serf__walk_all_props(file->propfind_props,
+ file->url,
ctx->target_rev,
- set_file_props, info,
+ set_file_props, file,
+ scratch_pool));
+
+ if (file->remove_props)
+ for (hi = apr_hash_first(scratch_pool, file->remove_props);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
+ svn__apr_hash_index_key(hi),
+ NULL /* value */,
+ scratch_pool));
+ }
+ }
+
+ /* Check for lock information. */
+ SVN_ERR(check_lock(file, scratch_pool));
+
+ if (file->url)
+ {
+ SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
+ SVN_RA_SERF__WC_CHECKED_IN_URL,
+ svn_string_create(file->url,
+ scratch_pool),
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));
+ SVN_ERR(ctx->editor->close_file(file->file_baton,
+ svn_checksum_to_cstring(
+ file->final_md5_checksum,
+ scratch_pool),
+ scratch_pool));
- /* We're done with our editor pool. */
- svn_pool_destroy(info->editor_pool);
+ svn_pool_destroy(file->pool);
+
+ SVN_ERR(maybe_close_dir(parent_dir)); /* Remove reference */
return SVN_NO_ERROR;
}
@@ -1087,8 +1003,8 @@ handle_fetch(serf_request_t *request,
const char *data;
apr_size_t len;
apr_status_t status;
- report_fetch_t *fetch_ctx = handler_baton;
- svn_error_t *err;
+ fetch_ctx_t *fetch_ctx = handler_baton;
+ file_baton_t *file = fetch_ctx->file;
/* ### new field. make sure we didn't miss some initialization. */
SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
@@ -1097,47 +1013,47 @@ handle_fetch(serf_request_t *request,
{
serf_bucket_t *hdrs;
const char *val;
- report_info_t *info;
+ file_baton_t *file;
+
+ /* 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)
+ {
+ fetch_ctx->handler->discard_body = TRUE;
+ return SVN_NO_ERROR; /* Will return an error in the DONE handler */
+ }
hdrs = serf_bucket_response_get_headers(response);
val = serf_bucket_headers_get(hdrs, "Content-Type");
- info = fetch_ctx->info;
+ file = fetch_ctx->file;
if (val && svn_cstring_casecmp(val, SVN_SVNDIFF_MIME_TYPE) == 0)
{
- fetch_ctx->delta_stream =
- svn_txdelta_parse_svndiff(info->textdelta,
- info->textdelta_baton,
- TRUE, info->editor_pool);
+ fetch_ctx->result_stream =
+ svn_txdelta_parse_svndiff(file->txdelta,
+ file->txdelta_baton,
+ TRUE, file->pool);
/* Validate the delta base claimed by the server matches
what we asked for! */
val = serf_bucket_headers_get(hdrs, SVN_DAV_DELTA_BASE_HEADER);
- if (val && (strcmp(val, info->delta_base) != 0))
+ if (val && (strcmp(val, fetch_ctx->delta_base) != 0))
{
- err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
- _("GET request returned unexpected "
- "delta base: %s"), val);
- return error_fetch(request, fetch_ctx, err);
+ return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+ _("GET request returned unexpected "
+ "delta base: %s"), val);
}
}
else
{
- fetch_ctx->delta_stream = NULL;
+ fetch_ctx->result_stream = NULL;
}
fetch_ctx->read_headers = TRUE;
}
- /* 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_trace(svn_ra_serf__unexpected_status(fetch_ctx->handler));
- return error_fetch(request, fetch_ctx, err);
- }
-
- while (1)
+ while (TRUE)
{
svn_txdelta_window_t delta_window = { 0 };
svn_txdelta_op_t delta_op;
@@ -1164,10 +1080,9 @@ handle_fetch(serf_request_t *request,
}
/* Skip on to the next iteration of this loop. */
- if (APR_STATUS_IS_EAGAIN(status))
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
+ if (status /* includes EAGAIN */)
+ return svn_ra_serf__wrap_err(status, NULL);
+
continue;
}
@@ -1177,17 +1092,12 @@ handle_fetch(serf_request_t *request,
/* Update data and len to just provide the new data. */
skip = len - (fetch_ctx->read_size - fetch_ctx->aborted_read_size);
data += skip;
- len -= skip;
+ len -= (apr_size_t)skip;
}
- if (fetch_ctx->delta_stream)
- {
- err = svn_stream_write(fetch_ctx->delta_stream, data, &len);
- if (err)
- {
- return error_fetch(request, fetch_ctx, err);
- }
- }
+ if (fetch_ctx->result_stream)
+ SVN_ERR(svn_stream_write(fetch_ctx->result_stream, data, &len));
+
/* otherwise, manually construct the text delta window. */
else if (len)
{
@@ -1204,372 +1114,214 @@ handle_fetch(serf_request_t *request,
delta_window.new_data = &window_data;
/* write to the file located in the info. */
- err = fetch_ctx->info->textdelta(&delta_window,
- fetch_ctx->info->textdelta_baton);
- if (err)
- {
- return error_fetch(request, fetch_ctx, err);
- }
+ SVN_ERR(file->txdelta(&delta_window, file->txdelta_baton));
}
if (APR_STATUS_IS_EOF(status))
{
- report_info_t *info = fetch_ctx->info;
-
- if (fetch_ctx->delta_stream)
- err = svn_error_trace(svn_stream_close(fetch_ctx->delta_stream));
+ if (fetch_ctx->result_stream)
+ SVN_ERR(svn_stream_close(fetch_ctx->result_stream));
else
- err = svn_error_trace(info->textdelta(NULL,
- info->textdelta_baton));
- if (err)
- {
- return error_fetch(request, fetch_ctx, err);
- }
+ SVN_ERR(file->txdelta(NULL, file->txdelta_baton));
+ }
- err = close_updated_file(info, info->pool);
- if (err)
- {
- return svn_error_trace(error_fetch(request, fetch_ctx, err));
- }
+ /* Report EOF, EEAGAIN and other special errors to serf */
+ if (status)
+ return svn_ra_serf__wrap_err(status, NULL);
+ }
+}
+
+/* --------------------------------------------------------- */
- 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;
+/* Implements svn_ra_serf__response_done_delegate_t */
+static svn_error_t *
+file_props_done(serf_request_t *request,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ file_baton_t *file = baton;
+ svn_ra_serf__handler_t *handler = file->propfind_handler;
- /* We're done with our pool. */
- svn_pool_destroy(info->pool);
+ if (handler->server_error)
+ return svn_error_trace(svn_ra_serf__server_error_create(handler,
+ scratch_pool));
- if (status)
- return svn_ra_serf__wrap_err(status, NULL);
- }
- if (APR_STATUS_IS_EAGAIN(status))
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
- }
- /* not reached */
+ if (handler->sline.code != 207)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
+
+ file->parent_dir->ctx->num_active_propfinds--;
+
+ file->fetch_props = FALSE;
+
+ if (file->fetch_file)
+ return SVN_NO_ERROR; /* Still processing file request */
+
+ /* Closing the file will automatically deliver the propfind props.
+ *
+ * Note that closing the directory may dispose the pool containing the
+ * handler, which is only a valid operation in this callback, as only
+ * after this callback our serf plumbing assumes the request is done. */
+
+ return svn_error_trace(close_file(file, scratch_pool));
}
-/* Implements svn_ra_serf__response_handler_t */
static svn_error_t *
-handle_stream(serf_request_t *request,
- serf_bucket_t *response,
- void *handler_baton,
- apr_pool_t *pool)
+file_fetch_done(serf_request_t *request,
+ void *baton,
+ apr_pool_t *scratch_pool)
{
- report_fetch_t *fetch_ctx = handler_baton;
- svn_error_t *err;
- apr_status_t status;
+ fetch_ctx_t *fetch_ctx = baton;
+ file_baton_t *file = fetch_ctx->file;
+ svn_ra_serf__handler_t *handler = fetch_ctx->handler;
- /* ### new field. make sure we didn't miss some initialization. */
- SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
+ if (handler->server_error)
+ return svn_error_trace(svn_ra_serf__server_error_create(handler,
+ scratch_pool));
- err = svn_ra_serf__error_on_status(fetch_ctx->handler->sline,
- fetch_ctx->info->name,
- fetch_ctx->handler->location);
- if (err)
- {
- fetch_ctx->handler->done = TRUE;
+ if (handler->sline.code != 200)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
- err = svn_error_compose_create(
- err,
- svn_ra_serf__handle_discard_body(request, response, NULL, pool));
+ file->parent_dir->ctx->num_active_fetches--;
- return svn_error_trace(err);
- }
+ file->fetch_file = FALSE;
- while (1)
+ if (file->fetch_props)
+ return SVN_NO_ERROR; /* Still processing PROPFIND request */
+
+ /* Closing the file will automatically deliver the propfind props.
+ *
+ * Note that closing the directory may dispose the pool containing the
+ * handler, fetch_ctx, etc. which is only a valid operation in this
+ * callback, as only after this callback our serf plumbing assumes the
+ * request is done. */
+ return svn_error_trace(close_file(file, scratch_pool));
+}
+
+/* Initiates additional requests needed for a file when not in "send-all" mode.
+ */
+static svn_error_t *
+fetch_for_file(file_baton_t *file,
+ apr_pool_t *scratch_pool)
+{
+ report_context_t *ctx = file->parent_dir->ctx;
+ svn_ra_serf__connection_t *conn;
+ svn_ra_serf__handler_t *handler;
+
+ /* Open extra connections if we have enough requests to send. */
+ if (ctx->sess->num_conns < ctx->sess->max_connections)
+ SVN_ERR(open_connection_if_needed(ctx->sess, ctx->num_active_fetches +
+ ctx->num_active_propfinds));
+
+ /* What connection should we go on? */
+ conn = get_best_connection(ctx);
+
+ /* Note that we (still) use conn for both requests.. Should we send
+ them out on different connections? */
+
+ if (file->fetch_file)
{
- const char *data;
- apr_size_t len;
+ SVN_ERR(open_file_txdelta(file, scratch_pool));
- status = serf_bucket_read(response, 8000, &data, &len);
- if (SERF_BUCKET_READ_ERROR(status))
+ if (!ctx->text_deltas
+ || file->txdelta == svn_delta_noop_window_handler)
{
- return svn_ra_serf__wrap_err(status, NULL);
+ SVN_ERR(file->txdelta(NULL, file->txdelta_baton));
+ file->fetch_file = FALSE;
}
- fetch_ctx->read_size += len;
-
- if (fetch_ctx->aborted_read)
+ if (file->fetch_file
+ && file->final_sha1_checksum
+ && ctx->sess->wc_callbacks->get_wc_contents)
{
- /* We haven't caught up to where we were before. */
- if (fetch_ctx->read_size < fetch_ctx->aborted_read_size)
- {
- /* Eek. What did the file shrink or something? */
- if (APR_STATUS_IS_EOF(status))
- {
- SVN_ERR_MALFUNCTION();
- }
+ svn_error_t *err;
+ svn_stream_t *cached_contents = NULL;
- /* Skip on to the next iteration of this loop. */
- if (APR_STATUS_IS_EAGAIN(status))
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
- continue;
- }
-
- /* Woo-hoo. We're back. */
- fetch_ctx->aborted_read = FALSE;
-
- /* Increment data and len by the difference. */
- data += fetch_ctx->read_size - fetch_ctx->aborted_read_size;
- len += fetch_ctx->read_size - fetch_ctx->aborted_read_size;
- }
-
- if (len)
- {
- apr_size_t written_len;
-
- written_len = len;
-
- SVN_ERR(svn_stream_write(fetch_ctx->target_stream, data,
- &written_len));
- }
-
- if (APR_STATUS_IS_EOF(status))
- {
- fetch_ctx->done = TRUE;
- }
-
- if (status)
- {
- return svn_ra_serf__wrap_err(status, NULL);
- }
- }
- /* not reached */
-}
-
-/* Close the directory represented by DIR -- and any suitable parents
- thereof -- if we are able to do so. This is the case whenever:
-
- - there are no remaining open items within the directory, and
- - the directory's XML close tag has been processed (so we know
- there are no more children to worry about in the future), and
- - either:
- - we aren't fetching properties for this directory, or
- - we've already finished fetching those properties.
-*/
-static svn_error_t *
-maybe_close_dir_chain(report_dir_t *dir)
-{
- report_dir_t *cur_dir = dir;
-
- SVN_ERR(ensure_dir_opened(cur_dir));
-
- while (cur_dir
- && !cur_dir->ref_count
- && cur_dir->tag_closed
- && (!cur_dir->fetch_props || cur_dir->propfind_handler->done))
- {
- report_dir_t *parent = cur_dir->parent_dir;
- report_context_t *report_context = cur_dir->report_context;
- svn_boolean_t propfind_in_done_list = FALSE;
- svn_ra_serf__list_t *done_list;
+ err = ctx->sess->wc_callbacks->get_wc_contents(
+ ctx->sess->wc_callback_baton,
+ &cached_contents,
+ file->final_sha1_checksum,
+ scratch_pool);
- /* Make sure there are no references to this dir in the
- active_dir_propfinds list. If there are, don't close the
- directory -- which would delete the pool from which the
- relevant active_dir_propfinds list item is allocated -- and
- of course don't crawl upward to check the parents for
- a closure opportunity, either. */
- done_list = report_context->active_dir_propfinds;
- while (done_list)
- {
- if (done_list->data == cur_dir)
+ if (err || !cached_contents)
+ svn_error_clear(err); /* ### Can we return some/most errors? */
+ else
{
- propfind_in_done_list = TRUE;
- break;
+ /* ### For debugging purposes we could validate the md5 here,
+ but our implementations in libsvn_client already do that
+ for us... */
+ SVN_ERR(svn_txdelta_send_stream(cached_contents,
+ file->txdelta,
+ file->txdelta_baton,
+ NULL, scratch_pool));
+ SVN_ERR(svn_stream_close(cached_contents));
+ file->fetch_file = FALSE;
}
- done_list = done_list->next;
}
- if (propfind_in_done_list)
- break;
-
- SVN_ERR(close_dir(cur_dir));
- if (parent)
- {
- parent->ref_count--;
- }
- else
- {
- report_context->closed_root = TRUE;
- }
- cur_dir = parent;
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Open the file associated with INFO for editing, pass along any
- propchanges we've recorded for it, and then close the file. */
-static svn_error_t *
-handle_propchange_only(report_info_t *info,
- apr_pool_t *scratch_pool)
-{
- 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->dir->ref_count--;
-
- /* See if the parent directory of this file (and perhaps even
- parents of that) can be closed now. */
- SVN_ERR(maybe_close_dir_chain(info->dir));
-
- return SVN_NO_ERROR;
-}
-
-/* "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. In editor-speak, this will add/open the file, transmit any
- property changes, handle the contents, and then close the file. */
-static svn_error_t *
-handle_local_content(report_info_t *info,
- apr_pool_t *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));
-
- /* We're done with our pool. */
- svn_pool_destroy(info->pool);
-
- info->dir->ref_count--;
-
- /* See if the parent directory of this fetched item (and
- perhaps even parents of that) can be closed now. */
- SVN_ERR(maybe_close_dir_chain(info->dir));
-
- return SVN_NO_ERROR;
-}
-
-/* --------------------------------------------------------- */
-
-static svn_error_t *
-fetch_file(report_context_t *ctx, report_info_t *info)
-{
- svn_ra_serf__connection_t *conn;
- svn_ra_serf__handler_t *handler;
- /* What connection should we go on? */
- conn = get_best_connection(ctx);
-
- /* If needed, create the PROPFIND to retrieve the file's properties. */
- info->propfind_handler = NULL;
- if (info->fetch_props)
- {
- SVN_ERR(svn_ra_serf__deliver_props(&info->propfind_handler, info->props,
- ctx->sess, conn, info->url,
- ctx->target_rev, "0", all_props,
- &ctx->done_propfinds,
- info->dir->pool));
- SVN_ERR_ASSERT(info->propfind_handler);
-
- /* Create a serf request for the PROPFIND. */
- svn_ra_serf__request_create(info->propfind_handler);
-
- ctx->num_active_propfinds++;
- }
-
- /* If we've been asked to fetch the file or it's an add, do so.
- * Otherwise, handle the case where only the properties changed.
- */
- if (info->fetch_file && ctx->text_deltas)
- {
- svn_stream_t *contents = NULL;
-
- /* Open the file for editing. */
- SVN_ERR(open_updated_file(info, FALSE, info->pool));
-
- if (info->textdelta == svn_delta_noop_window_handler)
+ if (file->fetch_file)
{
- /* There is nobody looking for an actual stream.
+ fetch_ctx_t *fetch_ctx;
- Just report an empty stream instead of fetching
- to be ingored data */
- info->cached_contents = svn_stream_empty(info->pool);
- }
- else if (ctx->sess->wc_callbacks->get_wc_contents
- && info->final_sha1_checksum)
- {
- svn_error_t *err = NULL;
- svn_checksum_t *checksum = NULL;
-
- /* Parse the optional SHA1 checksum (1.7+) */
- err = svn_checksum_parse_hex(&checksum, svn_checksum_sha1,
- info->final_sha1_checksum,
- info->pool);
+ /* Let's fetch the file with a GET request... */
+ SVN_ERR_ASSERT(file->url && file->repos_relpath);
- /* Okay so far? Let's try to get a stream on some readily
- available matching content. */
- if (!err && checksum)
- {
- err = ctx->sess->wc_callbacks->get_wc_contents(
- ctx->sess->wc_callback_baton, &contents,
- checksum, info->pool);
-
- if (! err)
- info->cached_contents = contents;
- }
+ /* Otherwise, we use a GET request for the file's contents. */
- 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);
- }
- }
+ fetch_ctx = apr_pcalloc(file->pool, sizeof(*fetch_ctx));
+ fetch_ctx->file = file;
+ fetch_ctx->sess = ctx->sess;
+ fetch_ctx->conn = conn;
- /* If the working copy can provide cached contents for this
- file, we don't have to fetch them from the server. */
- if (info->cached_contents)
- {
- /* If we'll be doing a PROPFIND for this file... */
- if (info->propfind_handler)
+ /* Can we somehow get away with just obtaining a DIFF? */
+ if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->sess))
{
- /* ... then we'll just leave ourselves a little "todo"
- about that fact (and we'll deal with the file content
- stuff later, after we've handled that PROPFIND
- response. */
- svn_ra_serf__list_t *list_item;
+ /* If this file is switched vs the editor root we should provide
+ its real url instead of the one calculated from the session root.
+ */
+ if (SVN_IS_VALID_REVNUM(file->base_rev))
+ {
+ fetch_ctx->delta_base = apr_psprintf(file->pool, "%s/%ld/%s",
+ ctx->sess->rev_root_stub,
+ file->base_rev,
+ svn_path_uri_encode(
+ file->repos_relpath,
+ file->pool));
+ }
+ else if (file->copyfrom_path)
+ {
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(file->copyfrom_rev));
- list_item = apr_pcalloc(info->dir->pool, sizeof(*list_item));
- list_item->data = info;
- list_item->next = ctx->file_propchanges_only;
- ctx->file_propchanges_only = list_item;
+ fetch_ctx->delta_base = apr_psprintf(file->pool, "%s/%ld/%s",
+ ctx->sess->rev_root_stub,
+ file->copyfrom_rev,
+ svn_path_uri_encode(
+ file->copyfrom_path+1,
+ file->pool));
+ }
}
- else
+ else if (ctx->sess->wc_callbacks->get_wc_prop)
{
- /* Otherwise, if we've no PROPFIND to do, we might as
- well take care of those locally accessible file
- contents now. */
- SVN_ERR(handle_local_content(info, info->pool));
+ /* If we have a WC, we might be able to dive all the way into the WC
+ * to get the previous URL so we can do a differential GET with the
+ * base URL.
+ */
+ const svn_string_t *value = NULL;
+ SVN_ERR(ctx->sess->wc_callbacks->get_wc_prop(
+ ctx->sess->wc_callback_baton,
+ file->relpath,
+ SVN_RA_SERF__WC_CHECKED_IN_URL,
+ &value, scratch_pool));
+
+ fetch_ctx->delta_base = value
+ ? apr_pstrdup(file->pool, value->data)
+ : NULL;
}
- }
- else
- {
- /* Otherwise, we use a GET request for the file's contents. */
- report_fetch_t *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 = svn_ra_serf__create_handler(info->dir->pool);
+ handler = svn_ra_serf__create_handler(file->pool);
handler->method = "GET";
- handler->path = fetch_ctx->info->url;
+ handler->path = file->url;
handler->conn = conn;
handler->session = ctx->sess;
@@ -1585,6 +1337,9 @@ fetch_file(report_context_t *ctx, report
handler->response_error = cancel_fetch;
handler->response_error_baton = fetch_ctx;
+ handler->done_delegate = file_fetch_done;
+ handler->done_delegate_baton = fetch_ctx;
+
fetch_ctx->handler = handler;
svn_ra_serf__request_create(handler);
@@ -1592,991 +1347,573 @@ fetch_file(report_context_t *ctx, report
ctx->num_active_fetches++;
}
}
- else if (info->propfind_handler)
- {
- svn_ra_serf__list_t *list_item;
-
- list_item = apr_pcalloc(info->dir->pool, sizeof(*list_item));
- list_item->data = info;
- list_item->next = ctx->file_propchanges_only;
- ctx->file_propchanges_only = list_item;
- }
- else
- {
- /* No propfind or GET request. Just handle the prop changes now. */
- SVN_ERR(handle_propchange_only(info, info->pool));
- }
-
- return SVN_NO_ERROR;
-}
-
-
-/** XML callbacks for our update-report response parsing */
-
-static svn_error_t *
-start_report(svn_ra_serf__xml_parser_t *parser,
- svn_ra_serf__dav_props_t name,
- const char **attrs,
- apr_pool_t *scratch_pool)
-{
- report_context_t *ctx = parser->user_data;
- report_state_e state;
-
- state = parser->state->current_state;
-
- if (state == NONE && strcmp(name.name, "update-report") == 0)
- {
- const char *val;
-
- val = svn_xml_get_attr_value("inline-props", attrs);
- if (val && (strcmp(val, "true") == 0))
- ctx->add_props_included = TRUE;
-
- val = svn_xml_get_attr_value("send-all", attrs);
- if (val && (strcmp(val, "true") == 0))
- {
- ctx->send_all_mode = TRUE;
- /* All properties are included in send-all mode. */
- ctx->add_props_included = TRUE;
- }
- }
- else if (state == NONE && strcmp(name.name, "target-revision") == 0)
+ /* If needed, create the PROPFIND to retrieve the file's properties. */
+ if (file->fetch_props)
{
- const char *rev;
+ file->propfind_props = apr_hash_make(file->pool);
+ SVN_ERR(svn_ra_serf__deliver_props(&file->propfind_handler,
+ file->propfind_props,
+ ctx->sess, conn, file->url,
+ ctx->target_rev, "0", all_props,
+ NULL, file->pool));
+ SVN_ERR_ASSERT(file->propfind_handler);
- rev = svn_xml_get_attr_value("rev", attrs);
+ file->propfind_handler->done_delegate = file_props_done;
+ file->propfind_handler->done_delegate_baton = file;
- if (!rev)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in target-revision element"));
- }
+ /* Create a serf request for the PROPFIND. */
+ svn_ra_serf__request_create(file->propfind_handler);
- SVN_ERR(ctx->update_editor->set_target_revision(ctx->update_baton,
- SVN_STR_TO_REV(rev),
- ctx->sess->pool));
+ ctx->num_active_propfinds++;
}
- else if (state == NONE && strcmp(name.name, "open-directory") == 0)
- {
- const char *rev;
- report_info_t *info;
-
- rev = svn_xml_get_attr_value("rev", attrs);
-
- if (!rev)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-directory element"));
- }
- info = push_state(parser, ctx, OPEN_DIR);
-
- info->base_rev = SVN_STR_TO_REV(rev);
- info->dir->base_rev = info->base_rev;
- info->fetch_props = TRUE;
-
- info->dir->base_name = "";
- info->dir->name = "";
+ if (file->fetch_props || file->fetch_file)
+ return SVN_NO_ERROR;
- info->base_name = info->dir->base_name;
- info->name = info->dir->name;
- info->dir->repos_relpath = svn_hash_gets(ctx->switched_paths, "");
+ /* Somehow we are done; probably via the local cache.
+ Close the file and release memory, etc. */
- if (!info->dir->repos_relpath)
- SVN_ERR(svn_ra_serf__get_relative_path(&info->dir->repos_relpath,
- ctx->sess->session_url.path,
- ctx->sess, ctx->conn,
- info->dir->pool));
- }
- else if (state == NONE)
- {
- /* do nothing as we haven't seen our valid start tag yet. */
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "open-directory") == 0)
- {
- const char *rev, *dirname;
- report_dir_t *dir;
- report_info_t *info;
-
- rev = svn_xml_get_attr_value("rev", attrs);
-
- if (!rev)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-directory element"));
- }
+ return svn_error_trace(close_file(file, scratch_pool));
+}
- dirname = svn_xml_get_attr_value("name", attrs);
+/* Implements svn_ra_serf__response_done_delegate_t */
+static svn_error_t *
+dir_props_done(serf_request_t *request,
+ void *baton,
+ apr_pool_t *scratch_pool)
+{
+ dir_baton_t *dir = baton;
+ svn_ra_serf__handler_t *handler = dir->propfind_handler;
- if (!dirname)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in open-directory element"));
- }
+ if (handler->server_error)
+ return svn_ra_serf__server_error_create(handler, scratch_pool);
- info = push_state(parser, ctx, OPEN_DIR);
+ if (handler->sline.code != 207)
+ return svn_error_trace(svn_ra_serf__unexpected_status(handler));
- dir = info->dir;
+ dir->ctx->num_active_propfinds--;
- info->base_rev = SVN_STR_TO_REV(rev);
- dir->base_rev = info->base_rev;
+ /* Closing the directory will automatically deliver the propfind props.
+ *
+ * Note that closing the directory may dispose the pool containing the
+ * handler, which is only a valid operation in this callback, as after
+ * this callback serf assumes the request is done. */
- info->fetch_props = FALSE;
+ return svn_error_trace(maybe_close_dir(dir));
+}
- dir->base_name = apr_pstrdup(dir->pool, dirname);
- info->base_name = dir->base_name;
+/* Initiates additional requests needed for a directory when not in "send-all"
+ * mode */
+static svn_error_t *
+fetch_for_dir(dir_baton_t *dir,
+ apr_pool_t *scratch)
+{
+ report_context_t *ctx = dir->ctx;
+ svn_ra_serf__connection_t *conn;
- /* Expand our name. */
- dir->name = svn_relpath_join(dir->parent_dir->name, dir->base_name,
- dir->pool);
- info->name = dir->name;
+ /* Open extra connections if we have enough requests to send. */
+ if (ctx->sess->num_conns < ctx->sess->max_connections)
+ SVN_ERR(open_connection_if_needed(ctx->sess, ctx->num_active_fetches +
+ ctx->num_active_propfinds));
- dir->repos_relpath = svn_hash_gets(ctx->switched_paths, dir->name);
+ /* What connection should we go on? */
+ conn = get_best_connection(ctx);
- if (!dir->repos_relpath)
- dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath,
- dir->base_name, dir->pool);
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "add-directory") == 0)
+ /* If needed, create the PROPFIND to retrieve the file's properties. */
+ if (dir->fetch_props)
{
- const char *dir_name, *cf, *cr;
- report_dir_t *dir;
- report_info_t *info;
-
- dir_name = svn_xml_get_attr_value("name", attrs);
- if (!dir_name)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in add-directory element"));
- }
- cf = svn_xml_get_attr_value("copyfrom-path", attrs);
- cr = svn_xml_get_attr_value("copyfrom-rev", attrs);
-
- info = push_state(parser, ctx, ADD_DIR);
-
- dir = info->dir;
-
- dir->base_name = apr_pstrdup(dir->pool, dir_name);
- info->base_name = dir->base_name;
-
- /* Expand our name. */
- dir->name = svn_relpath_join(dir->parent_dir->name, dir->base_name,
- dir->pool);
- info->name = dir->name;
-
- info->copyfrom_path = cf ? apr_pstrdup(info->pool, cf) : NULL;
- info->copyfrom_rev = cr ? SVN_STR_TO_REV(cr) : SVN_INVALID_REVNUM;
+ dir->propfind_props = apr_hash_make(dir->pool);
+ SVN_ERR(svn_ra_serf__deliver_props(&dir->propfind_handler,
+ dir->propfind_props,
+ ctx->sess, conn, dir->url,
+ ctx->target_rev, "0", all_props,
+ NULL,
+ dir->pool));
+ SVN_ERR_ASSERT(dir->propfind_handler);
- /* Mark that we don't have a base. */
- info->base_rev = SVN_INVALID_REVNUM;
- dir->base_rev = info->base_rev;
+ dir->propfind_handler->done_delegate = dir_props_done;
+ dir->propfind_handler->done_delegate_baton = dir;
- /* If the server isn't included properties for added items,
- we'll need to fetch them ourselves. */
- if (! ctx->add_props_included)
- dir->fetch_props = TRUE;
+ /* Create a serf request for the PROPFIND. */
+ svn_ra_serf__request_create(dir->propfind_handler);
- dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath,
- dir->base_name, dir->pool);
+ ctx->num_active_propfinds++;
}
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "open-file") == 0)
- {
- const char *file_name, *rev;
- report_info_t *info;
-
- file_name = svn_xml_get_attr_value("name", attrs);
-
- if (!file_name)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in open-file element"));
- }
-
- rev = svn_xml_get_attr_value("rev", attrs);
+ else
+ SVN_ERR_MALFUNCTION();
- if (!rev)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing revision attr in open-file element"));
- }
+ return SVN_NO_ERROR;
+}
- info = push_state(parser, ctx, OPEN_FILE);
+
+/** XML callbacks for our update-report response parsing */
- info->base_rev = SVN_STR_TO_REV(rev);
- info->fetch_props = FALSE;
+/* 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;
+ apr_hash_t *attrs;
- info->base_name = apr_pstrdup(info->pool, file_name);
- info->name = NULL;
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "add-file") == 0)
+ switch (entered_state)
{
- const char *file_name, *cf, *cr;
- report_info_t *info;
-
- file_name = svn_xml_get_attr_value("name", attrs);
- cf = svn_xml_get_attr_value("copyfrom-path", attrs);
- cr = svn_xml_get_attr_value("copyfrom-rev", attrs);
-
- if (!file_name)
+ case UPDATE_REPORT:
{
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in add-file element"));
- }
-
- info = push_state(parser, ctx, ADD_FILE);
+ const char *val;
- info->base_rev = SVN_INVALID_REVNUM;
+ attrs = svn_ra_serf__xml_gather_since(xes, UPDATE_REPORT);
+ val = svn_hash_gets(attrs, "inline-props");
- /* If the server isn't in "send-all" mode, we should expect to
- fetch contents for added files. */
- if (! ctx->send_all_mode)
- info->fetch_file = TRUE;
+ if (val && (strcmp(val, "true") == 0))
+ ctx->add_props_included = TRUE;
- /* If the server isn't included properties for added items,
- we'll need to fetch them ourselves. */
- if (! ctx->add_props_included)
- info->fetch_props = TRUE;
+ val = svn_hash_gets(attrs, "send-all");
- info->base_name = apr_pstrdup(info->pool, file_name);
- info->name = NULL;
-
- info->copyfrom_path = cf ? apr_pstrdup(info->pool, cf) : NULL;
- info->copyfrom_rev = cr ? SVN_STR_TO_REV(cr) : SVN_INVALID_REVNUM;
-
- info->final_sha1_checksum =
- svn_xml_get_attr_value("sha1-checksum", attrs);
- if (info->final_sha1_checksum)
- info->final_sha1_checksum = apr_pstrdup(info->pool,
- info->final_sha1_checksum);
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "delete-entry") == 0)
- {
- const char *file_name;
- const char *rev_str;
- report_info_t *info;
- apr_pool_t *tmppool;
- const char *full_path;
- svn_revnum_t delete_rev = SVN_INVALID_REVNUM;
-
- file_name = svn_xml_get_attr_value("name", attrs);
+ if (val && (strcmp(val, "true") == 0))
+ {
+ ctx->send_all_mode = TRUE;
- if (!file_name)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in delete-entry element"));
+ /* All properties are included in send-all mode. */
+ ctx->add_props_included = TRUE;
+ }
}
+ break;
- rev_str = svn_xml_get_attr_value("rev", attrs);
- if (rev_str) /* Not available on older repositories! */
- delete_rev = SVN_STR_TO_REV(rev_str);
-
- info = parser->state->private;
-
- SVN_ERR(ensure_dir_opened(info->dir));
-
- tmppool = svn_pool_create(info->dir->dir_baton_pool);
-
- full_path = svn_relpath_join(info->dir->name, file_name, tmppool);
-
- SVN_ERR(ctx->update_editor->delete_entry(full_path,
- delete_rev,
- info->dir->dir_baton,
- tmppool));
-
- svn_pool_destroy(tmppool);
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "absent-directory") == 0)
- {
- const char *file_name;
- report_info_t *info;
-
- file_name = svn_xml_get_attr_value("name", attrs);
-
- if (!file_name)
+ case OPEN_DIR:
+ case ADD_DIR:
{
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in absent-directory element"));
- }
+ dir_baton_t *dir;
+ attrs = svn_ra_serf__xml_gather_since(xes, entered_state);
- info = parser->state->private;
+ const char *name = svn_hash_gets(attrs, "name");
+ if (!name)
+ name = "";
- SVN_ERR(ensure_dir_opened(info->dir));
+ SVN_ERR(create_dir_baton(&dir, ctx, name, scratch_pool));
- SVN_ERR(ctx->update_editor->absent_directory(
- svn_relpath_join(info->name, file_name,
- info->dir->pool),
- info->dir->dir_baton,
- info->dir->pool));
- }
- else if ((state == OPEN_DIR || state == ADD_DIR) &&
- strcmp(name.name, "absent-file") == 0)
- {
- const char *file_name;
- report_info_t *info;
-
- file_name = svn_xml_get_attr_value("name", attrs);
+ if (entered_state == OPEN_DIR)
+ {
+ apr_int64_t base_rev;
- if (!file_name)
- {
- return svn_error_create(
- SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in absent-file element"));
- }
+ SVN_ERR(svn_cstring_atoi64(&base_rev,
+ svn_hash_gets(attrs, "rev")));
+ dir->base_rev = (svn_revnum_t)base_rev;
+ }
+ else
+ {
+ dir->copyfrom_path = svn_hash_gets(attrs, "copyfrom-path");
- info = parser->state->private;
+ if (dir->copyfrom_path)
+ {
+ apr_int64_t copyfrom_rev;
+ const char *copyfrom_rev_str;
+ dir->copyfrom_path = svn_fspath__canonicalize(
+ dir->copyfrom_path,
+ dir->pool);
+
+ copyfrom_rev_str = svn_hash_gets(attrs, "copyfrom-rev");
+
+ if (!copyfrom_rev_str)
+ return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND,
+ NULL,
+ _("Missing '%s' attribute"),
+ "copyfrom-rev");
- SVN_ERR(ensure_dir_opened(info->dir));
+ SVN_ERR(svn_cstring_atoi64(©from_rev, copyfrom_rev_str));
- SVN_ERR(ctx->update_editor->absent_file(
- svn_relpath_join(info->name, file_name,
- info->dir->pool),
- info->dir->dir_baton,
- info->dir->pool));
- }
- else if (state == OPEN_DIR || state == ADD_DIR)
- {
- report_info_t *info;
+ dir->copyfrom_rev = (svn_revnum_t)copyfrom_rev;
+ }
- if (strcmp(name.name, "checked-in") == 0)
- {
- info = push_state(parser, ctx, IGNORE_PROP_NAME);
- info->prop_ns = name.xmlns;
- info->prop_name = apr_pstrdup(parser->state->pool, name.name);
- info->prop_encoding = NULL;
- svn_stringbuf_setempty(info->prop_value);
+ if (! ctx->add_props_included)
+ dir->fetch_props = TRUE;
+ }
}
- else if (strcmp(name.name, "set-prop") == 0 ||
- strcmp(name.name, "remove-prop") == 0)
+ break;
+ case OPEN_FILE:
+ case ADD_FILE:
{
- const char *full_prop_name;
- const char *colon;
+ file_baton_t *file;
- info = push_state(parser, ctx, PROP);
+ attrs = svn_ra_serf__xml_gather_since(xes, entered_state);
- full_prop_name = svn_xml_get_attr_value("name", attrs);
- if (!full_prop_name)
- {
- return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
- _("Missing name attr in %s element"),
- name.name);
- }
+ SVN_ERR(create_file_baton(&file, ctx, svn_hash_gets(attrs, "name"),
+ scratch_pool));
- colon = strchr(full_prop_name, ':');
[... 1446 lines stripped ...]