summaryrefslogtreecommitdiff
path: root/subversion/libsvn_ra_serf/serf.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_ra_serf/serf.c')
-rw-r--r--subversion/libsvn_ra_serf/serf.c448
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"),