diff options
Diffstat (limited to 'subversion/libsvn_ra_serf/options.c')
-rw-r--r-- | subversion/libsvn_ra_serf/options.c | 611 |
1 files changed, 332 insertions, 279 deletions
diff --git a/subversion/libsvn_ra_serf/options.c b/subversion/libsvn_ra_serf/options.c index 8bdc8fd..5389b04 100644 --- a/subversion/libsvn_ra_serf/options.c +++ b/subversion/libsvn_ra_serf/options.c @@ -28,10 +28,12 @@ #include <serf.h> #include "svn_dirent_uri.h" +#include "svn_hash.h" #include "svn_pools.h" #include "svn_ra.h" #include "svn_dav.h" #include "svn_xml.h" +#include "svn_ctype.h" #include "../libsvn_ra/ra_loader.h" #include "svn_private_config.h" @@ -48,167 +50,68 @@ /* * This enum represents the current state of our XML parsing for an OPTIONS. */ -typedef enum options_state_e { +enum options_state_e { + INITIAL = 0, OPTIONS, ACTIVITY_COLLECTION, HREF -} options_state_e; - -typedef struct options_state_list_t { - /* The current state that we are in now. */ - options_state_e state; - - /* The previous state we were in. */ - struct options_state_list_t *prev; -} options_state_list_t; +}; -struct svn_ra_serf__options_context_t { +typedef struct options_context_t { /* pool to allocate memory from */ apr_pool_t *pool; - const char *attr_val; - apr_size_t attr_val_len; - svn_boolean_t collect_cdata; - - /* Current state we're in */ - options_state_list_t *state; - options_state_list_t *free_state; - - /* HTTP Status code */ - int status_code; - - /* are we done? */ - svn_boolean_t done; + /* Have we extracted options values from the headers already? */ + svn_boolean_t headers_processed; svn_ra_serf__session_t *session; svn_ra_serf__connection_t *conn; + svn_ra_serf__handler_t *handler; - const char *path; + svn_ra_serf__response_handler_t inner_handler; + void *inner_baton; const char *activity_collection; svn_revnum_t youngest_rev; - serf_response_acceptor_t acceptor; - serf_response_handler_t handler; - svn_ra_serf__xml_parser_t *parser_ctx; +} options_context_t; -}; +#define D_ "DAV:" +#define S_ SVN_XML_NAMESPACE +static const svn_ra_serf__xml_transition_t options_ttable[] = { + { INITIAL, D_, "options-response", OPTIONS, + FALSE, { NULL }, FALSE }, -static void -push_state(svn_ra_serf__options_context_t *options_ctx, options_state_e state) -{ - options_state_list_t *new_state; + { OPTIONS, D_, "activity-collection-set", ACTIVITY_COLLECTION, + FALSE, { NULL }, FALSE }, - if (!options_ctx->free_state) - { - new_state = apr_palloc(options_ctx->pool, sizeof(*options_ctx->state)); - } - else - { - new_state = options_ctx->free_state; - options_ctx->free_state = options_ctx->free_state->prev; - } - new_state->state = state; - - /* Add it to the state chain. */ - new_state->prev = options_ctx->state; - options_ctx->state = new_state; -} - -static void pop_state(svn_ra_serf__options_context_t *options_ctx) -{ - options_state_list_t *free_state; - free_state = options_ctx->state; - /* advance the current state */ - options_ctx->state = options_ctx->state->prev; - free_state->prev = options_ctx->free_state; - options_ctx->free_state = free_state; -} - -static svn_error_t * -start_options(svn_ra_serf__xml_parser_t *parser, - void *userData, - svn_ra_serf__dav_props_t name, - const char **attrs) -{ - svn_ra_serf__options_context_t *options_ctx = userData; + { ACTIVITY_COLLECTION, D_, "href", HREF, + TRUE, { NULL }, TRUE }, - if (!options_ctx->state && strcmp(name.name, "options-response") == 0) - { - push_state(options_ctx, OPTIONS); - } - else if (!options_ctx->state) - { - /* Nothing to do. */ - return SVN_NO_ERROR; - } - else if (options_ctx->state->state == OPTIONS && - strcmp(name.name, "activity-collection-set") == 0) - { - push_state(options_ctx, ACTIVITY_COLLECTION); - } - else if (options_ctx->state->state == ACTIVITY_COLLECTION && - strcmp(name.name, "href") == 0) - { - options_ctx->collect_cdata = TRUE; - push_state(options_ctx, HREF); - } + { 0 } +}; - return SVN_NO_ERROR; -} +/* Conforms to svn_ra_serf__xml_closed_t */ static svn_error_t * -end_options(svn_ra_serf__xml_parser_t *parser, - void *userData, - svn_ra_serf__dav_props_t name) +options_closed(svn_ra_serf__xml_estate_t *xes, + void *baton, + int leaving_state, + const svn_string_t *cdata, + apr_hash_t *attrs, + apr_pool_t *scratch_pool) { - svn_ra_serf__options_context_t *options_ctx = userData; - options_state_list_t *cur_state; - - if (!options_ctx->state) - { - return SVN_NO_ERROR; - } + options_context_t *opt_ctx = baton; - cur_state = options_ctx->state; + SVN_ERR_ASSERT(leaving_state == HREF); + SVN_ERR_ASSERT(cdata != NULL); - if (cur_state->state == OPTIONS && - strcmp(name.name, "options-response") == 0) - { - pop_state(options_ctx); - } - else if (cur_state->state == ACTIVITY_COLLECTION && - strcmp(name.name, "activity-collection-set") == 0) - { - pop_state(options_ctx); - } - else if (cur_state->state == HREF && - strcmp(name.name, "href") == 0) - { - options_ctx->collect_cdata = FALSE; - options_ctx->activity_collection = - svn_urlpath__canonicalize(options_ctx->attr_val, options_ctx->pool); - pop_state(options_ctx); - } + opt_ctx->activity_collection = svn_urlpath__canonicalize(cdata->data, + opt_ctx->pool); return SVN_NO_ERROR; } -static svn_error_t * -cdata_options(svn_ra_serf__xml_parser_t *parser, - void *userData, - const char *data, - apr_size_t len) -{ - svn_ra_serf__options_context_t *ctx = userData; - if (ctx->collect_cdata) - { - svn_ra_serf__expand_string(&ctx->attr_val, &ctx->attr_val_len, - data, len, ctx->pool); - } - - return SVN_NO_ERROR; -} static svn_error_t * create_options_body(serf_bucket_t **body_bkt, @@ -229,47 +132,17 @@ create_options_body(serf_bucket_t **body_bkt, return SVN_NO_ERROR; } -svn_boolean_t* -svn_ra_serf__get_options_done_ptr(svn_ra_serf__options_context_t *ctx) -{ - return &ctx->done; -} - -const char * -svn_ra_serf__options_get_activity_collection(svn_ra_serf__options_context_t *ctx) -{ - return ctx->activity_collection; -} - -svn_revnum_t -svn_ra_serf__options_get_youngest_rev(svn_ra_serf__options_context_t *ctx) -{ - return ctx->youngest_rev; -} - -/* Context for both options_response_handler() and capabilities callback. */ -struct options_response_ctx_t { - /* Baton for __handle_xml_parser() */ - svn_ra_serf__xml_parser_t *parser_ctx; - - /* Session into which we'll store server capabilities */ - svn_ra_serf__session_t *session; - - /* For temporary work only. */ - apr_pool_t *pool; -}; - /* We use these static pointers so we can employ pointer comparison * of our capabilities hash members instead of strcmp()ing all over * the place. */ /* Both server and repository support the capability. */ -static const char *capability_yes = "yes"; +static const char *const capability_yes = "yes"; /* Either server or repository does not support the capability. */ -static const char *capability_no = "no"; +static const char *const capability_no = "no"; /* Server supports the capability, but don't yet know if repository does. */ -static const char *capability_server_yes = "server-yes"; +static const char *const capability_server_yes = "server-yes"; /* This implements serf_bucket_headers_do_callback_fn_t. @@ -279,7 +152,8 @@ capabilities_headers_iterator_callback(void *baton, const char *key, const char *val) { - struct options_response_ctx_t *orc = baton; + options_context_t *opt_ctx = baton; + svn_ra_serf__session_t *session = opt_ctx->session; if (svn_cstring_casecmp(key, "dav") == 0) { @@ -287,7 +161,8 @@ capabilities_headers_iterator_callback(void *baton, DAV: version-control,checkout,working-resource DAV: merge,baseline,activity,version-controlled-collection DAV: http://subversion.tigris.org/xmlns/dav/svn/depth */ - apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, orc->pool); + apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, + opt_ctx->pool); /* Right now we only have a few capabilities to detect, so just seek for them directly. This could be written slightly more @@ -296,49 +171,86 @@ capabilities_headers_iterator_callback(void *baton, if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_DEPTH, vals)) { - apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_DEPTH, - APR_HASH_KEY_STRING, capability_yes); + svn_hash_sets(session->capabilities, + SVN_RA_CAPABILITY_DEPTH, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_MERGEINFO, vals)) { /* The server doesn't know what repository we're referring to, so it can't just say capability_yes. */ - apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, - APR_HASH_KEY_STRING, capability_server_yes); + if (!svn_hash_gets(session->capabilities, + SVN_RA_CAPABILITY_MERGEINFO)) + { + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, + capability_server_yes); + } } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_LOG_REVPROPS, vals)) { - apr_hash_set(orc->session->capabilities, - SVN_RA_CAPABILITY_LOG_REVPROPS, - APR_HASH_KEY_STRING, capability_yes); + svn_hash_sets(session->capabilities, + SVN_RA_CAPABILITY_LOG_REVPROPS, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS, vals)) { - apr_hash_set(orc->session->capabilities, - SVN_RA_CAPABILITY_ATOMIC_REVPROPS, - APR_HASH_KEY_STRING, capability_yes); + svn_hash_sets(session->capabilities, + SVN_RA_CAPABILITY_ATOMIC_REVPROPS, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY, vals)) { - apr_hash_set(orc->session->capabilities, - SVN_RA_CAPABILITY_PARTIAL_REPLAY, - APR_HASH_KEY_STRING, capability_yes); + svn_hash_sets(session->capabilities, + SVN_RA_CAPABILITY_PARTIAL_REPLAY, capability_yes); + } + if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INHERITED_PROPS, vals)) + { + svn_hash_sets(session->capabilities, + SVN_RA_CAPABILITY_INHERITED_PROPS, capability_yes); + } + if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REVERSE_FILE_REVS, + vals)) + { + svn_hash_sets(session->capabilities, + SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE, + capability_yes); + } + if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_EPHEMERAL_TXNPROPS, vals)) + { + svn_hash_sets(session->capabilities, + SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS, capability_yes); + } + if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INLINE_PROPS, vals)) + { + session->supports_inline_props = TRUE; + } + if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REPLAY_REV_RESOURCE, vals)) + { + session->supports_rev_rsrc_replay = TRUE; } } /* SVN-specific headers -- if present, server supports HTTP protocol v2 */ - else if (strncmp(key, "SVN", 3) == 0) + else if (!svn_ctype_casecmp(key[0], 'S') + && !svn_ctype_casecmp(key[1], 'V') + && !svn_ctype_casecmp(key[2], 'N')) { + /* If we've not yet seen any information about supported POST + requests, we'll initialize the list/hash with "create-txn" + (which we know is supported by virtue of the server speaking + HTTPv2 at all. */ + if (! session->supported_posts) + { + session->supported_posts = apr_hash_make(session->pool); + apr_hash_set(session->supported_posts, "create-txn", 10, (void *)1); + } + if (svn_cstring_casecmp(key, SVN_DAV_ROOT_URI_HEADER) == 0) { - orc->session->repos_root = orc->session->session_url; - orc->session->repos_root.path = apr_pstrdup(orc->session->pool, val); - orc->session->repos_root_str = + session->repos_root = session->session_url; + session->repos_root.path = + (char *)svn_fspath__canonicalize(val, session->pool); + session->repos_root_str = svn_urlpath__canonicalize( - apr_uri_unparse(orc->session->pool, - &orc->session->repos_root, - 0), - orc->session->pool); + apr_uri_unparse(session->pool, &session->repos_root, 0), + session->pool); } else if (svn_cstring_casecmp(key, SVN_DAV_ME_RESOURCE_HEADER) == 0) { @@ -347,44 +259,73 @@ capabilities_headers_iterator_callback(void *baton, if (!(ignore_v2_env_var && apr_strnatcasecmp(ignore_v2_env_var, "yes") == 0)) - orc->session->me_resource = apr_pstrdup(orc->session->pool, val); + session->me_resource = apr_pstrdup(session->pool, val); #else - orc->session->me_resource = apr_pstrdup(orc->session->pool, val); + session->me_resource = apr_pstrdup(session->pool, val); #endif } else if (svn_cstring_casecmp(key, SVN_DAV_REV_STUB_HEADER) == 0) { - orc->session->rev_stub = apr_pstrdup(orc->session->pool, val); + session->rev_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_REV_ROOT_STUB_HEADER) == 0) { - orc->session->rev_root_stub = apr_pstrdup(orc->session->pool, val); + session->rev_root_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_TXN_STUB_HEADER) == 0) { - orc->session->txn_stub = apr_pstrdup(orc->session->pool, val); + session->txn_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_TXN_ROOT_STUB_HEADER) == 0) { - orc->session->txn_root_stub = apr_pstrdup(orc->session->pool, val); + session->txn_root_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_STUB_HEADER) == 0) { - orc->session->vtxn_stub = apr_pstrdup(orc->session->pool, val); + session->vtxn_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_ROOT_STUB_HEADER) == 0) { - orc->session->vtxn_root_stub = apr_pstrdup(orc->session->pool, val); + session->vtxn_root_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_REPOS_UUID_HEADER) == 0) { - orc->session->uuid = apr_pstrdup(orc->session->pool, val); + session->uuid = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_YOUNGEST_REV_HEADER) == 0) { - struct svn_ra_serf__options_context_t *user_data = - orc->parser_ctx->user_data; - user_data->youngest_rev = SVN_STR_TO_REV(val); + opt_ctx->youngest_rev = SVN_STR_TO_REV(val); + } + else if (svn_cstring_casecmp(key, SVN_DAV_ALLOW_BULK_UPDATES) == 0) + { + session->server_allows_bulk = apr_pstrdup(session->pool, val); + } + else if (svn_cstring_casecmp(key, SVN_DAV_SUPPORTED_POSTS_HEADER) == 0) + { + /* May contain multiple values, separated by commas. */ + int i; + apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, + session->pool); + + for (i = 0; i < vals->nelts; i++) + { + const char *post_val = APR_ARRAY_IDX(vals, i, const char *); + + svn_hash_sets(session->supported_posts, post_val, (void *)1); + } + } + else if (svn_cstring_casecmp(key, SVN_DAV_REPOSITORY_MERGEINFO) == 0) + { + if (svn_cstring_casecmp(val, "yes") == 0) + { + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, + capability_yes); + } + else if (svn_cstring_casecmp(val, "no") == 0) + { + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, + capability_no); + } } } @@ -393,7 +334,7 @@ capabilities_headers_iterator_callback(void *baton, /* A custom serf_response_handler_t which is mostly a wrapper around - svn_ra_serf__handle_xml_parser -- it just notices OPTIONS response + the expat-based response handler -- it just notices OPTIONS response headers first, before handing off to the xml parser. Implements svn_ra_serf__response_handler_t */ static svn_error_t * @@ -402,86 +343,137 @@ options_response_handler(serf_request_t *request, void *baton, apr_pool_t *pool) { - struct options_response_ctx_t *orc = baton; - serf_bucket_t *hdrs = serf_bucket_response_get_headers(response); - - /* Start out assuming all capabilities are unsupported. */ - apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY, - APR_HASH_KEY_STRING, capability_no); - apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_DEPTH, - APR_HASH_KEY_STRING, capability_no); - apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, - APR_HASH_KEY_STRING, capability_no); - apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS, - APR_HASH_KEY_STRING, capability_no); - apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS, - APR_HASH_KEY_STRING, capability_no); - - /* Then see which ones we can discover. */ - serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback, orc); - - /* Execute the 'real' response handler to XML-parse the repsonse body. */ - return svn_ra_serf__handle_xml_parser(request, response, - orc->parser_ctx, pool); + options_context_t *opt_ctx = baton; + + if (!opt_ctx->headers_processed) + { + svn_ra_serf__session_t *session = opt_ctx->session; + serf_bucket_t *hdrs = serf_bucket_response_get_headers(response); + + /* Start out assuming all capabilities are unsupported. */ + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY, + capability_no); + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_DEPTH, + capability_no); + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, + NULL); + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS, + capability_no); + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS, + capability_no); + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_INHERITED_PROPS, + capability_no); + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS, + capability_no); + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE, + capability_no); + + /* Then see which ones we can discover. */ + serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback, + opt_ctx); + + /* Assume mergeinfo capability unsupported, if didn't recieve information + about server or repository mergeinfo capability. */ + if (!svn_hash_gets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO)) + svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, + capability_no); + + opt_ctx->headers_processed = TRUE; + } + + /* Execute the 'real' response handler to XML-parse the response body. */ + return opt_ctx->inner_handler(request, response, opt_ctx->inner_baton, pool); } -svn_error_t * -svn_ra_serf__create_options_req(svn_ra_serf__options_context_t **opt_ctx, - svn_ra_serf__session_t *session, - svn_ra_serf__connection_t *conn, - const char *path, - apr_pool_t *pool) +static svn_error_t * +create_options_req(options_context_t **opt_ctx, + svn_ra_serf__session_t *session, + svn_ra_serf__connection_t *conn, + apr_pool_t *pool) { - svn_ra_serf__options_context_t *new_ctx; + options_context_t *new_ctx; + svn_ra_serf__xml_context_t *xmlctx; svn_ra_serf__handler_t *handler; - svn_ra_serf__xml_parser_t *parser_ctx; - struct options_response_ctx_t *options_response_ctx; new_ctx = apr_pcalloc(pool, sizeof(*new_ctx)); - new_ctx->pool = pool; - - new_ctx->path = path; - new_ctx->youngest_rev = SVN_INVALID_REVNUM; - new_ctx->session = session; new_ctx->conn = conn; - handler = apr_pcalloc(pool, sizeof(*handler)); + new_ctx->youngest_rev = SVN_INVALID_REVNUM; + + xmlctx = svn_ra_serf__xml_context_create(options_ttable, + NULL, options_closed, NULL, + new_ctx, + pool); + handler = svn_ra_serf__create_expat_handler(xmlctx, pool); handler->method = "OPTIONS"; - handler->path = path; + handler->path = session->session_url.path; handler->body_delegate = create_options_body; handler->body_type = "text/xml"; handler->conn = conn; handler->session = session; - parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx)); + new_ctx->handler = handler; + + new_ctx->inner_handler = handler->response_handler; + new_ctx->inner_baton = handler->response_baton; + handler->response_handler = options_response_handler; + handler->response_baton = new_ctx; + + *opt_ctx = new_ctx; - parser_ctx->pool = pool; - parser_ctx->user_data = new_ctx; - parser_ctx->start = start_options; - parser_ctx->end = end_options; - parser_ctx->cdata = cdata_options; - parser_ctx->done = &new_ctx->done; - parser_ctx->status_code = &new_ctx->status_code; + return SVN_NO_ERROR; +} - options_response_ctx = apr_pcalloc(pool, sizeof(*options_response_ctx)); - options_response_ctx->parser_ctx = parser_ctx; - options_response_ctx->session = session; - options_response_ctx->pool = pool; - handler->response_handler = options_response_handler; - handler->response_baton = options_response_ctx; +svn_error_t * +svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest, + svn_ra_serf__connection_t *conn, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__session_t *session = conn->session; + options_context_t *opt_ctx; - svn_ra_serf__request_create(handler); + SVN_ERR_ASSERT(SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); - new_ctx->parser_ctx = parser_ctx; + SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool)); + SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool)); + SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline, + opt_ctx->handler->path, + opt_ctx->handler->location)); - *opt_ctx = new_ctx; + *youngest = opt_ctx->youngest_rev; + SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(*youngest)); + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_ra_serf__v1_get_activity_collection(const char **activity_url, + svn_ra_serf__connection_t *conn, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__session_t *session = conn->session; + options_context_t *opt_ctx; + + SVN_ERR_ASSERT(!SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); + + SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool)); + SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool)); + + SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline, + opt_ctx->handler->path, + opt_ctx->handler->location)); + + *activity_url = apr_pstrdup(result_pool, opt_ctx->activity_collection); return SVN_NO_ERROR; + } @@ -493,33 +485,100 @@ svn_ra_serf__exchange_capabilities(svn_ra_serf__session_t *serf_sess, const char **corrected_url, apr_pool_t *pool) { - svn_ra_serf__options_context_t *opt_ctx; + options_context_t *opt_ctx; svn_error_t *err; /* This routine automatically fills in serf_sess->capabilities */ - SVN_ERR(svn_ra_serf__create_options_req(&opt_ctx, serf_sess, - serf_sess->conns[0], - serf_sess->session_url.path, pool)); + SVN_ERR(create_options_req(&opt_ctx, serf_sess, serf_sess->conns[0], pool)); - err = svn_ra_serf__context_run_wait( - svn_ra_serf__get_options_done_ptr(opt_ctx), serf_sess, pool); + err = svn_ra_serf__context_run_one(opt_ctx->handler, pool); /* If our caller cares about server redirections, and our response carries such a thing, report as much. We'll disregard ERR -- it's most likely just a complaint about the response body not successfully parsing as XML or somesuch. */ - if (corrected_url && (opt_ctx->status_code == 301)) + if (corrected_url && (opt_ctx->handler->sline.code == 301)) { svn_error_clear(err); - *corrected_url = opt_ctx->parser_ctx->location; + *corrected_url = opt_ctx->handler->location; return SVN_NO_ERROR; } - return svn_error_compose_create( - svn_ra_serf__error_on_status(opt_ctx->status_code, - serf_sess->session_url.path, - opt_ctx->parser_ctx->location), - err); + SVN_ERR(svn_error_compose_create( + svn_ra_serf__error_on_status(opt_ctx->handler->sline, + serf_sess->session_url.path, + opt_ctx->handler->location), + err)); + + /* Opportunistically cache any reported activity URL. (We don't + want to have to ask for this again later, potentially against an + unreadable commit anchor URL.) */ + if (opt_ctx->activity_collection) + { + serf_sess->activity_collection_url = + apr_pstrdup(serf_sess->pool, opt_ctx->activity_collection); + } + + return SVN_NO_ERROR; +} + + +static svn_error_t * +create_simple_options_body(serf_bucket_t **body_bkt, + void *baton, + serf_bucket_alloc_t *alloc, + apr_pool_t *pool) +{ + serf_bucket_t *body; + serf_bucket_t *s; + + body = serf_bucket_aggregate_create(alloc); + svn_ra_serf__add_xml_header_buckets(body, alloc); + + s = SERF_BUCKET_SIMPLE_STRING("<D:options xmlns:D=\"DAV:\" />", alloc); + serf_bucket_aggregate_append(body, s); + + *body_bkt = body; + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__handler_t *handler; + + handler = apr_pcalloc(scratch_pool, sizeof(*handler)); + handler->handler_pool = scratch_pool; + handler->method = "OPTIONS"; + handler->path = serf_sess->session_url.path; + handler->conn = serf_sess->conns[0]; + handler->session = serf_sess; + + /* We don't care about the response body, so discard it. */ + handler->response_handler = svn_ra_serf__handle_discard_body; + + /* We need a simple body, in order to send it in chunked format. */ + handler->body_delegate = create_simple_options_body; + + /* No special headers. */ + + SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); + /* Some versions of nginx in reverse proxy mode will return 411. They want + a Content-Length header, rather than chunked requests. We can keep other + HTTP/1.1 features, but will disable the chunking. */ + if (handler->sline.code == 411) + { + serf_sess->using_chunked_requests = FALSE; + + return SVN_NO_ERROR; + } + SVN_ERR(svn_ra_serf__error_on_status(handler->sline, + handler->path, + handler->location)); + + return SVN_NO_ERROR; } @@ -539,21 +598,16 @@ svn_ra_serf__has_capability(svn_ra_session_t *ra_session, return SVN_NO_ERROR; } - cap_result = apr_hash_get(serf_sess->capabilities, - capability, - APR_HASH_KEY_STRING); + cap_result = svn_hash_gets(serf_sess->capabilities, capability); /* If any capability is unknown, they're all unknown, so ask. */ if (cap_result == NULL) SVN_ERR(svn_ra_serf__exchange_capabilities(serf_sess, NULL, pool)); /* Try again, now that we've fetched the capabilities. */ - cap_result = apr_hash_get(serf_sess->capabilities, - capability, APR_HASH_KEY_STRING); + cap_result = svn_hash_gets(serf_sess->capabilities, capability); - /* Some capabilities depend on the repository as well as the server. - NOTE: svn_ra_neon__has_capability() has a very similar code block. If - you change something here, check there as well. */ + /* Some capabilities depend on the repository as well as the server. */ if (cap_result == capability_server_yes) { if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0) @@ -597,9 +651,8 @@ svn_ra_serf__has_capability(svn_ra_session_t *ra_session, else cap_result = capability_yes; - apr_hash_set(serf_sess->capabilities, - SVN_RA_CAPABILITY_MERGEINFO, APR_HASH_KEY_STRING, - cap_result); + svn_hash_sets(serf_sess->capabilities, + SVN_RA_CAPABILITY_MERGEINFO, cap_result); } else { |