diff options
Diffstat (limited to 'subversion/libsvn_ra_serf/serf.c')
-rw-r--r-- | subversion/libsvn_ra_serf/serf.c | 448 |
1 files changed, 276 insertions, 172 deletions
diff --git a/subversion/libsvn_ra_serf/serf.c b/subversion/libsvn_ra_serf/serf.c index 391f031..66f9962 100644 --- a/subversion/libsvn_ra_serf/serf.c +++ b/subversion/libsvn_ra_serf/serf.c @@ -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,21 @@ ra_serf_version(void) #define RA_SERF_DESCRIPTION \ N_("Module for accessing a repository via WebDAV protocol using serf.") +#define RA_SERF_DESCRIPTION_VER \ + N_("Module for accessing a repository via WebDAV protocol using serf.\n" \ + " - using serf %d.%d.%d") + +/* Implements svn_ra__vtable_t.get_description(). */ static const char * -ra_serf_get_description(void) +ra_serf_get_description(apr_pool_t *pool) { - return _(RA_SERF_DESCRIPTION); + int major, minor, patch; + + serf_lib_version(&major, &minor, &patch); + return apr_psprintf(pool, _(RA_SERF_DESCRIPTION_VER), major, minor, patch); } +/* Implements svn_ra__vtable_t.get_schemes(). */ static const char * const * ra_serf_get_schemes(apr_pool_t *pool) { @@ -103,12 +111,11 @@ load_http_auth_types(apr_pool_t *pool, svn_config_t *config, if (http_auth_types) { - char *token, *last; + char *token; char *auth_types_list = apr_palloc(pool, strlen(http_auth_types) + 1); apr_collapse_spaces(auth_types_list, http_auth_types); - while ((token = apr_strtok(auth_types_list, ";", &last)) != NULL) + while ((token = svn_cstring_tokenize(";", &auth_types_list)) != NULL) { - auth_types_list = NULL; if (svn_cstring_casecmp("basic", token) == 0) *authn_types |= SERF_AUTHN_BASIC; else if (svn_cstring_casecmp("digest", token) == 0) @@ -119,8 +126,9 @@ load_http_auth_types(apr_pool_t *pool, svn_config_t *config, *authn_types |= SERF_AUTHN_NEGOTIATE; else return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, - _("Invalid config: unknown http auth" - "type '%s'"), token); + _("Invalid config: unknown %s " + "'%s'"), + SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, token); } } else @@ -131,7 +139,15 @@ load_http_auth_types(apr_pool_t *pool, svn_config_t *config, return SVN_NO_ERROR; } -#define DEFAULT_HTTP_TIMEOUT 3600 + +/* Default HTTP timeout (in seconds); overridden by the 'http-timeout' + runtime configuration variable. */ +#define DEFAULT_HTTP_TIMEOUT 600 + +/* Private symbol for the 1.9-public SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS */ +#define OPTION_HTTP_CHUNKED_REQUESTS "http-chunked-requests" + + static svn_error_t * load_config(svn_ra_serf__session_t *session, apr_hash_t *config_hash, @@ -144,14 +160,12 @@ load_config(svn_ra_serf__session_t *session, const char *timeout_str = NULL; const char *exceptions; apr_port_t proxy_port; - svn_boolean_t is_exception = FALSE; + svn_tristate_t chunked_requests; if (config_hash) { - config = apr_hash_get(config_hash, SVN_CONFIG_CATEGORY_SERVERS, - APR_HASH_KEY_STRING); - config_client = apr_hash_get(config_hash, SVN_CONFIG_CATEGORY_CONFIG, - APR_HASH_KEY_STRING); + config = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_SERVERS); + config_client = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_CONFIG); } else { @@ -184,16 +198,11 @@ load_config(svn_ra_serf__session_t *session, /* Use the default proxy-specific settings if and only if "http-proxy-exceptions" is not set to exclude this host. */ svn_config_get(config, &exceptions, SVN_CONFIG_SECTION_GLOBAL, - SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS, NULL); - if (exceptions) - { - apr_array_header_t *l = svn_cstring_split(exceptions, ",", TRUE, pool); - is_exception = svn_cstring_match_glob_list(session->session_url.hostname, - l); - } - if (! is_exception) + SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS, ""); + if (! svn_cstring_match_glob_list(session->session_url.hostname, + svn_cstring_split(exceptions, ",", + TRUE, pool))) { - /* Load the global proxy server settings, if set. */ svn_config_get(config, &proxy_host, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_PROXY_HOST, NULL); svn_config_get(config, &port_str, SVN_CONFIG_SECTION_GLOBAL, @@ -214,6 +223,26 @@ load_config(svn_ra_serf__session_t *session, svn_config_get(config, &session->ssl_authorities, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, NULL); + /* If set, read the flag that tells us to do bulk updates or not. Defaults + to skelta updates. */ + SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates, + SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, + "auto", + svn_tristate_unknown)); + + /* Load the maximum number of parallel session connections. */ + SVN_ERR(svn_config_get_int64(config, &session->max_connections, + SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, + SVN_CONFIG_DEFAULT_OPTION_HTTP_MAX_CONNECTIONS)); + + /* Should we use chunked transfer encoding. */ + SVN_ERR(svn_config_get_tristate(config, &chunked_requests, + SVN_CONFIG_SECTION_GLOBAL, + OPTION_HTTP_CHUNKED_REQUESTS, + "auto", svn_tristate_unknown)); + if (config) server_group = svn_config_find_group(config, session->session_url.hostname, @@ -233,26 +262,61 @@ load_config(svn_ra_serf__session_t *session, svn_auth_set_parameter(session->wc_callbacks->auth_baton, SVN_AUTH_PARAM_SERVER_GROUP, server_group); - /* Load the group proxy server settings, overriding global settings. */ + /* Load the group proxy server settings, overriding global + settings. We intentionally ignore 'http-proxy-exceptions' + here because, well, if this site was an exception, why is + there a per-server proxy configuration for it? */ svn_config_get(config, &proxy_host, server_group, - SVN_CONFIG_OPTION_HTTP_PROXY_HOST, NULL); + SVN_CONFIG_OPTION_HTTP_PROXY_HOST, proxy_host); svn_config_get(config, &port_str, server_group, - SVN_CONFIG_OPTION_HTTP_PROXY_PORT, NULL); + SVN_CONFIG_OPTION_HTTP_PROXY_PORT, port_str); svn_config_get(config, &session->proxy_username, server_group, - SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, NULL); + SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, + session->proxy_username); svn_config_get(config, &session->proxy_password, server_group, - SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, NULL); + SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, + session->proxy_password); /* Load the group ssl settings. */ SVN_ERR(svn_config_get_bool(config, &session->trust_default_ca, server_group, SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA, - TRUE)); + session->trust_default_ca)); svn_config_get(config, &session->ssl_authorities, server_group, - SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, NULL); + SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, + session->ssl_authorities); + + /* Load the group bulk updates flag. */ + SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates, + server_group, + SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, + "auto", + session->bulk_updates)); + + /* Load the maximum number of parallel session connections, + overriding global values. */ + SVN_ERR(svn_config_get_int64(config, &session->max_connections, + server_group, + SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, + session->max_connections)); + + /* Should we use chunked transfer encoding. */ + SVN_ERR(svn_config_get_tristate(config, &chunked_requests, + server_group, + OPTION_HTTP_CHUNKED_REQUESTS, + "auto", chunked_requests)); } + /* Don't allow the http-max-connections value to be larger than our + compiled-in limit, or to be too small to operate. Broken + functionality and angry administrators are equally undesirable. */ + if (session->max_connections > SVN_RA_SERF__MAX_CONNECTIONS_LIMIT) + session->max_connections = SVN_RA_SERF__MAX_CONNECTIONS_LIMIT; + if (session->max_connections < 2) + session->max_connections = 2; + /* Parse the connection timeout value, if any. */ + session->timeout = apr_time_from_sec(DEFAULT_HTTP_TIMEOUT); if (timeout_str) { char *endstr; @@ -267,8 +331,7 @@ load_config(svn_ra_serf__session_t *session, _("Invalid config: negative timeout value")); session->timeout = apr_time_from_sec(timeout); } - else - session->timeout = apr_time_from_sec(DEFAULT_HTTP_TIMEOUT); + SVN_ERR_ASSERT(session->timeout >= 0); /* Convert the proxy port value, if any. */ if (port_str) @@ -290,7 +353,9 @@ load_config(svn_ra_serf__session_t *session, proxy_port = (apr_port_t) port; } else - proxy_port = 80; + { + proxy_port = 80; + } if (proxy_host) { @@ -302,15 +367,35 @@ load_config(svn_ra_serf__session_t *session, session->pool); if (status) { - return svn_error_wrap_apr(status, - _("Could not resolve proxy server '%s'"), - proxy_host); + return svn_ra_serf__wrap_err( + status, _("Could not resolve proxy server '%s'"), + proxy_host); } session->using_proxy = TRUE; serf_config_proxy(session->context, proxy_addr); } else - session->using_proxy = FALSE; + { + session->using_proxy = FALSE; + } + + /* Setup detect_chunking and using_chunked_requests based on + * the chunked_requests tristate */ + if (chunked_requests == svn_tristate_unknown) + { + session->detect_chunking = TRUE; + session->using_chunked_requests = TRUE; + } + else if (chunked_requests == svn_tristate_true) + { + session->detect_chunking = FALSE; + session->using_chunked_requests = TRUE; + } + else /* chunked_requests == svn_tristate_false */ + { + session->detect_chunking = FALSE; + session->using_chunked_requests = FALSE; + } /* Setup authentication. */ SVN_ERR(load_http_auth_types(pool, config, server_group, @@ -335,10 +420,23 @@ svn_ra_serf__progress(void *progress_baton, apr_off_t read, apr_off_t written) } } +/** Our User-Agent string. */ +static const char * +get_user_agent_string(apr_pool_t *pool) +{ + int major, minor, patch; + serf_lib_version(&major, &minor, &patch); + + return apr_psprintf(pool, "SVN/%s (%s) serf/%d.%d.%d", + SVN_VER_NUMBER, SVN_BUILD_TARGET, + major, minor, patch); +} + +/* Implements svn_ra__vtable_t.open_session(). */ static svn_error_t * svn_ra_serf__open(svn_ra_session_t *session, const char **corrected_url, - const char *repos_URL, + const char *session_URL, const svn_ra_callbacks2_t *callbacks, void *callback_baton, apr_hash_t *config, @@ -348,6 +446,7 @@ svn_ra_serf__open(svn_ra_session_t *session, svn_ra_serf__session_t *serf_sess; apr_uri_t url; const char *client_string = NULL; + svn_error_t *err; if (corrected_url) *corrected_url = NULL; @@ -368,33 +467,40 @@ svn_ra_serf__open(svn_ra_session_t *session, serf_sess->pool)); - status = apr_uri_parse(serf_sess->pool, repos_URL, &url); + status = apr_uri_parse(serf_sess->pool, session_URL, &url); if (status) { return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, - _("Illegal repository URL '%s'"), - repos_URL); + _("Illegal URL '%s'"), + session_URL); } - /* Contrary to what the comment for apr_uri_t.path says in apr-util 1.2.12 and - older, for root paths url.path will be "", where serf requires "/". */ + /* Depending the version of apr-util in use, for root paths url.path + will be NULL or "", where serf requires "/". */ if (url.path == NULL || url.path[0] == '\0') - url.path = apr_pstrdup(serf_sess->pool, "/"); + { + url.path = apr_pstrdup(serf_sess->pool, "/"); + } if (!url.port) { url.port = apr_uri_port_of_scheme(url.scheme); } serf_sess->session_url = url; - serf_sess->session_url_str = apr_pstrdup(serf_sess->pool, repos_URL); + serf_sess->session_url_str = apr_pstrdup(serf_sess->pool, session_URL); serf_sess->using_ssl = (svn_cstring_casecmp(url.scheme, "https") == 0); serf_sess->supports_deadprop_count = svn_tristate_unknown; serf_sess->capabilities = apr_hash_make(serf_sess->pool); - SVN_ERR(load_config(serf_sess, config, serf_sess->pool)); + /* We have to assume that the server only supports HTTP/1.0. Once it's clear + HTTP/1.1 is supported, we can upgrade. */ + serf_sess->http10 = TRUE; + /* If we switch to HTTP/1.1, then we will use chunked requests. We may disable + this, if we find an intervening proxy does not support chunked requests. */ + serf_sess->using_chunked_requests = TRUE; - serf_sess->conns = apr_palloc(serf_sess->pool, sizeof(*serf_sess->conns) * 4); + SVN_ERR(load_config(serf_sess, config, serf_sess->pool)); serf_sess->conns[0] = apr_pcalloc(serf_sess->pool, sizeof(*serf_sess->conns[0])); @@ -403,21 +509,15 @@ svn_ra_serf__open(svn_ra_session_t *session, serf_sess->conns[0]->session = serf_sess; serf_sess->conns[0]->last_status_code = -1; - serf_sess->conns[0]->using_ssl = serf_sess->using_ssl; - serf_sess->conns[0]->server_cert_failures = 0; - serf_sess->conns[0]->using_compression = serf_sess->using_compression; - serf_sess->conns[0]->hostname = url.hostname; - serf_sess->conns[0]->useragent = NULL; - /* create the user agent string */ if (callbacks->get_client_string) - callbacks->get_client_string(callback_baton, &client_string, pool); + SVN_ERR(callbacks->get_client_string(callback_baton, &client_string, pool)); if (client_string) - serf_sess->conns[0]->useragent = apr_pstrcat(pool, USER_AGENT, "/", - client_string, (char *)NULL); + serf_sess->useragent = apr_pstrcat(pool, get_user_agent_string(pool), " ", + client_string, (char *)NULL); else - serf_sess->conns[0]->useragent = USER_AGENT; + serf_sess->useragent = get_user_agent_string(pool); /* go ahead and tell serf about the connection. */ status = @@ -428,7 +528,7 @@ svn_ra_serf__open(svn_ra_session_t *session, svn_ra_serf__conn_closed, serf_sess->conns[0], serf_sess->pool); if (status) - return svn_error_wrap_apr(status, NULL); + return svn_ra_serf__wrap_err(status, NULL); /* Set the progress callback. */ serf_context_set_progress_cb(serf_sess->context, svn_ra_serf__progress, @@ -438,9 +538,26 @@ svn_ra_serf__open(svn_ra_session_t *session, session->priv = serf_sess; - return svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, pool); + err = svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, pool); + + /* serf should produce a usable error code instead of APR_EGENERAL */ + if (err && err->apr_err == APR_EGENERAL) + err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, err, + _("Connection to '%s' failed"), session_URL); + SVN_ERR(err); + + /* We have set up a useful connection (that doesn't indication a redirect). + If we've been told there is possibly a worrisome proxy in our path to the + server AND we switched to HTTP/1.1 (chunked requests), then probe for + problems in any proxy. */ + if ((corrected_url == NULL || *corrected_url == NULL) + && serf_sess->detect_chunking && !serf_sess->http10) + SVN_ERR(svn_ra_serf__probe_proxy(serf_sess, pool)); + + return SVN_NO_ERROR; } +/* Implements svn_ra__vtable_t.reparent(). */ static svn_error_t * svn_ra_serf__reparent(svn_ra_session_t *ra_session, const char *url, @@ -470,19 +587,31 @@ svn_ra_serf__reparent(svn_ra_session_t *ra_session, "URL '%s'"), url, session->repos_root_str); } - status = apr_uri_parse(session->pool, url, &new_url); + status = apr_uri_parse(pool, url, &new_url); if (status) { return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Illegal repository URL '%s'"), url); } - session->session_url.path = new_url.path; + /* Depending the version of apr-util in use, for root paths url.path + will be NULL or "", where serf requires "/". */ + /* ### Maybe we should use a string buffer for these strings so we + ### don't allocate memory in the session on every reparent? */ + if (new_url.path == NULL || new_url.path[0] == '\0') + { + session->session_url.path = apr_pstrdup(session->pool, "/"); + } + else + { + session->session_url.path = apr_pstrdup(session->pool, new_url.path); + } session->session_url_str = apr_pstrdup(session->pool, url); 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, @@ -493,20 +622,19 @@ svn_ra_serf__get_session_url(svn_ra_session_t *ra_session, 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, @@ -533,6 +661,7 @@ svn_ra_serf__rev_proplist(svn_ra_session_t *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)); @@ -543,6 +672,7 @@ svn_ra_serf__rev_proplist(svn_ra_session_t *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, @@ -554,77 +684,49 @@ svn_ra_serf__rev_prop(svn_ra_session_t *session, SVN_ERR(svn_ra_serf__rev_proplist(session, rev, &props, pool)); - *value = apr_hash_get(props, name, APR_HASH_KEY_STRING); + *value = svn_hash_gets(props, name); return SVN_NO_ERROR; } 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); - } + if (session_relpath) + url = svn_path_url_add_component2(url, session_relpath, scratch_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 + /* 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)) { - const char *relative_url, *basecoll_url; - - 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)); + SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */, + session, NULL /* conn */, + url, revision, + scratch_pool, scratch_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; + /* 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, @@ -634,13 +736,10 @@ svn_ra_serf__check_path(svn_ra_session_t *ra_session, { 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) { @@ -651,9 +750,9 @@ svn_ra_serf__check_path(svn_ra_session_t *ra_session, { /* 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(kind, props)); } return SVN_NO_ERROR; @@ -775,14 +874,14 @@ path_dirent_walker(void *baton, { const char *base_name; - entry = apr_pcalloc(pool, sizeof(*entry)); + entry = svn_dirent_create(pool); apr_hash_set(dirents->full_paths, path, path_len, entry); base_name = svn_path_uri_decode(svn_urlpath__basename(path, pool), pool); - apr_hash_set(dirents->base_paths, base_name, APR_HASH_KEY_STRING, entry); + svn_hash_sets(dirents->base_paths, base_name, entry); } dwb.entry = entry; @@ -863,6 +962,7 @@ get_dirent_props(apr_uint32_t dirent_fields, 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, @@ -872,17 +972,14 @@ svn_ra_serf__stat(svn_ra_session_t *ra_session, { 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) @@ -895,12 +992,10 @@ svn_ra_serf__stat(svn_ra_session_t *ra_session, return svn_error_trace(err); } - dwb.entry = apr_pcalloc(pool, sizeof(*dwb.entry)); + dwb.entry = svn_dirent_create(pool); 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 @@ -910,14 +1005,12 @@ svn_ra_serf__stat(svn_ra_session_t *ra_session, 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) @@ -933,13 +1026,11 @@ svn_ra_serf__stat(svn_ra_session_t *ra_session, * 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_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) { @@ -950,6 +1041,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, @@ -976,34 +1068,37 @@ svn_ra_serf__get_dir(svn_ra_session_t *ra_session, 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 @@ -1015,8 +1110,9 @@ svn_ra_serf__get_dir(svn_ra_session_t *ra_session, 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 @@ -1027,15 +1123,15 @@ svn_ra_serf__get_dir(svn_ra_session_t *ra_session, 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)); - SVN_ERR(svn_hash__clear(dirent_walk.full_paths, pool)); - SVN_ERR(svn_hash__clear(dirent_walk.base_paths, pool)); + apr_hash_clear(dirent_walk.full_paths); + apr_hash_clear(dirent_walk.base_paths); - 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)); } @@ -1051,20 +1147,23 @@ svn_ra_serf__get_dir(svn_ra_session_t *ra_session, { 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; } -static svn_error_t * +svn_error_t * svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session, const char **url, apr_pool_t *pool) @@ -1089,6 +1188,8 @@ svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session, 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, @@ -1158,7 +1259,9 @@ static const svn_ra__vtable_t serf_vtable = { svn_ra_serf__replay, svn_ra_serf__has_capability, svn_ra_serf__replay_range, - svn_ra_serf__get_deleted_rev + svn_ra_serf__get_deleted_rev, + svn_ra_serf__register_editor_shim_callbacks, + svn_ra_serf__get_inherited_props }; svn_error_t * @@ -1176,7 +1279,7 @@ svn_ra_serf__init(const svn_version_t *loader_version, int serf_minor; int serf_patch; - SVN_ERR(svn_ver_check_list(ra_serf_version(), checklist)); + SVN_ERR(svn_ver_check_list2(ra_serf_version(), checklist, svn_ver_equal)); /* Simplified version check to make sure we can safely use the VTABLE parameter. The RA loader does a more exhaustive check. */ @@ -1196,6 +1299,7 @@ svn_ra_serf__init(const svn_version_t *loader_version, || serf_minor < SERF_MINOR_VERSION) { return svn_error_createf( + /* ### should return a unique error */ SVN_ERR_VERSION_MISMATCH, NULL, _("ra_serf was compiled for serf %d.%d.%d but loaded " "an incompatible %d.%d.%d library"), |