49
50
* This enum represents the current state of our XML parsing for an OPTIONS.
51
typedef enum options_state_e {
52
enum options_state_e {
53
55
ACTIVITY_COLLECTION,
57
typedef struct options_state_list_t {
58
/* The current state that we are in now. */
59
options_state_e state;
61
/* The previous state we were in. */
62
struct options_state_list_t *prev;
63
} options_state_list_t;
65
struct svn_ra_serf__options_context_t {
59
typedef struct options_context_t {
66
60
/* pool to allocate memory from */
70
apr_size_t attr_val_len;
71
svn_boolean_t collect_cdata;
73
/* Current state we're in */
74
options_state_list_t *state;
75
options_state_list_t *free_state;
77
/* HTTP Status code */
63
/* Have we extracted options values from the headers already? */
64
svn_boolean_t headers_processed;
83
66
svn_ra_serf__session_t *session;
84
67
svn_ra_serf__connection_t *conn;
68
svn_ra_serf__handler_t *handler;
70
svn_ra_serf__response_handler_t inner_handler;
88
73
const char *activity_collection;
89
74
svn_revnum_t youngest_rev;
91
serf_response_acceptor_t acceptor;
92
serf_response_handler_t handler;
93
svn_ra_serf__xml_parser_t *parser_ctx;
79
#define S_ SVN_XML_NAMESPACE
80
static const svn_ra_serf__xml_transition_t options_ttable[] = {
81
{ INITIAL, D_, "options-response", OPTIONS,
82
FALSE, { NULL }, FALSE },
84
{ OPTIONS, D_, "activity-collection-set", ACTIVITY_COLLECTION,
85
FALSE, { NULL }, FALSE },
87
{ ACTIVITY_COLLECTION, D_, "href", HREF,
88
TRUE, { NULL }, TRUE },
98
push_state(svn_ra_serf__options_context_t *options_ctx, options_state_e state)
100
options_state_list_t *new_state;
102
if (!options_ctx->free_state)
104
new_state = apr_palloc(options_ctx->pool, sizeof(*options_ctx->state));
108
new_state = options_ctx->free_state;
109
options_ctx->free_state = options_ctx->free_state->prev;
111
new_state->state = state;
113
/* Add it to the state chain. */
114
new_state->prev = options_ctx->state;
115
options_ctx->state = new_state;
118
static void pop_state(svn_ra_serf__options_context_t *options_ctx)
120
options_state_list_t *free_state;
121
free_state = options_ctx->state;
122
/* advance the current state */
123
options_ctx->state = options_ctx->state->prev;
124
free_state->prev = options_ctx->free_state;
125
options_ctx->free_state = free_state;
129
start_options(svn_ra_serf__xml_parser_t *parser,
131
svn_ra_serf__dav_props_t name,
134
svn_ra_serf__options_context_t *options_ctx = userData;
136
if (!options_ctx->state && strcmp(name.name, "options-response") == 0)
138
push_state(options_ctx, OPTIONS);
140
else if (!options_ctx->state)
145
else if (options_ctx->state->state == OPTIONS &&
146
strcmp(name.name, "activity-collection-set") == 0)
148
push_state(options_ctx, ACTIVITY_COLLECTION);
150
else if (options_ctx->state->state == ACTIVITY_COLLECTION &&
151
strcmp(name.name, "href") == 0)
153
options_ctx->collect_cdata = TRUE;
154
push_state(options_ctx, HREF);
161
end_options(svn_ra_serf__xml_parser_t *parser,
163
svn_ra_serf__dav_props_t name)
165
svn_ra_serf__options_context_t *options_ctx = userData;
166
options_state_list_t *cur_state;
168
if (!options_ctx->state)
173
cur_state = options_ctx->state;
175
if (cur_state->state == OPTIONS &&
176
strcmp(name.name, "options-response") == 0)
178
pop_state(options_ctx);
180
else if (cur_state->state == ACTIVITY_COLLECTION &&
181
strcmp(name.name, "activity-collection-set") == 0)
183
pop_state(options_ctx);
185
else if (cur_state->state == HREF &&
186
strcmp(name.name, "href") == 0)
188
options_ctx->collect_cdata = FALSE;
189
options_ctx->activity_collection =
190
svn_urlpath__canonicalize(options_ctx->attr_val, options_ctx->pool);
191
pop_state(options_ctx);
198
cdata_options(svn_ra_serf__xml_parser_t *parser,
203
svn_ra_serf__options_context_t *ctx = userData;
204
if (ctx->collect_cdata)
206
svn_ra_serf__expand_string(&ctx->attr_val, &ctx->attr_val_len,
207
data, len, ctx->pool);
94
/* Conforms to svn_ra_serf__xml_closed_t */
96
options_closed(svn_ra_serf__xml_estate_t *xes,
99
const svn_string_t *cdata,
101
apr_pool_t *scratch_pool)
103
options_context_t *opt_ctx = baton;
105
SVN_ERR_ASSERT(leaving_state == HREF);
106
SVN_ERR_ASSERT(cdata != NULL);
108
opt_ctx->activity_collection = svn_urlpath__canonicalize(cdata->data,
213
115
static svn_error_t *
214
116
create_options_body(serf_bucket_t **body_bkt,
229
131
return SVN_NO_ERROR;
233
svn_ra_serf__get_options_done_ptr(svn_ra_serf__options_context_t *ctx)
239
svn_ra_serf__options_get_activity_collection(svn_ra_serf__options_context_t *ctx)
241
return ctx->activity_collection;
245
svn_ra_serf__options_get_youngest_rev(svn_ra_serf__options_context_t *ctx)
247
return ctx->youngest_rev;
250
/* Context for both options_response_handler() and capabilities callback. */
251
struct options_response_ctx_t {
252
/* Baton for __handle_xml_parser() */
253
svn_ra_serf__xml_parser_t *parser_ctx;
255
/* Session into which we'll store server capabilities */
256
svn_ra_serf__session_t *session;
258
/* For temporary work only. */
263
135
/* We use these static pointers so we can employ pointer comparison
264
136
* of our capabilities hash members instead of strcmp()ing all over
267
139
/* Both server and repository support the capability. */
268
static const char *capability_yes = "yes";
140
static const char *const capability_yes = "yes";
269
141
/* Either server or repository does not support the capability. */
270
static const char *capability_no = "no";
142
static const char *const capability_no = "no";
271
143
/* Server supports the capability, but don't yet know if repository does. */
272
static const char *capability_server_yes = "server-yes";
144
static const char *const capability_server_yes = "server-yes";
275
147
/* This implements serf_bucket_headers_do_callback_fn_t.
297
171
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_DEPTH, vals))
299
apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_DEPTH,
300
APR_HASH_KEY_STRING, capability_yes);
173
svn_hash_sets(session->capabilities,
174
SVN_RA_CAPABILITY_DEPTH, capability_yes);
302
176
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_MERGEINFO, vals))
304
178
/* The server doesn't know what repository we're referring
305
179
to, so it can't just say capability_yes. */
306
apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
307
APR_HASH_KEY_STRING, capability_server_yes);
180
if (!svn_hash_gets(session->capabilities,
181
SVN_RA_CAPABILITY_MERGEINFO))
183
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
184
capability_server_yes);
309
187
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_LOG_REVPROPS, vals))
311
apr_hash_set(orc->session->capabilities,
312
SVN_RA_CAPABILITY_LOG_REVPROPS,
313
APR_HASH_KEY_STRING, capability_yes);
189
svn_hash_sets(session->capabilities,
190
SVN_RA_CAPABILITY_LOG_REVPROPS, capability_yes);
315
192
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS, vals))
317
apr_hash_set(orc->session->capabilities,
318
SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
319
APR_HASH_KEY_STRING, capability_yes);
194
svn_hash_sets(session->capabilities,
195
SVN_RA_CAPABILITY_ATOMIC_REVPROPS, capability_yes);
321
197
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY, vals))
323
apr_hash_set(orc->session->capabilities,
324
SVN_RA_CAPABILITY_PARTIAL_REPLAY,
325
APR_HASH_KEY_STRING, capability_yes);
199
svn_hash_sets(session->capabilities,
200
SVN_RA_CAPABILITY_PARTIAL_REPLAY, capability_yes);
202
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INHERITED_PROPS, vals))
204
svn_hash_sets(session->capabilities,
205
SVN_RA_CAPABILITY_INHERITED_PROPS, capability_yes);
207
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REVERSE_FILE_REVS,
210
svn_hash_sets(session->capabilities,
211
SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
214
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_EPHEMERAL_TXNPROPS, vals))
216
svn_hash_sets(session->capabilities,
217
SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS, capability_yes);
219
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INLINE_PROPS, vals))
221
session->supports_inline_props = TRUE;
223
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REPLAY_REV_RESOURCE, vals))
225
session->supports_rev_rsrc_replay = TRUE;
329
229
/* SVN-specific headers -- if present, server supports HTTP protocol v2 */
330
230
else if (strncmp(key, "SVN", 3) == 0)
232
/* If we've not yet seen any information about supported POST
233
requests, we'll initialize the list/hash with "create-txn"
234
(which we know is supported by virtue of the server speaking
236
if (! session->supported_posts)
238
session->supported_posts = apr_hash_make(session->pool);
239
apr_hash_set(session->supported_posts, "create-txn", 10, (void *)1);
332
242
if (svn_cstring_casecmp(key, SVN_DAV_ROOT_URI_HEADER) == 0)
334
orc->session->repos_root = orc->session->session_url;
335
orc->session->repos_root.path =
336
(char *)svn_fspath__canonicalize(val, orc->session->pool);
337
orc->session->repos_root_str =
244
session->repos_root = session->session_url;
245
session->repos_root.path =
246
(char *)svn_fspath__canonicalize(val, session->pool);
247
session->repos_root_str =
338
248
svn_urlpath__canonicalize(
339
apr_uri_unparse(orc->session->pool,
340
&orc->session->repos_root,
249
apr_uri_unparse(session->pool, &session->repos_root, 0),
344
252
else if (svn_cstring_casecmp(key, SVN_DAV_ME_RESOURCE_HEADER) == 0)
349
257
if (!(ignore_v2_env_var
350
258
&& apr_strnatcasecmp(ignore_v2_env_var, "yes") == 0))
351
orc->session->me_resource = apr_pstrdup(orc->session->pool, val);
259
session->me_resource = apr_pstrdup(session->pool, val);
353
orc->session->me_resource = apr_pstrdup(orc->session->pool, val);
261
session->me_resource = apr_pstrdup(session->pool, val);
356
264
else if (svn_cstring_casecmp(key, SVN_DAV_REV_STUB_HEADER) == 0)
358
orc->session->rev_stub = apr_pstrdup(orc->session->pool, val);
266
session->rev_stub = apr_pstrdup(session->pool, val);
360
268
else if (svn_cstring_casecmp(key, SVN_DAV_REV_ROOT_STUB_HEADER) == 0)
362
orc->session->rev_root_stub = apr_pstrdup(orc->session->pool, val);
270
session->rev_root_stub = apr_pstrdup(session->pool, val);
364
272
else if (svn_cstring_casecmp(key, SVN_DAV_TXN_STUB_HEADER) == 0)
366
orc->session->txn_stub = apr_pstrdup(orc->session->pool, val);
274
session->txn_stub = apr_pstrdup(session->pool, val);
368
276
else if (svn_cstring_casecmp(key, SVN_DAV_TXN_ROOT_STUB_HEADER) == 0)
370
orc->session->txn_root_stub = apr_pstrdup(orc->session->pool, val);
278
session->txn_root_stub = apr_pstrdup(session->pool, val);
372
280
else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_STUB_HEADER) == 0)
374
orc->session->vtxn_stub = apr_pstrdup(orc->session->pool, val);
282
session->vtxn_stub = apr_pstrdup(session->pool, val);
376
284
else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_ROOT_STUB_HEADER) == 0)
378
orc->session->vtxn_root_stub = apr_pstrdup(orc->session->pool, val);
286
session->vtxn_root_stub = apr_pstrdup(session->pool, val);
380
288
else if (svn_cstring_casecmp(key, SVN_DAV_REPOS_UUID_HEADER) == 0)
382
orc->session->uuid = apr_pstrdup(orc->session->pool, val);
290
session->uuid = apr_pstrdup(session->pool, val);
384
292
else if (svn_cstring_casecmp(key, SVN_DAV_YOUNGEST_REV_HEADER) == 0)
386
struct svn_ra_serf__options_context_t *user_data =
387
orc->parser_ctx->user_data;
388
user_data->youngest_rev = SVN_STR_TO_REV(val);
294
opt_ctx->youngest_rev = SVN_STR_TO_REV(val);
296
else if (svn_cstring_casecmp(key, SVN_DAV_ALLOW_BULK_UPDATES) == 0)
298
session->server_allows_bulk = apr_pstrdup(session->pool, val);
300
else if (svn_cstring_casecmp(key, SVN_DAV_SUPPORTED_POSTS_HEADER) == 0)
302
/* May contain multiple values, separated by commas. */
304
apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE,
307
for (i = 0; i < vals->nelts; i++)
309
const char *post_val = APR_ARRAY_IDX(vals, i, const char *);
311
svn_hash_sets(session->supported_posts, post_val, (void *)1);
314
else if (svn_cstring_casecmp(key, SVN_DAV_REPOSITORY_MERGEINFO) == 0)
316
if (svn_cstring_casecmp(val, "yes") == 0)
318
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
321
else if (svn_cstring_casecmp(val, "no") == 0)
323
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
404
341
apr_pool_t *pool)
406
struct options_response_ctx_t *orc = baton;
407
serf_bucket_t *hdrs = serf_bucket_response_get_headers(response);
409
/* Start out assuming all capabilities are unsupported. */
410
apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY,
411
APR_HASH_KEY_STRING, capability_no);
412
apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_DEPTH,
413
APR_HASH_KEY_STRING, capability_no);
414
apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
415
APR_HASH_KEY_STRING, capability_no);
416
apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
417
APR_HASH_KEY_STRING, capability_no);
418
apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
419
APR_HASH_KEY_STRING, capability_no);
421
/* Then see which ones we can discover. */
422
serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback, orc);
424
/* Execute the 'real' response handler to XML-parse the repsonse body. */
425
return svn_ra_serf__handle_xml_parser(request, response,
426
orc->parser_ctx, pool);
343
options_context_t *opt_ctx = baton;
345
if (!opt_ctx->headers_processed)
347
svn_ra_serf__session_t *session = opt_ctx->session;
348
serf_bucket_t *hdrs = serf_bucket_response_get_headers(response);
350
/* Start out assuming all capabilities are unsupported. */
351
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY,
353
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_DEPTH,
355
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
357
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
359
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
361
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_INHERITED_PROPS,
363
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS,
365
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
368
/* Then see which ones we can discover. */
369
serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback,
372
/* Assume mergeinfo capability unsupported, if didn't recieve information
373
about server or repository mergeinfo capability. */
374
if (!svn_hash_gets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO))
375
svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
378
opt_ctx->headers_processed = TRUE;
381
/* Execute the 'real' response handler to XML-parse the response body. */
382
return opt_ctx->inner_handler(request, response, opt_ctx->inner_baton, pool);
431
svn_ra_serf__create_options_req(svn_ra_serf__options_context_t **opt_ctx,
432
svn_ra_serf__session_t *session,
433
svn_ra_serf__connection_t *conn,
387
create_options_req(options_context_t **opt_ctx,
388
svn_ra_serf__session_t *session,
389
svn_ra_serf__connection_t *conn,
437
svn_ra_serf__options_context_t *new_ctx;
392
options_context_t *new_ctx;
393
svn_ra_serf__xml_context_t *xmlctx;
438
394
svn_ra_serf__handler_t *handler;
439
svn_ra_serf__xml_parser_t *parser_ctx;
440
struct options_response_ctx_t *options_response_ctx;
442
396
new_ctx = apr_pcalloc(pool, sizeof(*new_ctx));
444
397
new_ctx->pool = pool;
446
new_ctx->path = path;
447
new_ctx->youngest_rev = SVN_INVALID_REVNUM;
449
398
new_ctx->session = session;
450
399
new_ctx->conn = conn;
452
handler = apr_pcalloc(pool, sizeof(*handler));
401
new_ctx->youngest_rev = SVN_INVALID_REVNUM;
403
xmlctx = svn_ra_serf__xml_context_create(options_ttable,
404
NULL, options_closed, NULL,
407
handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
454
409
handler->method = "OPTIONS";
455
handler->path = path;
410
handler->path = session->session_url.path;
456
411
handler->body_delegate = create_options_body;
457
412
handler->body_type = "text/xml";
458
413
handler->conn = conn;
459
414
handler->session = session;
461
parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
463
parser_ctx->pool = pool;
464
parser_ctx->user_data = new_ctx;
465
parser_ctx->start = start_options;
466
parser_ctx->end = end_options;
467
parser_ctx->cdata = cdata_options;
468
parser_ctx->done = &new_ctx->done;
469
parser_ctx->status_code = &new_ctx->status_code;
471
options_response_ctx = apr_pcalloc(pool, sizeof(*options_response_ctx));
472
options_response_ctx->parser_ctx = parser_ctx;
473
options_response_ctx->session = session;
474
options_response_ctx->pool = pool;
416
new_ctx->handler = handler;
418
new_ctx->inner_handler = handler->response_handler;
419
new_ctx->inner_baton = handler->response_baton;
476
420
handler->response_handler = options_response_handler;
477
handler->response_baton = options_response_ctx;
479
svn_ra_serf__request_create(handler);
481
new_ctx->parser_ctx = parser_ctx;
421
handler->response_baton = new_ctx;
483
423
*opt_ctx = new_ctx;
430
svn_ra_serf__v2_get_youngest_revnum(svn_revnum_t *youngest,
431
svn_ra_serf__connection_t *conn,
432
apr_pool_t *scratch_pool)
434
svn_ra_serf__session_t *session = conn->session;
435
options_context_t *opt_ctx;
437
SVN_ERR_ASSERT(SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session));
439
SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool));
440
SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool));
441
SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline,
442
opt_ctx->handler->path,
443
opt_ctx->handler->location));
445
*youngest = opt_ctx->youngest_rev;
446
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(*youngest));
453
svn_ra_serf__v1_get_activity_collection(const char **activity_url,
454
svn_ra_serf__connection_t *conn,
455
apr_pool_t *result_pool,
456
apr_pool_t *scratch_pool)
458
svn_ra_serf__session_t *session = conn->session;
459
options_context_t *opt_ctx;
461
SVN_ERR_ASSERT(!SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session));
463
SVN_ERR(create_options_req(&opt_ctx, session, conn, scratch_pool));
464
SVN_ERR(svn_ra_serf__context_run_one(opt_ctx->handler, scratch_pool));
466
SVN_ERR(svn_ra_serf__error_on_status(opt_ctx->handler->sline,
467
opt_ctx->handler->path,
468
opt_ctx->handler->location));
470
*activity_url = apr_pstrdup(result_pool, opt_ctx->activity_collection);
490
478
/** Capabilities exchange. */
494
482
const char **corrected_url,
495
483
apr_pool_t *pool)
497
svn_ra_serf__options_context_t *opt_ctx;
485
options_context_t *opt_ctx;
498
486
svn_error_t *err;
500
488
/* This routine automatically fills in serf_sess->capabilities */
501
SVN_ERR(svn_ra_serf__create_options_req(&opt_ctx, serf_sess,
503
serf_sess->session_url.path, pool));
489
SVN_ERR(create_options_req(&opt_ctx, serf_sess, serf_sess->conns[0], pool));
505
err = svn_ra_serf__context_run_wait(
506
svn_ra_serf__get_options_done_ptr(opt_ctx), serf_sess, pool);
491
err = svn_ra_serf__context_run_one(opt_ctx->handler, pool);
508
493
/* If our caller cares about server redirections, and our response
509
494
carries such a thing, report as much. We'll disregard ERR --
510
495
it's most likely just a complaint about the response body not
511
496
successfully parsing as XML or somesuch. */
512
if (corrected_url && (opt_ctx->status_code == 301))
497
if (corrected_url && (opt_ctx->handler->sline.code == 301))
514
499
svn_error_clear(err);
515
*corrected_url = opt_ctx->parser_ctx->location;
500
*corrected_url = opt_ctx->handler->location;
516
501
return SVN_NO_ERROR;
519
504
SVN_ERR(svn_error_compose_create(
520
svn_ra_serf__error_on_status(opt_ctx->status_code,
505
svn_ra_serf__error_on_status(opt_ctx->handler->sline,
521
506
serf_sess->session_url.path,
522
opt_ctx->parser_ctx->location),
507
opt_ctx->handler->location),
525
510
/* Opportunistically cache any reported activity URL. (We don't
524
create_simple_options_body(serf_bucket_t **body_bkt,
526
serf_bucket_alloc_t *alloc,
532
body = serf_bucket_aggregate_create(alloc);
533
svn_ra_serf__add_xml_header_buckets(body, alloc);
535
s = SERF_BUCKET_SIMPLE_STRING("<D:options xmlns:D=\"DAV:\" />", alloc);
536
serf_bucket_aggregate_append(body, s);
544
svn_ra_serf__probe_proxy(svn_ra_serf__session_t *serf_sess,
545
apr_pool_t *scratch_pool)
547
svn_ra_serf__handler_t *handler;
549
handler = apr_pcalloc(scratch_pool, sizeof(*handler));
550
handler->handler_pool = scratch_pool;
551
handler->method = "OPTIONS";
552
handler->path = serf_sess->session_url.path;
553
handler->conn = serf_sess->conns[0];
554
handler->session = serf_sess;
556
/* We don't care about the response body, so discard it. */
557
handler->response_handler = svn_ra_serf__handle_discard_body;
559
/* We need a simple body, in order to send it in chunked format. */
560
handler->body_delegate = create_simple_options_body;
562
/* No special headers. */
564
SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool));
565
/* Some versions of nginx in reverse proxy mode will return 411. They want
566
a Content-Length header, rather than chunked requests. We can keep other
567
HTTP/1.1 features, but will disable the chunking. */
568
if (handler->sline.code == 411)
570
serf_sess->using_chunked_requests = FALSE;
574
SVN_ERR(svn_ra_serf__error_on_status(handler->sline,
539
583
svn_ra_serf__has_capability(svn_ra_session_t *ra_session,
540
584
svn_boolean_t *has,
551
595
return SVN_NO_ERROR;
554
cap_result = apr_hash_get(serf_sess->capabilities,
556
APR_HASH_KEY_STRING);
598
cap_result = svn_hash_gets(serf_sess->capabilities, capability);
558
600
/* If any capability is unknown, they're all unknown, so ask. */
559
601
if (cap_result == NULL)
560
602
SVN_ERR(svn_ra_serf__exchange_capabilities(serf_sess, NULL, pool));
562
604
/* Try again, now that we've fetched the capabilities. */
563
cap_result = apr_hash_get(serf_sess->capabilities,
564
capability, APR_HASH_KEY_STRING);
605
cap_result = svn_hash_gets(serf_sess->capabilities, capability);
566
/* Some capabilities depend on the repository as well as the server.
567
NOTE: svn_ra_neon__has_capability() has a very similar code block. If
568
you change something here, check there as well. */
607
/* Some capabilities depend on the repository as well as the server. */
569
608
if (cap_result == capability_server_yes)
571
610
if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0)