options.c revision 289166
1177633Sdfr/* 2177633Sdfr * options.c : entry point for OPTIONS RA functions for ra_serf 3177633Sdfr * 4177633Sdfr * ==================================================================== 5177633Sdfr * Licensed to the Apache Software Foundation (ASF) under one 6177633Sdfr * or more contributor license agreements. See the NOTICE file 7177633Sdfr * distributed with this work for additional information 8177633Sdfr * regarding copyright ownership. The ASF licenses this file 9177633Sdfr * to you under the Apache License, Version 2.0 (the 10177633Sdfr * "License"); you may not use this file except in compliance 11177633Sdfr * with the License. You may obtain a copy of the License at 12177633Sdfr * 13177633Sdfr * http://www.apache.org/licenses/LICENSE-2.0 14177633Sdfr * 15177633Sdfr * Unless required by applicable law or agreed to in writing, 16177633Sdfr * software distributed under the License is distributed on an 17177633Sdfr * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18177633Sdfr * KIND, either express or implied. See the License for the 19177633Sdfr * specific language governing permissions and limitations 20177633Sdfr * under the License. 21177633Sdfr * ==================================================================== 22177633Sdfr */ 23177633Sdfr 24177633Sdfr 25177633Sdfr 26177633Sdfr#include <apr_uri.h> 27177633Sdfr 28177633Sdfr#include <serf.h> 29177633Sdfr 30177633Sdfr#include "svn_dirent_uri.h" 31177633Sdfr#include "svn_hash.h" 32177633Sdfr#include "svn_pools.h" 33177633Sdfr#include "svn_ra.h" 34177633Sdfr#include "svn_dav.h" 35177633Sdfr#include "svn_xml.h" 36177633Sdfr#include "svn_ctype.h" 37177633Sdfr 38177633Sdfr#include "../libsvn_ra/ra_loader.h" 39177633Sdfr#include "svn_private_config.h" 40177633Sdfr#include "private/svn_fspath.h" 41177633Sdfr 42177633Sdfr#include "ra_serf.h" 43177633Sdfr 44177633Sdfr 45177633Sdfr/* In a debug build, setting this environment variable to "yes" will force 46177633Sdfr the client to speak v1, even if the server is capable of speaking v2. */ 47177633Sdfr#define SVN_IGNORE_V2_ENV_VAR "SVN_I_LIKE_LATENCY_SO_IGNORE_HTTPV2" 48177633Sdfr 49177633Sdfr 50177633Sdfr/* 51177633Sdfr * This enum represents the current state of our XML parsing for an OPTIONS. 52177633Sdfr */ 53177633Sdfrenum options_state_e { 54177633Sdfr INITIAL = 0, 55177633Sdfr OPTIONS, 56177633Sdfr ACTIVITY_COLLECTION, 57177633Sdfr HREF 58177633Sdfr}; 59177633Sdfr 60177633Sdfrtypedef struct options_context_t { 61177633Sdfr /* pool to allocate memory from */ 62177633Sdfr apr_pool_t *pool; 63177633Sdfr 64177633Sdfr /* Have we extracted options values from the headers already? */ 65180025Sdfr svn_boolean_t headers_processed; 66177633Sdfr 67177633Sdfr svn_ra_serf__session_t *session; 68177633Sdfr svn_ra_serf__connection_t *conn; 69177633Sdfr svn_ra_serf__handler_t *handler; 70177633Sdfr 71177633Sdfr svn_ra_serf__response_handler_t inner_handler; 72177633Sdfr void *inner_baton; 73177633Sdfr 74177633Sdfr const char *activity_collection; 75177633Sdfr svn_revnum_t youngest_rev; 76177633Sdfr 77177633Sdfr} options_context_t; 78177633Sdfr 79177633Sdfr#define D_ "DAV:" 80177633Sdfr#define S_ SVN_XML_NAMESPACE 81177633Sdfrstatic const svn_ra_serf__xml_transition_t options_ttable[] = { 82177633Sdfr { INITIAL, D_, "options-response", OPTIONS, 83177633Sdfr FALSE, { NULL }, FALSE }, 84177633Sdfr 85177633Sdfr { OPTIONS, D_, "activity-collection-set", ACTIVITY_COLLECTION, 86177633Sdfr FALSE, { NULL }, FALSE }, 87177633Sdfr 88177633Sdfr { ACTIVITY_COLLECTION, D_, "href", HREF, 89177633Sdfr TRUE, { NULL }, TRUE }, 90177633Sdfr 91177633Sdfr { 0 } 92177633Sdfr}; 93177633Sdfr 94177633Sdfr 95177633Sdfr/* Conforms to svn_ra_serf__xml_closed_t */ 96177633Sdfrstatic svn_error_t * 97177633Sdfroptions_closed(svn_ra_serf__xml_estate_t *xes, 98177633Sdfr void *baton, 99177633Sdfr int leaving_state, 100177633Sdfr const svn_string_t *cdata, 101177633Sdfr apr_hash_t *attrs, 102177633Sdfr apr_pool_t *scratch_pool) 103177633Sdfr{ 104177633Sdfr options_context_t *opt_ctx = baton; 105177633Sdfr 106177633Sdfr SVN_ERR_ASSERT(leaving_state == HREF); 107177633Sdfr SVN_ERR_ASSERT(cdata != NULL); 108177633Sdfr 109177633Sdfr opt_ctx->activity_collection = svn_urlpath__canonicalize(cdata->data, 110177633Sdfr opt_ctx->pool); 111177633Sdfr 112177633Sdfr return SVN_NO_ERROR; 113180025Sdfr} 114180025Sdfr 115180025Sdfr 116180025Sdfrstatic svn_error_t * 117180025Sdfrcreate_options_body(serf_bucket_t **body_bkt, 118180025Sdfr void *baton, 119177633Sdfr serf_bucket_alloc_t *alloc, 120177633Sdfr apr_pool_t *pool) 121184588Sdfr{ 122184588Sdfr serf_bucket_t *body; 123184588Sdfr body = serf_bucket_aggregate_create(alloc); 124184588Sdfr svn_ra_serf__add_xml_header_buckets(body, alloc); 125184588Sdfr svn_ra_serf__add_open_tag_buckets(body, alloc, "D:options", 126184588Sdfr "xmlns:D", "DAV:", 127184588Sdfr NULL); 128184588Sdfr svn_ra_serf__add_tag_buckets(body, "D:activity-collection-set", NULL, alloc); 129184588Sdfr svn_ra_serf__add_close_tag_buckets(body, alloc, "D:options"); 130180025Sdfr 131180025Sdfr *body_bkt = body; 132180025Sdfr return SVN_NO_ERROR; 133180025Sdfr} 134180025Sdfr 135180025Sdfr 136180025Sdfr/* We use these static pointers so we can employ pointer comparison 137184588Sdfr * of our capabilities hash members instead of strcmp()ing all over 138184588Sdfr * the place. 139180025Sdfr */ 140180025Sdfr/* Both server and repository support the capability. */ 141180025Sdfrstatic const char *const capability_yes = "yes"; 142180025Sdfr/* Either server or repository does not support the capability. */ 143177633Sdfrstatic const char *const capability_no = "no"; 144177633Sdfr/* Server supports the capability, but don't yet know if repository does. */ 145177633Sdfrstatic const char *const capability_server_yes = "server-yes"; 146177633Sdfr 147177633Sdfr 148180025Sdfr/* This implements serf_bucket_headers_do_callback_fn_t. 149180025Sdfr */ 150177633Sdfrstatic int 151177633Sdfrcapabilities_headers_iterator_callback(void *baton, 152177633Sdfr const char *key, 153177633Sdfr const char *val) 154184588Sdfr{ 155184588Sdfr options_context_t *opt_ctx = baton; 156177633Sdfr svn_ra_serf__session_t *session = opt_ctx->session; 157177633Sdfr 158177633Sdfr if (svn_cstring_casecmp(key, "dav") == 0) 159177633Sdfr { 160177633Sdfr /* Each header may contain multiple values, separated by commas, e.g.: 161177633Sdfr DAV: version-control,checkout,working-resource 162177633Sdfr DAV: merge,baseline,activity,version-controlled-collection 163177633Sdfr DAV: http://subversion.tigris.org/xmlns/dav/svn/depth */ 164184588Sdfr apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, 165184588Sdfr opt_ctx->pool); 166177633Sdfr 167177633Sdfr /* Right now we only have a few capabilities to detect, so just 168177633Sdfr seek for them directly. This could be written slightly more 169177633Sdfr efficiently, but that wouldn't be worth it until we have many 170177633Sdfr more capabilities. */ 171177633Sdfr 172180025Sdfr if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_DEPTH, vals)) 173180025Sdfr { 174180025Sdfr svn_hash_sets(session->capabilities, 175180025Sdfr SVN_RA_CAPABILITY_DEPTH, capability_yes); 176180025Sdfr } 177180025Sdfr if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_MERGEINFO, vals)) 178180025Sdfr { 179180025Sdfr /* The server doesn't know what repository we're referring 180180025Sdfr to, so it can't just say capability_yes. */ 181180025Sdfr if (!svn_hash_gets(session->capabilities, 182180025Sdfr SVN_RA_CAPABILITY_MERGEINFO)) 183180025Sdfr { 184180025Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, 185180025Sdfr capability_server_yes); 186180025Sdfr } 187180025Sdfr } 188180025Sdfr if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_LOG_REVPROPS, vals)) 189180025Sdfr { 190180025Sdfr svn_hash_sets(session->capabilities, 191180025Sdfr SVN_RA_CAPABILITY_LOG_REVPROPS, capability_yes); 192180025Sdfr } 193180025Sdfr if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS, vals)) 194177633Sdfr { 195177633Sdfr svn_hash_sets(session->capabilities, 196177633Sdfr SVN_RA_CAPABILITY_ATOMIC_REVPROPS, capability_yes); 197177633Sdfr } 198177633Sdfr if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY, vals)) 199177633Sdfr { 200177633Sdfr svn_hash_sets(session->capabilities, 201177633Sdfr SVN_RA_CAPABILITY_PARTIAL_REPLAY, capability_yes); 202180025Sdfr } 203180025Sdfr if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INHERITED_PROPS, vals)) 204180025Sdfr { 205180025Sdfr svn_hash_sets(session->capabilities, 206177633Sdfr SVN_RA_CAPABILITY_INHERITED_PROPS, capability_yes); 207177633Sdfr } 208177633Sdfr if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REVERSE_FILE_REVS, 209177633Sdfr vals)) 210177633Sdfr { 211177633Sdfr svn_hash_sets(session->capabilities, 212177633Sdfr SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE, 213177633Sdfr capability_yes); 214177633Sdfr } 215177633Sdfr if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_EPHEMERAL_TXNPROPS, vals)) 216177633Sdfr { 217177633Sdfr svn_hash_sets(session->capabilities, 218177633Sdfr SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS, capability_yes); 219177633Sdfr } 220177633Sdfr if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INLINE_PROPS, vals)) 221180025Sdfr { 222180025Sdfr session->supports_inline_props = TRUE; 223180025Sdfr } 224180025Sdfr if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REPLAY_REV_RESOURCE, vals)) 225180025Sdfr { 226180025Sdfr session->supports_rev_rsrc_replay = TRUE; 227180025Sdfr } 228177633Sdfr } 229184588Sdfr 230184588Sdfr /* SVN-specific headers -- if present, server supports HTTP protocol v2 */ 231184588Sdfr else if (!svn_ctype_casecmp(key[0], 'S') 232184588Sdfr && !svn_ctype_casecmp(key[1], 'V') 233184588Sdfr && !svn_ctype_casecmp(key[2], 'N')) 234184588Sdfr { 235184588Sdfr /* If we've not yet seen any information about supported POST 236184588Sdfr requests, we'll initialize the list/hash with "create-txn" 237184588Sdfr (which we know is supported by virtue of the server speaking 238184588Sdfr HTTPv2 at all. */ 239177633Sdfr if (! session->supported_posts) 240184588Sdfr { 241184588Sdfr session->supported_posts = apr_hash_make(session->pool); 242184588Sdfr apr_hash_set(session->supported_posts, "create-txn", 10, (void *)1); 243184588Sdfr } 244184588Sdfr 245184588Sdfr if (svn_cstring_casecmp(key, SVN_DAV_ROOT_URI_HEADER) == 0) 246184588Sdfr { 247184588Sdfr session->repos_root = session->session_url; 248184588Sdfr session->repos_root.path = 249184588Sdfr (char *)svn_fspath__canonicalize(val, session->pool); 250184588Sdfr session->repos_root_str = 251184588Sdfr svn_urlpath__canonicalize( 252184588Sdfr apr_uri_unparse(session->pool, &session->repos_root, 0), 253184588Sdfr session->pool); 254184588Sdfr } 255184588Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_ME_RESOURCE_HEADER) == 0) 256180025Sdfr { 257180025Sdfr#ifdef SVN_DEBUG 258180025Sdfr char *ignore_v2_env_var = getenv(SVN_IGNORE_V2_ENV_VAR); 259180025Sdfr 260180025Sdfr if (!(ignore_v2_env_var 261180025Sdfr && apr_strnatcasecmp(ignore_v2_env_var, "yes") == 0)) 262180025Sdfr session->me_resource = apr_pstrdup(session->pool, val); 263180025Sdfr#else 264180025Sdfr session->me_resource = apr_pstrdup(session->pool, val); 265180025Sdfr#endif 266180025Sdfr } 267184588Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_REV_STUB_HEADER) == 0) 268184588Sdfr { 269180025Sdfr session->rev_stub = apr_pstrdup(session->pool, val); 270180025Sdfr } 271180025Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_REV_ROOT_STUB_HEADER) == 0) 272180025Sdfr { 273177633Sdfr session->rev_root_stub = apr_pstrdup(session->pool, val); 274177633Sdfr } 275177633Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_TXN_STUB_HEADER) == 0) 276177633Sdfr { 277177633Sdfr session->txn_stub = apr_pstrdup(session->pool, val); 278177633Sdfr } 279177633Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_TXN_ROOT_STUB_HEADER) == 0) 280177633Sdfr { 281177633Sdfr session->txn_root_stub = apr_pstrdup(session->pool, val); 282180025Sdfr } 283184588Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_STUB_HEADER) == 0) 284184588Sdfr { 285184588Sdfr session->vtxn_stub = apr_pstrdup(session->pool, val); 286184588Sdfr } 287184588Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_ROOT_STUB_HEADER) == 0) 288184588Sdfr { 289180025Sdfr session->vtxn_root_stub = apr_pstrdup(session->pool, val); 290180025Sdfr } 291180025Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_REPOS_UUID_HEADER) == 0) 292180025Sdfr { 293180025Sdfr session->uuid = apr_pstrdup(session->pool, val); 294180025Sdfr } 295180025Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_YOUNGEST_REV_HEADER) == 0) 296180025Sdfr { 297177633Sdfr opt_ctx->youngest_rev = SVN_STR_TO_REV(val); 298177633Sdfr } 299177633Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_ALLOW_BULK_UPDATES) == 0) 300177633Sdfr { 301177633Sdfr session->server_allows_bulk = apr_pstrdup(session->pool, val); 302177633Sdfr } 303177633Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_SUPPORTED_POSTS_HEADER) == 0) 304177633Sdfr { 305177633Sdfr /* May contain multiple values, separated by commas. */ 306177633Sdfr int i; 307177633Sdfr apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, 308177633Sdfr session->pool); 309177633Sdfr 310177633Sdfr for (i = 0; i < vals->nelts; i++) 311177633Sdfr { 312177633Sdfr const char *post_val = APR_ARRAY_IDX(vals, i, const char *); 313177633Sdfr 314177633Sdfr svn_hash_sets(session->supported_posts, post_val, (void *)1); 315177633Sdfr } 316177633Sdfr } 317177633Sdfr else if (svn_cstring_casecmp(key, SVN_DAV_REPOSITORY_MERGEINFO) == 0) 318177633Sdfr { 319177633Sdfr if (svn_cstring_casecmp(val, "yes") == 0) 320177633Sdfr { 321177633Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, 322177633Sdfr capability_yes); 323177633Sdfr } 324177633Sdfr else if (svn_cstring_casecmp(val, "no") == 0) 325177633Sdfr { 326177633Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, 327177633Sdfr capability_no); 328177633Sdfr } 329177633Sdfr } 330177633Sdfr } 331177633Sdfr 332177633Sdfr return 0; 333177633Sdfr} 334177633Sdfr 335177633Sdfr 336177633Sdfr/* A custom serf_response_handler_t which is mostly a wrapper around 337177633Sdfr the expat-based response handler -- it just notices OPTIONS response 338177633Sdfr headers first, before handing off to the xml parser. 339177633Sdfr Implements svn_ra_serf__response_handler_t */ 340177633Sdfrstatic svn_error_t * 341177633Sdfroptions_response_handler(serf_request_t *request, 342177633Sdfr serf_bucket_t *response, 343177633Sdfr void *baton, 344177633Sdfr apr_pool_t *pool) 345177633Sdfr{ 346177633Sdfr options_context_t *opt_ctx = baton; 347177633Sdfr 348177633Sdfr if (!opt_ctx->headers_processed) 349177633Sdfr { 350177633Sdfr svn_ra_serf__session_t *session = opt_ctx->session; 351177633Sdfr serf_bucket_t *hdrs = serf_bucket_response_get_headers(response); 352177633Sdfr 353177633Sdfr /* Start out assuming all capabilities are unsupported. */ 354177633Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY, 355177633Sdfr capability_no); 356177633Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_DEPTH, 357177633Sdfr capability_no); 358177633Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, 359177633Sdfr NULL); 360177633Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS, 361177633Sdfr capability_no); 362177633Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS, 363177633Sdfr capability_no); 364177633Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_INHERITED_PROPS, 365177633Sdfr capability_no); 366177633Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS, 367177633Sdfr capability_no); 368177633Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE, 369177633Sdfr capability_no); 370177633Sdfr 371180025Sdfr /* Then see which ones we can discover. */ 372180025Sdfr serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback, 373184588Sdfr opt_ctx); 374184588Sdfr 375244008Srmacklem /* Assume mergeinfo capability unsupported, if didn't recieve information 376177633Sdfr about server or repository mergeinfo capability. */ 377177633Sdfr if (!svn_hash_gets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO)) 378177633Sdfr svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, 379177633Sdfr capability_no); 380177633Sdfr 381177633Sdfr opt_ctx->headers_processed = TRUE; 382177633Sdfr } 383177633Sdfr 384177633Sdfr /* Execute the 'real' response handler to XML-parse the response body. */ 385177633Sdfr return opt_ctx->inner_handler(request, response, opt_ctx->inner_baton, pool); 386177633Sdfr} 387177633Sdfr 388177633Sdfr 389177633Sdfrstatic svn_error_t * 390177633Sdfrcreate_options_req(options_context_t **opt_ctx, 391177633Sdfr svn_ra_serf__session_t *session, 392177633Sdfr svn_ra_serf__connection_t *conn, 393177633Sdfr apr_pool_t *pool) 394177633Sdfr{ 395177633Sdfr options_context_t *new_ctx; 396177633Sdfr svn_ra_serf__xml_context_t *xmlctx; 397177633Sdfr svn_ra_serf__handler_t *handler; 398177633Sdfr 399177633Sdfr new_ctx = apr_pcalloc(pool, sizeof(*new_ctx)); 400177633Sdfr new_ctx->pool = pool; 401177633Sdfr new_ctx->session = session; 402177633Sdfr new_ctx->conn = conn; 403177633Sdfr 404177633Sdfr new_ctx->youngest_rev = SVN_INVALID_REVNUM; 405177633Sdfr 406177633Sdfr xmlctx = svn_ra_serf__xml_context_create(options_ttable, 407177633Sdfr NULL, options_closed, NULL, 408177633Sdfr new_ctx, 409177633Sdfr pool); 410177633Sdfr handler = svn_ra_serf__create_expat_handler(xmlctx, pool); 411177633Sdfr 412177633Sdfr handler->method = "OPTIONS"; 413177633Sdfr handler->path = session->session_url.path; 414177633Sdfr handler->body_delegate = create_options_body; 415177633Sdfr handler->body_type = "text/xml"; 416177633Sdfr handler->conn = conn; 417177633Sdfr handler->session = session; 418177633Sdfr 419177633Sdfr new_ctx->handler = handler; 420177633Sdfr 421177633Sdfr new_ctx->inner_handler = handler->response_handler; 422177633Sdfr new_ctx->inner_baton = handler->response_baton; 423177633Sdfr handler->response_handler = options_response_handler; 424177633Sdfr handler->response_baton = new_ctx; 425177633Sdfr 426177633Sdfr *opt_ctx = new_ctx; 427177633Sdfr 428177633Sdfr return SVN_NO_ERROR; 429177633Sdfr} 430177633Sdfr 431177633Sdfr 432177633Sdfrsvn_error_t * 433177633Sdfrsvn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest, 434177633Sdfr svn_ra_serf__connection_t *conn, 435177633Sdfr apr_pool_t *scratch_pool) 436177633Sdfr{ 437221127Srmacklem svn_ra_serf__session_t *session = conn->session; 438177633Sdfr options_context_t *opt_ctx; 439177633Sdfr 440177633Sdfr SVN_ERR_ASSERT(SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); 441221127Srmacklem 442177633Sdfr SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool)); 443177633Sdfr SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool)); 444177633Sdfr SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline, 445177633Sdfr opt_ctx->handler->path, 446177633Sdfr opt_ctx->handler->location)); 447177633Sdfr 448177633Sdfr *youngest = opt_ctx->youngest_rev; 449177633Sdfr SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(*youngest)); 450177633Sdfr 451177633Sdfr return SVN_NO_ERROR; 452177633Sdfr} 453177633Sdfr 454177633Sdfr 455177633Sdfrsvn_error_t * 456177633Sdfrsvn_ra_serf__v1_get_activity_collection(const char **activity_url, 457177633Sdfr svn_ra_serf__connection_t *conn, 458177633Sdfr apr_pool_t *result_pool, 459177633Sdfr apr_pool_t *scratch_pool) 460177633Sdfr{ 461177633Sdfr svn_ra_serf__session_t *session = conn->session; 462177633Sdfr options_context_t *opt_ctx; 463177633Sdfr 464177633Sdfr SVN_ERR_ASSERT(!SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); 465177633Sdfr 466177633Sdfr SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool)); 467177633Sdfr SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool)); 468177633Sdfr 469177633Sdfr SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline, 470177633Sdfr opt_ctx->handler->path, 471177633Sdfr opt_ctx->handler->location)); 472177633Sdfr 473177633Sdfr *activity_url = apr_pstrdup(result_pool, opt_ctx->activity_collection); 474177633Sdfr 475177633Sdfr return SVN_NO_ERROR; 476177633Sdfr 477177633Sdfr} 478177633Sdfr 479177633Sdfr 480177633Sdfr 481177633Sdfr/** Capabilities exchange. */ 482177633Sdfr 483177633Sdfrsvn_error_t * 484177633Sdfrsvn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess, 485177633Sdfr const char **corrected_url, 486177633Sdfr apr_pool_t *pool) 487177633Sdfr{ 488177633Sdfr options_context_t *opt_ctx; 489177633Sdfr svn_error_t *err; 490177633Sdfr 491177633Sdfr /* This routine automatically fills in serf_sess->capabilities */ 492177633Sdfr SVN_ERR(create_options_req(&opt_ctx, serf_sess, serf_sess->conns[0], pool)); 493177633Sdfr 494177633Sdfr err = svn_ra_serf__context_run_one(opt_ctx->handler, pool); 495177633Sdfr 496177633Sdfr /* If our caller cares about server redirections, and our response 497177633Sdfr carries such a thing, report as much. We'll disregard ERR -- 498177633Sdfr it's most likely just a complaint about the response body not 499177633Sdfr successfully parsing as XML or somesuch. */ 500177633Sdfr if (corrected_url && (opt_ctx->handler->sline.code == 301)) 501177633Sdfr { 502177633Sdfr svn_error_clear(err); 503177633Sdfr *corrected_url = opt_ctx->handler->location; 504177633Sdfr return SVN_NO_ERROR; 505177633Sdfr } 506177633Sdfr 507177633Sdfr SVN_ERR(svn_error_compose_create( 508177633Sdfr svn_ra_serf__error_on_status(opt_ctx->handler->sline, 509177633Sdfr serf_sess->session_url.path, 510177633Sdfr opt_ctx->handler->location), 511177633Sdfr err)); 512177633Sdfr 513177633Sdfr /* Opportunistically cache any reported activity URL. (We don't 514177633Sdfr want to have to ask for this again later, potentially against an 515177633Sdfr unreadable commit anchor URL.) */ 516177633Sdfr if (opt_ctx->activity_collection) 517177633Sdfr { 518177633Sdfr serf_sess->activity_collection_url = 519177633Sdfr apr_pstrdup(serf_sess->pool, opt_ctx->activity_collection); 520177633Sdfr } 521177633Sdfr 522177633Sdfr return SVN_NO_ERROR; 523177633Sdfr} 524177633Sdfr 525177633Sdfr 526177633Sdfrstatic svn_error_t * 527177633Sdfrcreate_simple_options_body(serf_bucket_t **body_bkt, 528177633Sdfr void *baton, 529177633Sdfr serf_bucket_alloc_t *alloc, 530177633Sdfr apr_pool_t *pool) 531177633Sdfr{ 532177633Sdfr serf_bucket_t *body; 533177633Sdfr serf_bucket_t *s; 534177633Sdfr 535177633Sdfr body = serf_bucket_aggregate_create(alloc); 536177633Sdfr svn_ra_serf__add_xml_header_buckets(body, alloc); 537177633Sdfr 538177633Sdfr s = SERF_BUCKET_SIMPLE_STRING("<D:options xmlns:D=\"DAV:\" />", alloc); 539177633Sdfr serf_bucket_aggregate_append(body, s); 540177633Sdfr 541177633Sdfr *body_bkt = body; 542177633Sdfr return SVN_NO_ERROR; 543177633Sdfr} 544177633Sdfr 545177633Sdfr 546177633Sdfrsvn_error_t * 547177633Sdfrsvn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess, 548177633Sdfr apr_pool_t *scratch_pool) 549177633Sdfr{ 550177633Sdfr svn_ra_serf__handler_t *handler; 551177633Sdfr 552177633Sdfr handler = apr_pcalloc(scratch_pool, sizeof(*handler)); 553177633Sdfr handler->handler_pool = scratch_pool; 554177633Sdfr handler->method = "OPTIONS"; 555177633Sdfr handler->path = serf_sess->session_url.path; 556177633Sdfr handler->conn = serf_sess->conns[0]; 557177633Sdfr handler->session = serf_sess; 558177633Sdfr 559177633Sdfr /* We don't care about the response body, so discard it. */ 560177633Sdfr handler->response_handler = svn_ra_serf__handle_discard_body; 561177633Sdfr 562177633Sdfr /* We need a simple body, in order to send it in chunked format. */ 563177633Sdfr handler->body_delegate = create_simple_options_body; 564177633Sdfr 565177633Sdfr /* No special headers. */ 566177633Sdfr 567177633Sdfr SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); 568177633Sdfr /* Some versions of nginx in reverse proxy mode will return 411. They want 569177633Sdfr a Content-Length header, rather than chunked requests. We can keep other 570177633Sdfr HTTP/1.1 features, but will disable the chunking. */ 571177633Sdfr if (handler->sline.code == 411) 572177633Sdfr { 573177633Sdfr serf_sess->using_chunked_requests = FALSE; 574177633Sdfr 575177633Sdfr return SVN_NO_ERROR; 576177633Sdfr } 577177633Sdfr SVN_ERR(svn_ra_serf__error_on_status(handler->sline, 578177633Sdfr handler->path, 579177633Sdfr handler->location)); 580177633Sdfr 581177633Sdfr return SVN_NO_ERROR; 582177633Sdfr} 583177633Sdfr 584177633Sdfr 585177633Sdfrsvn_error_t * 586177633Sdfrsvn_ra_serf__has_capability(svn_ra_session_t *ra_session, 587177633Sdfr svn_boolean_t *has, 588177633Sdfr const char *capability, 589177633Sdfr apr_pool_t *pool) 590177633Sdfr{ 591177633Sdfr svn_ra_serf__session_t *serf_sess = ra_session->priv; 592177633Sdfr const char *cap_result; 593177633Sdfr 594177633Sdfr /* This capability doesn't rely on anything server side. */ 595177633Sdfr if (strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0) 596177633Sdfr { 597177633Sdfr *has = TRUE; 598177633Sdfr return SVN_NO_ERROR; 599177633Sdfr } 600177633Sdfr 601177633Sdfr cap_result = svn_hash_gets(serf_sess->capabilities, capability); 602177633Sdfr 603177633Sdfr /* If any capability is unknown, they're all unknown, so ask. */ 604177633Sdfr if (cap_result == NULL) 605177633Sdfr SVN_ERR(svn_ra_serf__exchange_capabilities(serf_sess, NULL, pool)); 606177633Sdfr 607177633Sdfr /* Try again, now that we've fetched the capabilities. */ 608177633Sdfr cap_result = svn_hash_gets(serf_sess->capabilities, capability); 609177633Sdfr 610177633Sdfr /* Some capabilities depend on the repository as well as the server. */ 611177633Sdfr if (cap_result == capability_server_yes) 612177633Sdfr { 613177633Sdfr if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0) 614177633Sdfr { 615177633Sdfr /* Handle mergeinfo specially. Mergeinfo depends on the 616177633Sdfr repository as well as the server, but the server routine 617177633Sdfr that answered our svn_ra_serf__exchange_capabilities() call above 618177633Sdfr didn't even know which repository we were interested in 619177633Sdfr -- it just told us whether the server supports mergeinfo. 620177633Sdfr If the answer was 'no', there's no point checking the 621177633Sdfr particular repository; but if it was 'yes', we still must 622177633Sdfr change it to 'no' iff the repository itself doesn't 623177633Sdfr support mergeinfo. */ 624177633Sdfr svn_mergeinfo_catalog_t ignored; 625177633Sdfr svn_error_t *err; 626177633Sdfr apr_array_header_t *paths = apr_array_make(pool, 1, 627177633Sdfr sizeof(char *)); 628177633Sdfr APR_ARRAY_PUSH(paths, const char *) = ""; 629177633Sdfr 630177633Sdfr err = svn_ra_serf__get_mergeinfo(ra_session, &ignored, paths, 0, 631177633Sdfr FALSE, FALSE, pool); 632177633Sdfr 633177633Sdfr if (err) 634177633Sdfr { 635177633Sdfr if (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE) 636177633Sdfr { 637177633Sdfr svn_error_clear(err); 638177633Sdfr cap_result = capability_no; 639177633Sdfr } 640177633Sdfr else if (err->apr_err == SVN_ERR_FS_NOT_FOUND) 641177633Sdfr { 642177633Sdfr /* Mergeinfo requests use relative paths, and 643177633Sdfr anyway we're in r0, so this is a likely error, 644177633Sdfr but it means the repository supports mergeinfo! */ 645177633Sdfr svn_error_clear(err); 646177633Sdfr cap_result = capability_yes; 647177633Sdfr } 648177633Sdfr else 649180025Sdfr return err; 650177633Sdfr } 651177633Sdfr else 652177633Sdfr cap_result = capability_yes; 653177633Sdfr 654177633Sdfr svn_hash_sets(serf_sess->capabilities, 655177633Sdfr SVN_RA_CAPABILITY_MERGEINFO, cap_result); 656177633Sdfr } 657177633Sdfr else 658177633Sdfr { 659177633Sdfr return svn_error_createf 660177633Sdfr (SVN_ERR_UNKNOWN_CAPABILITY, NULL, 661177633Sdfr _("Don't know how to handle '%s' for capability '%s'"), 662177633Sdfr capability_server_yes, capability); 663177633Sdfr } 664177633Sdfr } 665177633Sdfr 666177633Sdfr if (cap_result == capability_yes) 667177633Sdfr { 668177633Sdfr *has = TRUE; 669177633Sdfr } 670177633Sdfr else if (cap_result == capability_no) 671177633Sdfr { 672177633Sdfr *has = FALSE; 673177633Sdfr } 674177633Sdfr else if (cap_result == NULL) 675177633Sdfr { 676177633Sdfr return svn_error_createf 677177633Sdfr (SVN_ERR_UNKNOWN_CAPABILITY, NULL, 678177633Sdfr _("Don't know anything about capability '%s'"), capability); 679177633Sdfr } 680177633Sdfr else /* "can't happen" */ 681177633Sdfr { 682177633Sdfr /* Well, let's hope it's a string. */ 683177633Sdfr return svn_error_createf 684177633Sdfr (SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL, 685177633Sdfr _("Attempt to fetch capability '%s' resulted in '%s'"), 686177633Sdfr capability, cap_result); 687177633Sdfr } 688177633Sdfr 689177633Sdfr return SVN_NO_ERROR; 690177633Sdfr} 691177633Sdfr