1251881Speter/* 2251881Speter * options.c : entry point for OPTIONS RA functions for ra_serf 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include <apr_uri.h> 27251881Speter 28251881Speter#include <serf.h> 29251881Speter 30251881Speter#include "svn_dirent_uri.h" 31251881Speter#include "svn_hash.h" 32251881Speter#include "svn_pools.h" 33251881Speter#include "svn_ra.h" 34251881Speter#include "svn_dav.h" 35251881Speter#include "svn_xml.h" 36251881Speter 37251881Speter#include "../libsvn_ra/ra_loader.h" 38251881Speter#include "svn_private_config.h" 39251881Speter#include "private/svn_fspath.h" 40251881Speter 41251881Speter#include "ra_serf.h" 42251881Speter 43251881Speter 44251881Speter/* In a debug build, setting this environment variable to "yes" will force 45251881Speter the client to speak v1, even if the server is capable of speaking v2. */ 46251881Speter#define SVN_IGNORE_V2_ENV_VAR "SVN_I_LIKE_LATENCY_SO_IGNORE_HTTPV2" 47251881Speter 48251881Speter 49251881Speter/* 50251881Speter * This enum represents the current state of our XML parsing for an OPTIONS. 51251881Speter */ 52251881Speterenum options_state_e { 53251881Speter INITIAL = 0, 54251881Speter OPTIONS, 55251881Speter ACTIVITY_COLLECTION, 56251881Speter HREF 57251881Speter}; 58251881Speter 59251881Spetertypedef struct options_context_t { 60251881Speter /* pool to allocate memory from */ 61251881Speter apr_pool_t *pool; 62251881Speter 63251881Speter /* Have we extracted options values from the headers already? */ 64251881Speter svn_boolean_t headers_processed; 65251881Speter 66251881Speter svn_ra_serf__session_t *session; 67251881Speter svn_ra_serf__connection_t *conn; 68251881Speter svn_ra_serf__handler_t *handler; 69251881Speter 70251881Speter svn_ra_serf__response_handler_t inner_handler; 71251881Speter void *inner_baton; 72251881Speter 73251881Speter const char *activity_collection; 74251881Speter svn_revnum_t youngest_rev; 75251881Speter 76251881Speter} options_context_t; 77251881Speter 78251881Speter#define D_ "DAV:" 79251881Speter#define S_ SVN_XML_NAMESPACE 80251881Speterstatic const svn_ra_serf__xml_transition_t options_ttable[] = { 81251881Speter { INITIAL, D_, "options-response", OPTIONS, 82251881Speter FALSE, { NULL }, FALSE }, 83251881Speter 84251881Speter { OPTIONS, D_, "activity-collection-set", ACTIVITY_COLLECTION, 85251881Speter FALSE, { NULL }, FALSE }, 86251881Speter 87251881Speter { ACTIVITY_COLLECTION, D_, "href", HREF, 88251881Speter TRUE, { NULL }, TRUE }, 89251881Speter 90251881Speter { 0 } 91251881Speter}; 92251881Speter 93251881Speter 94251881Speter/* Conforms to svn_ra_serf__xml_closed_t */ 95251881Speterstatic svn_error_t * 96251881Speteroptions_closed(svn_ra_serf__xml_estate_t *xes, 97251881Speter void *baton, 98251881Speter int leaving_state, 99251881Speter const svn_string_t *cdata, 100251881Speter apr_hash_t *attrs, 101251881Speter apr_pool_t *scratch_pool) 102251881Speter{ 103251881Speter options_context_t *opt_ctx = baton; 104251881Speter 105251881Speter SVN_ERR_ASSERT(leaving_state == HREF); 106251881Speter SVN_ERR_ASSERT(cdata != NULL); 107251881Speter 108251881Speter opt_ctx->activity_collection = svn_urlpath__canonicalize(cdata->data, 109251881Speter opt_ctx->pool); 110251881Speter 111251881Speter return SVN_NO_ERROR; 112251881Speter} 113251881Speter 114251881Speter 115251881Speterstatic svn_error_t * 116251881Spetercreate_options_body(serf_bucket_t **body_bkt, 117251881Speter void *baton, 118251881Speter serf_bucket_alloc_t *alloc, 119251881Speter apr_pool_t *pool) 120251881Speter{ 121251881Speter serf_bucket_t *body; 122251881Speter body = serf_bucket_aggregate_create(alloc); 123251881Speter svn_ra_serf__add_xml_header_buckets(body, alloc); 124251881Speter svn_ra_serf__add_open_tag_buckets(body, alloc, "D:options", 125251881Speter "xmlns:D", "DAV:", 126251881Speter NULL); 127251881Speter svn_ra_serf__add_tag_buckets(body, "D:activity-collection-set", NULL, alloc); 128251881Speter svn_ra_serf__add_close_tag_buckets(body, alloc, "D:options"); 129251881Speter 130251881Speter *body_bkt = body; 131251881Speter return SVN_NO_ERROR; 132251881Speter} 133251881Speter 134251881Speter 135251881Speter/* We use these static pointers so we can employ pointer comparison 136251881Speter * of our capabilities hash members instead of strcmp()ing all over 137251881Speter * the place. 138251881Speter */ 139251881Speter/* Both server and repository support the capability. */ 140251881Speterstatic const char *const capability_yes = "yes"; 141251881Speter/* Either server or repository does not support the capability. */ 142251881Speterstatic const char *const capability_no = "no"; 143251881Speter/* Server supports the capability, but don't yet know if repository does. */ 144251881Speterstatic const char *const capability_server_yes = "server-yes"; 145251881Speter 146251881Speter 147251881Speter/* This implements serf_bucket_headers_do_callback_fn_t. 148251881Speter */ 149251881Speterstatic int 150251881Spetercapabilities_headers_iterator_callback(void *baton, 151251881Speter const char *key, 152251881Speter const char *val) 153251881Speter{ 154251881Speter options_context_t *opt_ctx = baton; 155251881Speter svn_ra_serf__session_t *session = opt_ctx->session; 156251881Speter 157251881Speter if (svn_cstring_casecmp(key, "dav") == 0) 158251881Speter { 159251881Speter /* Each header may contain multiple values, separated by commas, e.g.: 160251881Speter DAV: version-control,checkout,working-resource 161251881Speter DAV: merge,baseline,activity,version-controlled-collection 162251881Speter DAV: http://subversion.tigris.org/xmlns/dav/svn/depth */ 163251881Speter apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, 164251881Speter opt_ctx->pool); 165251881Speter 166251881Speter /* Right now we only have a few capabilities to detect, so just 167251881Speter seek for them directly. This could be written slightly more 168251881Speter efficiently, but that wouldn't be worth it until we have many 169251881Speter more capabilities. */ 170251881Speter 171251881Speter if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_DEPTH, vals)) 172251881Speter { 173251881Speter svn_hash_sets(session->capabilities, 174251881Speter SVN_RA_CAPABILITY_DEPTH, capability_yes); 175251881Speter } 176251881Speter if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_MERGEINFO, vals)) 177251881Speter { 178251881Speter /* The server doesn't know what repository we're referring 179251881Speter to, so it can't just say capability_yes. */ 180251881Speter if (!svn_hash_gets(session->capabilities, 181251881Speter SVN_RA_CAPABILITY_MERGEINFO)) 182251881Speter { 183251881Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, 184251881Speter capability_server_yes); 185251881Speter } 186251881Speter } 187251881Speter if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_LOG_REVPROPS, vals)) 188251881Speter { 189251881Speter svn_hash_sets(session->capabilities, 190251881Speter SVN_RA_CAPABILITY_LOG_REVPROPS, capability_yes); 191251881Speter } 192251881Speter if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS, vals)) 193251881Speter { 194251881Speter svn_hash_sets(session->capabilities, 195251881Speter SVN_RA_CAPABILITY_ATOMIC_REVPROPS, capability_yes); 196251881Speter } 197251881Speter if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY, vals)) 198251881Speter { 199251881Speter svn_hash_sets(session->capabilities, 200251881Speter SVN_RA_CAPABILITY_PARTIAL_REPLAY, capability_yes); 201251881Speter } 202251881Speter if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INHERITED_PROPS, vals)) 203251881Speter { 204251881Speter svn_hash_sets(session->capabilities, 205251881Speter SVN_RA_CAPABILITY_INHERITED_PROPS, capability_yes); 206251881Speter } 207251881Speter if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REVERSE_FILE_REVS, 208251881Speter vals)) 209251881Speter { 210251881Speter svn_hash_sets(session->capabilities, 211251881Speter SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE, 212251881Speter capability_yes); 213251881Speter } 214251881Speter if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_EPHEMERAL_TXNPROPS, vals)) 215251881Speter { 216251881Speter svn_hash_sets(session->capabilities, 217251881Speter SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS, capability_yes); 218251881Speter } 219251881Speter if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INLINE_PROPS, vals)) 220251881Speter { 221251881Speter session->supports_inline_props = TRUE; 222251881Speter } 223251881Speter if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REPLAY_REV_RESOURCE, vals)) 224251881Speter { 225251881Speter session->supports_rev_rsrc_replay = TRUE; 226251881Speter } 227251881Speter } 228251881Speter 229251881Speter /* SVN-specific headers -- if present, server supports HTTP protocol v2 */ 230251881Speter else if (strncmp(key, "SVN", 3) == 0) 231251881Speter { 232251881Speter /* If we've not yet seen any information about supported POST 233251881Speter requests, we'll initialize the list/hash with "create-txn" 234251881Speter (which we know is supported by virtue of the server speaking 235251881Speter HTTPv2 at all. */ 236251881Speter if (! session->supported_posts) 237251881Speter { 238251881Speter session->supported_posts = apr_hash_make(session->pool); 239251881Speter apr_hash_set(session->supported_posts, "create-txn", 10, (void *)1); 240251881Speter } 241251881Speter 242251881Speter if (svn_cstring_casecmp(key, SVN_DAV_ROOT_URI_HEADER) == 0) 243251881Speter { 244251881Speter session->repos_root = session->session_url; 245251881Speter session->repos_root.path = 246251881Speter (char *)svn_fspath__canonicalize(val, session->pool); 247251881Speter session->repos_root_str = 248251881Speter svn_urlpath__canonicalize( 249251881Speter apr_uri_unparse(session->pool, &session->repos_root, 0), 250251881Speter session->pool); 251251881Speter } 252251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_ME_RESOURCE_HEADER) == 0) 253251881Speter { 254251881Speter#ifdef SVN_DEBUG 255251881Speter char *ignore_v2_env_var = getenv(SVN_IGNORE_V2_ENV_VAR); 256251881Speter 257251881Speter if (!(ignore_v2_env_var 258251881Speter && apr_strnatcasecmp(ignore_v2_env_var, "yes") == 0)) 259251881Speter session->me_resource = apr_pstrdup(session->pool, val); 260251881Speter#else 261251881Speter session->me_resource = apr_pstrdup(session->pool, val); 262251881Speter#endif 263251881Speter } 264251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_REV_STUB_HEADER) == 0) 265251881Speter { 266251881Speter session->rev_stub = apr_pstrdup(session->pool, val); 267251881Speter } 268251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_REV_ROOT_STUB_HEADER) == 0) 269251881Speter { 270251881Speter session->rev_root_stub = apr_pstrdup(session->pool, val); 271251881Speter } 272251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_TXN_STUB_HEADER) == 0) 273251881Speter { 274251881Speter session->txn_stub = apr_pstrdup(session->pool, val); 275251881Speter } 276251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_TXN_ROOT_STUB_HEADER) == 0) 277251881Speter { 278251881Speter session->txn_root_stub = apr_pstrdup(session->pool, val); 279251881Speter } 280251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_STUB_HEADER) == 0) 281251881Speter { 282251881Speter session->vtxn_stub = apr_pstrdup(session->pool, val); 283251881Speter } 284251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_ROOT_STUB_HEADER) == 0) 285251881Speter { 286251881Speter session->vtxn_root_stub = apr_pstrdup(session->pool, val); 287251881Speter } 288251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_REPOS_UUID_HEADER) == 0) 289251881Speter { 290251881Speter session->uuid = apr_pstrdup(session->pool, val); 291251881Speter } 292251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_YOUNGEST_REV_HEADER) == 0) 293251881Speter { 294251881Speter opt_ctx->youngest_rev = SVN_STR_TO_REV(val); 295251881Speter } 296251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_ALLOW_BULK_UPDATES) == 0) 297251881Speter { 298251881Speter session->server_allows_bulk = apr_pstrdup(session->pool, val); 299251881Speter } 300251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_SUPPORTED_POSTS_HEADER) == 0) 301251881Speter { 302251881Speter /* May contain multiple values, separated by commas. */ 303251881Speter int i; 304251881Speter apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, 305251881Speter opt_ctx->pool); 306251881Speter 307251881Speter for (i = 0; i < vals->nelts; i++) 308251881Speter { 309251881Speter const char *post_val = APR_ARRAY_IDX(vals, i, const char *); 310251881Speter 311251881Speter svn_hash_sets(session->supported_posts, post_val, (void *)1); 312251881Speter } 313251881Speter } 314251881Speter else if (svn_cstring_casecmp(key, SVN_DAV_REPOSITORY_MERGEINFO) == 0) 315251881Speter { 316251881Speter if (svn_cstring_casecmp(val, "yes") == 0) 317251881Speter { 318251881Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, 319251881Speter capability_yes); 320251881Speter } 321251881Speter else if (svn_cstring_casecmp(val, "no") == 0) 322251881Speter { 323251881Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, 324251881Speter capability_no); 325251881Speter } 326251881Speter } 327251881Speter } 328251881Speter 329251881Speter return 0; 330251881Speter} 331251881Speter 332251881Speter 333251881Speter/* A custom serf_response_handler_t which is mostly a wrapper around 334251881Speter the expat-based response handler -- it just notices OPTIONS response 335251881Speter headers first, before handing off to the xml parser. 336251881Speter Implements svn_ra_serf__response_handler_t */ 337251881Speterstatic svn_error_t * 338251881Speteroptions_response_handler(serf_request_t *request, 339251881Speter serf_bucket_t *response, 340251881Speter void *baton, 341251881Speter apr_pool_t *pool) 342251881Speter{ 343251881Speter options_context_t *opt_ctx = baton; 344251881Speter 345251881Speter if (!opt_ctx->headers_processed) 346251881Speter { 347251881Speter svn_ra_serf__session_t *session = opt_ctx->session; 348251881Speter serf_bucket_t *hdrs = serf_bucket_response_get_headers(response); 349251881Speter 350251881Speter /* Start out assuming all capabilities are unsupported. */ 351251881Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY, 352251881Speter capability_no); 353251881Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_DEPTH, 354251881Speter capability_no); 355251881Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, 356251881Speter NULL); 357251881Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS, 358251881Speter capability_no); 359251881Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS, 360251881Speter capability_no); 361251881Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_INHERITED_PROPS, 362251881Speter capability_no); 363251881Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS, 364251881Speter capability_no); 365253734Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE, 366253734Speter capability_no); 367251881Speter 368251881Speter /* Then see which ones we can discover. */ 369251881Speter serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback, 370251881Speter opt_ctx); 371251881Speter 372251881Speter /* Assume mergeinfo capability unsupported, if didn't recieve information 373251881Speter about server or repository mergeinfo capability. */ 374251881Speter if (!svn_hash_gets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO)) 375251881Speter svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, 376251881Speter capability_no); 377251881Speter 378251881Speter opt_ctx->headers_processed = TRUE; 379251881Speter } 380251881Speter 381251881Speter /* Execute the 'real' response handler to XML-parse the response body. */ 382251881Speter return opt_ctx->inner_handler(request, response, opt_ctx->inner_baton, pool); 383251881Speter} 384251881Speter 385251881Speter 386251881Speterstatic svn_error_t * 387251881Spetercreate_options_req(options_context_t **opt_ctx, 388251881Speter svn_ra_serf__session_t *session, 389251881Speter svn_ra_serf__connection_t *conn, 390251881Speter apr_pool_t *pool) 391251881Speter{ 392251881Speter options_context_t *new_ctx; 393251881Speter svn_ra_serf__xml_context_t *xmlctx; 394251881Speter svn_ra_serf__handler_t *handler; 395251881Speter 396251881Speter new_ctx = apr_pcalloc(pool, sizeof(*new_ctx)); 397251881Speter new_ctx->pool = pool; 398251881Speter new_ctx->session = session; 399251881Speter new_ctx->conn = conn; 400251881Speter 401251881Speter new_ctx->youngest_rev = SVN_INVALID_REVNUM; 402251881Speter 403251881Speter xmlctx = svn_ra_serf__xml_context_create(options_ttable, 404251881Speter NULL, options_closed, NULL, 405251881Speter new_ctx, 406251881Speter pool); 407251881Speter handler = svn_ra_serf__create_expat_handler(xmlctx, pool); 408251881Speter 409251881Speter handler->method = "OPTIONS"; 410251881Speter handler->path = session->session_url.path; 411251881Speter handler->body_delegate = create_options_body; 412251881Speter handler->body_type = "text/xml"; 413251881Speter handler->conn = conn; 414251881Speter handler->session = session; 415251881Speter 416251881Speter new_ctx->handler = handler; 417251881Speter 418251881Speter new_ctx->inner_handler = handler->response_handler; 419251881Speter new_ctx->inner_baton = handler->response_baton; 420251881Speter handler->response_handler = options_response_handler; 421251881Speter handler->response_baton = new_ctx; 422251881Speter 423251881Speter *opt_ctx = new_ctx; 424251881Speter 425251881Speter return SVN_NO_ERROR; 426251881Speter} 427251881Speter 428251881Speter 429251881Spetersvn_error_t * 430251881Spetersvn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest, 431251881Speter svn_ra_serf__connection_t *conn, 432251881Speter apr_pool_t *scratch_pool) 433251881Speter{ 434251881Speter svn_ra_serf__session_t *session = conn->session; 435251881Speter options_context_t *opt_ctx; 436251881Speter 437251881Speter SVN_ERR_ASSERT(SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); 438251881Speter 439251881Speter SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool)); 440251881Speter SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool)); 441253734Speter SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline, 442251881Speter opt_ctx->handler->path, 443251881Speter opt_ctx->handler->location)); 444251881Speter 445251881Speter *youngest = opt_ctx->youngest_rev; 446253734Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(*youngest)); 447251881Speter 448251881Speter return SVN_NO_ERROR; 449251881Speter} 450251881Speter 451251881Speter 452251881Spetersvn_error_t * 453251881Spetersvn_ra_serf__v1_get_activity_collection(const char **activity_url, 454251881Speter svn_ra_serf__connection_t *conn, 455251881Speter apr_pool_t *result_pool, 456251881Speter apr_pool_t *scratch_pool) 457251881Speter{ 458251881Speter svn_ra_serf__session_t *session = conn->session; 459251881Speter options_context_t *opt_ctx; 460251881Speter 461251881Speter SVN_ERR_ASSERT(!SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); 462251881Speter 463251881Speter SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool)); 464251881Speter SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool)); 465251881Speter 466253734Speter SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline, 467251881Speter opt_ctx->handler->path, 468251881Speter opt_ctx->handler->location)); 469251881Speter 470251881Speter *activity_url = apr_pstrdup(result_pool, opt_ctx->activity_collection); 471251881Speter 472251881Speter return SVN_NO_ERROR; 473251881Speter 474251881Speter} 475251881Speter 476251881Speter 477251881Speter 478251881Speter/** Capabilities exchange. */ 479251881Speter 480251881Spetersvn_error_t * 481251881Spetersvn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess, 482251881Speter const char **corrected_url, 483251881Speter apr_pool_t *pool) 484251881Speter{ 485251881Speter options_context_t *opt_ctx; 486251881Speter svn_error_t *err; 487251881Speter 488251881Speter /* This routine automatically fills in serf_sess->capabilities */ 489251881Speter SVN_ERR(create_options_req(&opt_ctx, serf_sess, serf_sess->conns[0], pool)); 490251881Speter 491251881Speter err = svn_ra_serf__context_run_one(opt_ctx->handler, pool); 492251881Speter 493251881Speter /* If our caller cares about server redirections, and our response 494251881Speter carries such a thing, report as much. We'll disregard ERR -- 495251881Speter it's most likely just a complaint about the response body not 496251881Speter successfully parsing as XML or somesuch. */ 497251881Speter if (corrected_url && (opt_ctx->handler->sline.code == 301)) 498251881Speter { 499251881Speter svn_error_clear(err); 500251881Speter *corrected_url = opt_ctx->handler->location; 501251881Speter return SVN_NO_ERROR; 502251881Speter } 503251881Speter 504251881Speter SVN_ERR(svn_error_compose_create( 505253734Speter svn_ra_serf__error_on_status(opt_ctx->handler->sline, 506251881Speter serf_sess->session_url.path, 507251881Speter opt_ctx->handler->location), 508251881Speter err)); 509251881Speter 510251881Speter /* Opportunistically cache any reported activity URL. (We don't 511251881Speter want to have to ask for this again later, potentially against an 512251881Speter unreadable commit anchor URL.) */ 513251881Speter if (opt_ctx->activity_collection) 514251881Speter { 515251881Speter serf_sess->activity_collection_url = 516251881Speter apr_pstrdup(serf_sess->pool, opt_ctx->activity_collection); 517251881Speter } 518251881Speter 519251881Speter return SVN_NO_ERROR; 520251881Speter} 521251881Speter 522251881Speter 523253734Speterstatic svn_error_t * 524253734Spetercreate_simple_options_body(serf_bucket_t **body_bkt, 525253734Speter void *baton, 526253734Speter serf_bucket_alloc_t *alloc, 527253734Speter apr_pool_t *pool) 528253734Speter{ 529253734Speter serf_bucket_t *body; 530253734Speter serf_bucket_t *s; 531253734Speter 532253734Speter body = serf_bucket_aggregate_create(alloc); 533253734Speter svn_ra_serf__add_xml_header_buckets(body, alloc); 534253734Speter 535253734Speter s = SERF_BUCKET_SIMPLE_STRING("<D:options xmlns:D=\"DAV:\" />", alloc); 536253734Speter serf_bucket_aggregate_append(body, s); 537253734Speter 538253734Speter *body_bkt = body; 539253734Speter return SVN_NO_ERROR; 540253734Speter} 541253734Speter 542253734Speter 543251881Spetersvn_error_t * 544253734Spetersvn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess, 545253734Speter apr_pool_t *scratch_pool) 546253734Speter{ 547253734Speter svn_ra_serf__handler_t *handler; 548253734Speter 549253734Speter handler = apr_pcalloc(scratch_pool, sizeof(*handler)); 550253734Speter handler->handler_pool = scratch_pool; 551253734Speter handler->method = "OPTIONS"; 552253734Speter handler->path = serf_sess->session_url.path; 553253734Speter handler->conn = serf_sess->conns[0]; 554253734Speter handler->session = serf_sess; 555253734Speter 556253734Speter /* We don't care about the response body, so discard it. */ 557253734Speter handler->response_handler = svn_ra_serf__handle_discard_body; 558253734Speter 559253734Speter /* We need a simple body, in order to send it in chunked format. */ 560253734Speter handler->body_delegate = create_simple_options_body; 561253734Speter 562253734Speter /* No special headers. */ 563253734Speter 564253734Speter SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); 565253734Speter /* Some versions of nginx in reverse proxy mode will return 411. They want 566253734Speter a Content-Length header, rather than chunked requests. We can keep other 567253734Speter HTTP/1.1 features, but will disable the chunking. */ 568253734Speter if (handler->sline.code == 411) 569253734Speter { 570253734Speter serf_sess->using_chunked_requests = FALSE; 571253734Speter 572253734Speter return SVN_NO_ERROR; 573253734Speter } 574253734Speter SVN_ERR(svn_ra_serf__error_on_status(handler->sline, 575253734Speter handler->path, 576253734Speter handler->location)); 577253734Speter 578253734Speter return SVN_NO_ERROR; 579253734Speter} 580253734Speter 581253734Speter 582253734Spetersvn_error_t * 583251881Spetersvn_ra_serf__has_capability(svn_ra_session_t *ra_session, 584251881Speter svn_boolean_t *has, 585251881Speter const char *capability, 586251881Speter apr_pool_t *pool) 587251881Speter{ 588251881Speter svn_ra_serf__session_t *serf_sess = ra_session->priv; 589251881Speter const char *cap_result; 590251881Speter 591251881Speter /* This capability doesn't rely on anything server side. */ 592251881Speter if (strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0) 593251881Speter { 594251881Speter *has = TRUE; 595251881Speter return SVN_NO_ERROR; 596251881Speter } 597251881Speter 598251881Speter cap_result = svn_hash_gets(serf_sess->capabilities, capability); 599251881Speter 600251881Speter /* If any capability is unknown, they're all unknown, so ask. */ 601251881Speter if (cap_result == NULL) 602251881Speter SVN_ERR(svn_ra_serf__exchange_capabilities(serf_sess, NULL, pool)); 603251881Speter 604251881Speter /* Try again, now that we've fetched the capabilities. */ 605251881Speter cap_result = svn_hash_gets(serf_sess->capabilities, capability); 606251881Speter 607251881Speter /* Some capabilities depend on the repository as well as the server. */ 608251881Speter if (cap_result == capability_server_yes) 609251881Speter { 610251881Speter if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0) 611251881Speter { 612251881Speter /* Handle mergeinfo specially. Mergeinfo depends on the 613251881Speter repository as well as the server, but the server routine 614251881Speter that answered our svn_ra_serf__exchange_capabilities() call above 615251881Speter didn't even know which repository we were interested in 616251881Speter -- it just told us whether the server supports mergeinfo. 617251881Speter If the answer was 'no', there's no point checking the 618251881Speter particular repository; but if it was 'yes', we still must 619251881Speter change it to 'no' iff the repository itself doesn't 620251881Speter support mergeinfo. */ 621251881Speter svn_mergeinfo_catalog_t ignored; 622251881Speter svn_error_t *err; 623251881Speter apr_array_header_t *paths = apr_array_make(pool, 1, 624251881Speter sizeof(char *)); 625251881Speter APR_ARRAY_PUSH(paths, const char *) = ""; 626251881Speter 627251881Speter err = svn_ra_serf__get_mergeinfo(ra_session, &ignored, paths, 0, 628251881Speter FALSE, FALSE, pool); 629251881Speter 630251881Speter if (err) 631251881Speter { 632251881Speter if (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE) 633251881Speter { 634251881Speter svn_error_clear(err); 635251881Speter cap_result = capability_no; 636251881Speter } 637251881Speter else if (err->apr_err == SVN_ERR_FS_NOT_FOUND) 638251881Speter { 639251881Speter /* Mergeinfo requests use relative paths, and 640251881Speter anyway we're in r0, so this is a likely error, 641251881Speter but it means the repository supports mergeinfo! */ 642251881Speter svn_error_clear(err); 643251881Speter cap_result = capability_yes; 644251881Speter } 645251881Speter else 646251881Speter return err; 647251881Speter } 648251881Speter else 649251881Speter cap_result = capability_yes; 650251881Speter 651251881Speter svn_hash_sets(serf_sess->capabilities, 652251881Speter SVN_RA_CAPABILITY_MERGEINFO, cap_result); 653251881Speter } 654251881Speter else 655251881Speter { 656251881Speter return svn_error_createf 657251881Speter (SVN_ERR_UNKNOWN_CAPABILITY, NULL, 658251881Speter _("Don't know how to handle '%s' for capability '%s'"), 659251881Speter capability_server_yes, capability); 660251881Speter } 661251881Speter } 662251881Speter 663251881Speter if (cap_result == capability_yes) 664251881Speter { 665251881Speter *has = TRUE; 666251881Speter } 667251881Speter else if (cap_result == capability_no) 668251881Speter { 669251881Speter *has = FALSE; 670251881Speter } 671251881Speter else if (cap_result == NULL) 672251881Speter { 673251881Speter return svn_error_createf 674251881Speter (SVN_ERR_UNKNOWN_CAPABILITY, NULL, 675251881Speter _("Don't know anything about capability '%s'"), capability); 676251881Speter } 677251881Speter else /* "can't happen" */ 678251881Speter { 679251881Speter /* Well, let's hope it's a string. */ 680251881Speter return svn_error_createf 681251881Speter (SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL, 682251881Speter _("Attempt to fetch capability '%s' resulted in '%s'"), 683251881Speter capability, cap_result); 684251881Speter } 685251881Speter 686251881Speter return SVN_NO_ERROR; 687251881Speter} 688