util.c (269833) | util.c (289180) |
---|---|
1/* 2 * util.c : serf utility routines for ra_serf 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file --- 18 unchanged lines hidden (view full) --- 27 28#define APR_WANT_STRFUNC 29#include <apr.h> 30#include <apr_want.h> 31 32#include <serf.h> 33#include <serf_bucket_types.h> 34 | 1/* 2 * util.c : serf utility routines for ra_serf 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file --- 18 unchanged lines hidden (view full) --- 27 28#define APR_WANT_STRFUNC 29#include <apr.h> 30#include <apr_want.h> 31 32#include <serf.h> 33#include <serf_bucket_types.h> 34 |
35#include <expat.h> 36 | |
37#include "svn_hash.h" 38#include "svn_dirent_uri.h" 39#include "svn_path.h" 40#include "svn_private_config.h" 41#include "svn_string.h" | 35#include "svn_hash.h" 36#include "svn_dirent_uri.h" 37#include "svn_path.h" 38#include "svn_private_config.h" 39#include "svn_string.h" |
42#include "svn_xml.h" | |
43#include "svn_props.h" 44#include "svn_dirent_uri.h" 45 46#include "../libsvn_ra/ra_loader.h" 47#include "private/svn_dep_compat.h" 48#include "private/svn_fspath.h" | 40#include "svn_props.h" 41#include "svn_dirent_uri.h" 42 43#include "../libsvn_ra/ra_loader.h" 44#include "private/svn_dep_compat.h" 45#include "private/svn_fspath.h" |
49#include "private/svn_subr_private.h" | |
50#include "private/svn_auth_private.h" 51#include "private/svn_cert.h" 52 53#include "ra_serf.h" 54 | 46#include "private/svn_auth_private.h" 47#include "private/svn_cert.h" 48 49#include "ra_serf.h" 50 |
55 56/* Fix for older expat 1.95.x's that do not define 57 * XML_STATUS_OK/XML_STATUS_ERROR 58 */ 59#ifndef XML_STATUS_OK 60#define XML_STATUS_OK 1 61#define XML_STATUS_ERROR 0 62#endif 63 64#ifndef XML_VERSION_AT_LEAST 65#define XML_VERSION_AT_LEAST(major,minor,patch) \ 66(((major) < XML_MAJOR_VERSION) \ 67 || ((major) == XML_MAJOR_VERSION && (minor) < XML_MINOR_VERSION) \ 68 || ((major) == XML_MAJOR_VERSION && (minor) == XML_MINOR_VERSION && \ 69 (patch) <= XML_MICRO_VERSION)) 70#endif /* APR_VERSION_AT_LEAST */ 71 72#if XML_VERSION_AT_LEAST(1, 95, 8) 73#define EXPAT_HAS_STOPPARSER 74#endif 75 76/* Read/write chunks of this size into the spillbuf. */ 77#define PARSE_CHUNK_SIZE 8000 78 79/* We will store one megabyte in memory, before switching to store content 80 into a temporary file. */ 81#define SPILL_SIZE 1000000 82 83 84/* This structure records pending data for the parser in memory blocks, 85 and possibly into a temporary file if "too much" content arrives. */ 86struct svn_ra_serf__pending_t { 87 /* The spillbuf where we record the pending data. */ 88 svn_spillbuf_t *buf; 89 90 /* This flag is set when the network has reached EOF. The PENDING 91 processing can then properly detect when parsing has completed. */ 92 svn_boolean_t network_eof; 93}; 94 95#define HAS_PENDING_DATA(p) ((p) != NULL && (p)->buf != NULL \ 96 && svn_spillbuf__get_size((p)->buf) != 0) 97 98 99struct expat_ctx_t { 100 svn_ra_serf__xml_context_t *xmlctx; 101 XML_Parser parser; 102 svn_ra_serf__handler_t *handler; 103 104 svn_error_t *inner_error; 105 106 /* Do not use this pool for allocation. It is merely recorded for running 107 the cleanup handler. */ 108 apr_pool_t *cleanup_pool; 109}; 110 111 | |
112static const apr_uint32_t serf_failure_map[][2] = 113{ 114 { SERF_SSL_CERT_NOTYETVALID, SVN_AUTH_SSL_NOTYETVALID }, 115 { SERF_SSL_CERT_EXPIRED, SVN_AUTH_SSL_EXPIRED }, 116 { SERF_SSL_CERT_SELF_SIGNED, SVN_AUTH_SSL_UNKNOWNCA }, 117 { SERF_SSL_CERT_UNKNOWNCA, SVN_AUTH_SSL_UNKNOWNCA } 118}; 119 --- 67 unchanged lines hidden (view full) --- 187} 188 189/* Convert a hash table containing the fields (as documented in X.509) of an 190 organisation to a string ORG, allocated in POOL. ORG is as returned by 191 serf_ssl_cert_issuer() and serf_ssl_cert_subject(). */ 192static char * 193convert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool) 194{ | 51static const apr_uint32_t serf_failure_map[][2] = 52{ 53 { SERF_SSL_CERT_NOTYETVALID, SVN_AUTH_SSL_NOTYETVALID }, 54 { SERF_SSL_CERT_EXPIRED, SVN_AUTH_SSL_EXPIRED }, 55 { SERF_SSL_CERT_SELF_SIGNED, SVN_AUTH_SSL_UNKNOWNCA }, 56 { SERF_SSL_CERT_UNKNOWNCA, SVN_AUTH_SSL_UNKNOWNCA } 57}; 58 --- 67 unchanged lines hidden (view full) --- 126} 127 128/* Convert a hash table containing the fields (as documented in X.509) of an 129 organisation to a string ORG, allocated in POOL. ORG is as returned by 130 serf_ssl_cert_issuer() and serf_ssl_cert_subject(). */ 131static char * 132convert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool) 133{ |
134 const char *cn = svn_hash_gets(org, "CN"); |
|
195 const char *org_unit = svn_hash_gets(org, "OU"); 196 const char *org_name = svn_hash_gets(org, "O"); 197 const char *locality = svn_hash_gets(org, "L"); 198 const char *state = svn_hash_gets(org, "ST"); 199 const char *country = svn_hash_gets(org, "C"); 200 const char *email = svn_hash_gets(org, "E"); 201 svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool); 202 | 135 const char *org_unit = svn_hash_gets(org, "OU"); 136 const char *org_name = svn_hash_gets(org, "O"); 137 const char *locality = svn_hash_gets(org, "L"); 138 const char *state = svn_hash_gets(org, "ST"); 139 const char *country = svn_hash_gets(org, "C"); 140 const char *email = svn_hash_gets(org, "E"); 141 svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool); 142 |
143 if (cn) 144 { 145 svn_stringbuf_appendcstr(buf, cn); 146 svn_stringbuf_appendcstr(buf, ", "); 147 } 148 |
|
203 if (org_unit) 204 { 205 svn_stringbuf_appendcstr(buf, org_unit); 206 svn_stringbuf_appendcstr(buf, ", "); 207 } 208 209 if (org_name) 210 { --- 69 unchanged lines hidden (view full) --- 280 281 if (serf_ssl_cert_depth(cert) == 0) 282 { 283 /* If the depth is 0, the hostname must match the certificate. 284 285 ### This should really be handled by serf, which should pass an error 286 for this case, but that has backwards compatibility issues. */ 287 apr_array_header_t *san; | 149 if (org_unit) 150 { 151 svn_stringbuf_appendcstr(buf, org_unit); 152 svn_stringbuf_appendcstr(buf, ", "); 153 } 154 155 if (org_name) 156 { --- 69 unchanged lines hidden (view full) --- 226 227 if (serf_ssl_cert_depth(cert) == 0) 228 { 229 /* If the depth is 0, the hostname must match the certificate. 230 231 ### This should really be handled by serf, which should pass an error 232 for this case, but that has backwards compatibility issues. */ 233 apr_array_header_t *san; |
288 svn_boolean_t found_san_entry = FALSE; | |
289 svn_boolean_t found_matching_hostname = FALSE; 290 svn_string_t *actual_hostname = 291 svn_string_create(conn->session->session_url.hostname, scratch_pool); 292 293 serf_cert = serf_ssl_cert_certificate(cert, scratch_pool); 294 295 san = svn_hash_gets(serf_cert, "subjectAltName"); | 234 svn_boolean_t found_matching_hostname = FALSE; 235 svn_string_t *actual_hostname = 236 svn_string_create(conn->session->session_url.hostname, scratch_pool); 237 238 serf_cert = serf_ssl_cert_certificate(cert, scratch_pool); 239 240 san = svn_hash_gets(serf_cert, "subjectAltName"); |
296 /* Try to find matching server name via subjectAltName first... */ 297 if (san) | 241 /* Match server certificate CN with the hostname of the server iff 242 * we didn't find any subjectAltName fields and try to match them. 243 * Per RFC 2818 they are authoritative if present and CommonName 244 * should be ignored. NOTE: This isn't 100% correct since serf 245 * only loads the subjectAltName hash with dNSNames, technically 246 * we should ignore the CommonName if any subjectAltName entry 247 * exists even if it is one we don't support. */ 248 if (san && san->nelts > 0) |
298 { 299 int i; | 249 { 250 int i; |
300 found_san_entry = san->nelts > 0; | |
301 for (i = 0; i < san->nelts; i++) 302 { 303 const char *s = APR_ARRAY_IDX(san, i, const char*); 304 svn_string_t *cert_hostname = svn_string_create(s, scratch_pool); 305 306 if (svn_cert__match_dns_identity(cert_hostname, actual_hostname)) 307 { 308 found_matching_hostname = TRUE; 309 break; 310 } 311 } 312 } | 251 for (i = 0; i < san->nelts; i++) 252 { 253 const char *s = APR_ARRAY_IDX(san, i, const char*); 254 svn_string_t *cert_hostname = svn_string_create(s, scratch_pool); 255 256 if (svn_cert__match_dns_identity(cert_hostname, actual_hostname)) 257 { 258 found_matching_hostname = TRUE; 259 break; 260 } 261 } 262 } |
313 314 /* Match server certificate CN with the hostname of the server iff 315 * we didn't find any subjectAltName fields and try to match them. 316 * Per RFC 2818 they are authoritative if present and CommonName 317 * should be ignored. */ 318 if (!found_matching_hostname && !found_san_entry) | 263 else |
319 { 320 const char *hostname = NULL; 321 322 subject = serf_ssl_cert_subject(cert, scratch_pool); 323 324 if (subject) 325 hostname = svn_hash_gets(subject, "CN"); 326 --- 36 unchanged lines hidden (view full) --- 363 cert_info.issuer_dname = convert_organisation_to_str(issuer, scratch_pool); 364 cert_info.ascii_cert = serf_ssl_cert_export(cert, scratch_pool); 365 366 /* Handle any non-server certs. */ 367 if (serf_ssl_cert_depth(cert) > 0) 368 { 369 svn_error_t *err; 370 | 264 { 265 const char *hostname = NULL; 266 267 subject = serf_ssl_cert_subject(cert, scratch_pool); 268 269 if (subject) 270 hostname = svn_hash_gets(subject, "CN"); 271 --- 36 unchanged lines hidden (view full) --- 308 cert_info.issuer_dname = convert_organisation_to_str(issuer, scratch_pool); 309 cert_info.ascii_cert = serf_ssl_cert_export(cert, scratch_pool); 310 311 /* Handle any non-server certs. */ 312 if (serf_ssl_cert_depth(cert) > 0) 313 { 314 svn_error_t *err; 315 |
371 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, | 316 svn_auth_set_parameter(conn->session->auth_baton, |
372 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, 373 &cert_info); 374 | 317 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, 318 &cert_info); 319 |
375 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, | 320 svn_auth_set_parameter(conn->session->auth_baton, |
376 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, 377 &svn_failures); 378 379 realmstring = apr_psprintf(scratch_pool, "AUTHORITY:%s", 380 cert_info.fingerprint); 381 382 err = svn_auth_first_credentials(&creds, &state, 383 SVN_AUTH_CRED_SSL_SERVER_AUTHORITY, 384 realmstring, | 321 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, 322 &svn_failures); 323 324 realmstring = apr_psprintf(scratch_pool, "AUTHORITY:%s", 325 cert_info.fingerprint); 326 327 err = svn_auth_first_credentials(&creds, &state, 328 SVN_AUTH_CRED_SSL_SERVER_AUTHORITY, 329 realmstring, |
385 conn->session->wc_callbacks->auth_baton, | 330 conn->session->auth_baton, |
386 scratch_pool); 387 | 331 scratch_pool); 332 |
388 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, | 333 svn_auth_set_parameter(conn->session->auth_baton, |
389 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); 390 | 334 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); 335 |
391 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, | 336 svn_auth_set_parameter(conn->session->auth_baton, |
392 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, NULL); 393 394 if (err) 395 { 396 if (err->apr_err != SVN_ERR_AUTHN_NO_PROVIDER) 397 return svn_error_trace(err); 398 399 /* No provider registered that handles server authorities */ --- 10 unchanged lines hidden (view full) --- 410 } 411 412 if (svn_failures) 413 conn->server_cert_failures |= svn_failures; 414 415 return APR_SUCCESS; 416 } 417 | 337 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, NULL); 338 339 if (err) 340 { 341 if (err->apr_err != SVN_ERR_AUTHN_NO_PROVIDER) 342 return svn_error_trace(err); 343 344 /* No provider registered that handles server authorities */ --- 10 unchanged lines hidden (view full) --- 355 } 356 357 if (svn_failures) 358 conn->server_cert_failures |= svn_failures; 359 360 return APR_SUCCESS; 361 } 362 |
418 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, | 363 svn_auth_set_parameter(conn->session->auth_baton, |
419 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, 420 &svn_failures); 421 | 364 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, 365 &svn_failures); 366 |
422 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, | 367 svn_auth_set_parameter(conn->session->auth_baton, |
423 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, 424 &cert_info); 425 426 realmstring = construct_realm(conn->session, conn->session->pool); 427 428 SVN_ERR(svn_auth_first_credentials(&creds, &state, 429 SVN_AUTH_CRED_SSL_SERVER_TRUST, 430 realmstring, | 368 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, 369 &cert_info); 370 371 realmstring = construct_realm(conn->session, conn->session->pool); 372 373 SVN_ERR(svn_auth_first_credentials(&creds, &state, 374 SVN_AUTH_CRED_SSL_SERVER_TRUST, 375 realmstring, |
431 conn->session->wc_callbacks->auth_baton, | 376 conn->session->auth_baton, |
432 scratch_pool)); 433 if (creds) 434 { 435 server_creds = creds; 436 svn_failures &= ~server_creds->accepted_failures; 437 SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 438 } 439 --- 4 unchanged lines hidden (view full) --- 444 if (creds) 445 { 446 server_creds = creds; 447 svn_failures &= ~server_creds->accepted_failures; 448 SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 449 } 450 } 451 | 377 scratch_pool)); 378 if (creds) 379 { 380 server_creds = creds; 381 svn_failures &= ~server_creds->accepted_failures; 382 SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 383 } 384 --- 4 unchanged lines hidden (view full) --- 389 if (creds) 390 { 391 server_creds = creds; 392 svn_failures &= ~server_creds->accepted_failures; 393 SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 394 } 395 } 396 |
452 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, | 397 svn_auth_set_parameter(conn->session->auth_baton, |
453 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); 454 455 /* Are there non accepted failures left? */ 456 if (svn_failures) 457 { 458 svn_stringbuf_t *errmsg; 459 int reasons = 0; 460 --- 156 unchanged lines hidden (view full) --- 617 618/* Our default serf response acceptor. */ 619static serf_bucket_t * 620accept_response(serf_request_t *request, 621 serf_bucket_t *stream, 622 void *acceptor_baton, 623 apr_pool_t *pool) 624{ | 398 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); 399 400 /* Are there non accepted failures left? */ 401 if (svn_failures) 402 { 403 svn_stringbuf_t *errmsg; 404 int reasons = 0; 405 --- 156 unchanged lines hidden (view full) --- 562 563/* Our default serf response acceptor. */ 564static serf_bucket_t * 565accept_response(serf_request_t *request, 566 serf_bucket_t *stream, 567 void *acceptor_baton, 568 apr_pool_t *pool) 569{ |
570 /* svn_ra_serf__handler_t *handler = acceptor_baton; */ |
|
625 serf_bucket_t *c; 626 serf_bucket_alloc_t *bkt_alloc; 627 628 bkt_alloc = serf_request_get_alloc(request); 629 c = serf_bucket_barrier_create(stream, bkt_alloc); 630 631 return serf_bucket_response_create(c, bkt_alloc); 632} 633 634 635/* Custom response acceptor for HEAD requests. */ 636static serf_bucket_t * 637accept_head(serf_request_t *request, 638 serf_bucket_t *stream, 639 void *acceptor_baton, 640 apr_pool_t *pool) 641{ | 571 serf_bucket_t *c; 572 serf_bucket_alloc_t *bkt_alloc; 573 574 bkt_alloc = serf_request_get_alloc(request); 575 c = serf_bucket_barrier_create(stream, bkt_alloc); 576 577 return serf_bucket_response_create(c, bkt_alloc); 578} 579 580 581/* Custom response acceptor for HEAD requests. */ 582static serf_bucket_t * 583accept_head(serf_request_t *request, 584 serf_bucket_t *stream, 585 void *acceptor_baton, 586 apr_pool_t *pool) 587{ |
588 /* svn_ra_serf__handler_t *handler = acceptor_baton; */ |
|
642 serf_bucket_t *response; 643 644 response = accept_response(request, stream, acceptor_baton, pool); 645 646 /* We know we shouldn't get a response body. */ 647 serf_bucket_response_set_head(response); 648 649 return response; 650} 651 652static svn_error_t * 653connection_closed(svn_ra_serf__connection_t *conn, 654 apr_status_t why, 655 apr_pool_t *pool) 656{ 657 if (why) 658 { | 589 serf_bucket_t *response; 590 591 response = accept_response(request, stream, acceptor_baton, pool); 592 593 /* We know we shouldn't get a response body. */ 594 serf_bucket_response_set_head(response); 595 596 return response; 597} 598 599static svn_error_t * 600connection_closed(svn_ra_serf__connection_t *conn, 601 apr_status_t why, 602 apr_pool_t *pool) 603{ 604 if (why) 605 { |
659 return svn_error_wrap_apr(why, NULL); | 606 return svn_ra_serf__wrap_err(why, NULL); |
660 } 661 662 if (conn->session->using_ssl) 663 conn->ssl_context = NULL; 664 665 return SVN_NO_ERROR; 666} 667 --- 28 unchanged lines hidden (view full) --- 696 realm = construct_realm(session, session->pool); 697 698 if (!conn->ssl_client_auth_state) 699 { 700 SVN_ERR(svn_auth_first_credentials(&creds, 701 &conn->ssl_client_auth_state, 702 SVN_AUTH_CRED_SSL_CLIENT_CERT, 703 realm, | 607 } 608 609 if (conn->session->using_ssl) 610 conn->ssl_context = NULL; 611 612 return SVN_NO_ERROR; 613} 614 --- 28 unchanged lines hidden (view full) --- 643 realm = construct_realm(session, session->pool); 644 645 if (!conn->ssl_client_auth_state) 646 { 647 SVN_ERR(svn_auth_first_credentials(&creds, 648 &conn->ssl_client_auth_state, 649 SVN_AUTH_CRED_SSL_CLIENT_CERT, 650 realm, |
704 session->wc_callbacks->auth_baton, | 651 session->auth_baton, |
705 pool)); 706 } 707 else 708 { 709 SVN_ERR(svn_auth_next_credentials(&creds, 710 conn->ssl_client_auth_state, 711 session->pool)); 712 } --- 35 unchanged lines hidden (view full) --- 748 *password = NULL; 749 750 if (!conn->ssl_client_pw_auth_state) 751 { 752 SVN_ERR(svn_auth_first_credentials(&creds, 753 &conn->ssl_client_pw_auth_state, 754 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 755 cert_path, | 652 pool)); 653 } 654 else 655 { 656 SVN_ERR(svn_auth_next_credentials(&creds, 657 conn->ssl_client_auth_state, 658 session->pool)); 659 } --- 35 unchanged lines hidden (view full) --- 695 *password = NULL; 696 697 if (!conn->ssl_client_pw_auth_state) 698 { 699 SVN_ERR(svn_auth_first_credentials(&creds, 700 &conn->ssl_client_pw_auth_state, 701 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 702 cert_path, |
756 session->wc_callbacks->auth_baton, | 703 session->auth_baton, |
757 pool)); 758 } 759 else 760 { 761 SVN_ERR(svn_auth_next_credentials(&creds, 762 conn->ssl_client_pw_auth_state, 763 pool)); 764 } --- 34 unchanged lines hidden (view full) --- 799 * corresponds to the new request. 800 * 801 * The request will be METHOD at URL. 802 * 803 * If BODY_BKT is not-NULL, it will be sent as the request body. 804 * 805 * If CONTENT_TYPE is not-NULL, it will be sent as the Content-Type header. 806 * | 704 pool)); 705 } 706 else 707 { 708 SVN_ERR(svn_auth_next_credentials(&creds, 709 conn->ssl_client_pw_auth_state, 710 pool)); 711 } --- 34 unchanged lines hidden (view full) --- 746 * corresponds to the new request. 747 * 748 * The request will be METHOD at URL. 749 * 750 * If BODY_BKT is not-NULL, it will be sent as the request body. 751 * 752 * If CONTENT_TYPE is not-NULL, it will be sent as the Content-Type header. 753 * |
754 * If DAV_HEADERS is non-zero, it will add standard DAV capabilites headers 755 * to request. 756 * |
|
807 * REQUEST_POOL should live for the duration of the request. Serf will 808 * construct this and provide it to the request_setup callback, so we 809 * should just use that one. 810 */ 811static svn_error_t * 812setup_serf_req(serf_request_t *request, 813 serf_bucket_t **req_bkt, 814 serf_bucket_t **hdrs_bkt, 815 svn_ra_serf__session_t *session, 816 const char *method, const char *url, 817 serf_bucket_t *body_bkt, const char *content_type, 818 const char *accept_encoding, | 757 * REQUEST_POOL should live for the duration of the request. Serf will 758 * construct this and provide it to the request_setup callback, so we 759 * should just use that one. 760 */ 761static svn_error_t * 762setup_serf_req(serf_request_t *request, 763 serf_bucket_t **req_bkt, 764 serf_bucket_t **hdrs_bkt, 765 svn_ra_serf__session_t *session, 766 const char *method, const char *url, 767 serf_bucket_t *body_bkt, const char *content_type, 768 const char *accept_encoding, |
769 svn_boolean_t dav_headers, |
|
819 apr_pool_t *request_pool, 820 apr_pool_t *scratch_pool) 821{ 822 serf_bucket_alloc_t *allocator = serf_request_get_alloc(request); 823 824 svn_spillbuf_t *buf; 825 svn_boolean_t set_CL = session->http10 || !session->using_chunked_requests; 826 --- 50 unchanged lines hidden (view full) --- 877 serf_bucket_headers_setn(*hdrs_bkt, "Connection", "keep-alive"); 878 } 879 880 if (accept_encoding) 881 { 882 serf_bucket_headers_setn(*hdrs_bkt, "Accept-Encoding", accept_encoding); 883 } 884 | 770 apr_pool_t *request_pool, 771 apr_pool_t *scratch_pool) 772{ 773 serf_bucket_alloc_t *allocator = serf_request_get_alloc(request); 774 775 svn_spillbuf_t *buf; 776 svn_boolean_t set_CL = session->http10 || !session->using_chunked_requests; 777 --- 50 unchanged lines hidden (view full) --- 828 serf_bucket_headers_setn(*hdrs_bkt, "Connection", "keep-alive"); 829 } 830 831 if (accept_encoding) 832 { 833 serf_bucket_headers_setn(*hdrs_bkt, "Accept-Encoding", accept_encoding); 834 } 835 |
885 /* These headers need to be sent with every request; see issue #3255 886 ("mod_dav_svn does not pass client capabilities to start-commit 887 hooks") for why. */ 888 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH); 889 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO); 890 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS); | 836 /* These headers need to be sent with every request that might need 837 capability processing (e.g. during commit, reports, etc.), see 838 issue #3255 ("mod_dav_svn does not pass client capabilities to 839 start-commit hooks") for why. |
891 | 840 |
841 Some request types like GET/HEAD/PROPFIND are unaware of capability 842 handling; and in some cases the responses can even be cached by 843 proxies, so we don't have to send these hearders there. */ 844 if (dav_headers) 845 { 846 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH); 847 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO); 848 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS); 849 } 850 |
|
892 return SVN_NO_ERROR; 893} 894 895svn_error_t * | 851 return SVN_NO_ERROR; 852} 853 854svn_error_t * |
855svn_ra_serf__context_run(svn_ra_serf__session_t *sess, 856 apr_interval_time_t *waittime_left, 857 apr_pool_t *scratch_pool) 858{ 859 apr_status_t status; 860 svn_error_t *err; 861 assert(sess->pending_error == SVN_NO_ERROR); 862 863 if (sess->cancel_func) 864 SVN_ERR(sess->cancel_func(sess->cancel_baton)); 865 866 status = serf_context_run(sess->context, 867 SVN_RA_SERF__CONTEXT_RUN_DURATION, 868 scratch_pool); 869 870 err = sess->pending_error; 871 sess->pending_error = SVN_NO_ERROR; 872 873 /* If the context duration timeout is up, we'll subtract that 874 duration from the total time alloted for such things. If 875 there's no time left, we fail with a message indicating that 876 the connection timed out. */ 877 if (APR_STATUS_IS_TIMEUP(status)) 878 { 879 status = 0; 880 881 if (sess->timeout) 882 { 883 if (*waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION) 884 { 885 *waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION; 886 } 887 else 888 { 889 return 890 svn_error_compose_create( 891 err, 892 svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL, 893 _("Connection timed out"))); 894 } 895 } 896 } 897 else 898 { 899 *waittime_left = sess->timeout; 900 } 901 902 SVN_ERR(err); 903 if (status) 904 { 905 /* ### This omits SVN_WARNING, and possibly relies on the fact that 906 ### MAX(SERF_ERROR_*) < SVN_ERR_BAD_CATEGORY_START? */ 907 if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST) 908 { 909 /* apr can't translate subversion errors to text */ 910 SVN_ERR_W(svn_error_create(status, NULL, NULL), 911 _("Error running context")); 912 } 913 914 return svn_ra_serf__wrap_err(status, _("Error running context")); 915 } 916 917 return SVN_NO_ERROR; 918} 919 920svn_error_t * |
|
896svn_ra_serf__context_run_wait(svn_boolean_t *done, 897 svn_ra_serf__session_t *sess, 898 apr_pool_t *scratch_pool) 899{ 900 apr_pool_t *iterpool; 901 apr_interval_time_t waittime_left = sess->timeout; 902 903 assert(sess->pending_error == SVN_NO_ERROR); 904 905 iterpool = svn_pool_create(scratch_pool); 906 while (!*done) 907 { | 921svn_ra_serf__context_run_wait(svn_boolean_t *done, 922 svn_ra_serf__session_t *sess, 923 apr_pool_t *scratch_pool) 924{ 925 apr_pool_t *iterpool; 926 apr_interval_time_t waittime_left = sess->timeout; 927 928 assert(sess->pending_error == SVN_NO_ERROR); 929 930 iterpool = svn_pool_create(scratch_pool); 931 while (!*done) 932 { |
908 apr_status_t status; 909 svn_error_t *err; | |
910 int i; 911 912 svn_pool_clear(iterpool); 913 | 933 int i; 934 935 svn_pool_clear(iterpool); 936 |
914 if (sess->cancel_func) 915 SVN_ERR((*sess->cancel_func)(sess->cancel_baton)); | 937 SVN_ERR(svn_ra_serf__context_run(sess, &waittime_left, iterpool)); |
916 | 938 |
917 status = serf_context_run(sess->context, 918 SVN_RA_SERF__CONTEXT_RUN_DURATION, 919 iterpool); 920 921 err = sess->pending_error; 922 sess->pending_error = SVN_NO_ERROR; 923 924 /* If the context duration timeout is up, we'll subtract that 925 duration from the total time alloted for such things. If 926 there's no time left, we fail with a message indicating that 927 the connection timed out. */ 928 if (APR_STATUS_IS_TIMEUP(status)) 929 { 930 status = 0; 931 932 if (sess->timeout) 933 { 934 if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION) 935 { 936 waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION; 937 } 938 else 939 { 940 return 941 svn_error_compose_create( 942 err, 943 svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL, 944 _("Connection timed out"))); 945 } 946 } 947 } 948 else 949 { 950 waittime_left = sess->timeout; 951 } 952 953 SVN_ERR(err); 954 if (status) 955 { 956 if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST) 957 { 958 /* apr can't translate subversion errors to text */ 959 SVN_ERR_W(svn_error_create(status, NULL, NULL), 960 _("Error running context")); 961 } 962 963 return svn_ra_serf__wrap_err(status, _("Error running context")); 964 } 965 | |
966 /* Debugging purposes only! */ 967 for (i = 0; i < sess->num_conns; i++) 968 { 969 serf_debug__closed_conn(sess->conns[i]->bkt_alloc); 970 } 971 } 972 svn_pool_destroy(iterpool); 973 974 return SVN_NO_ERROR; 975} 976 | 939 /* Debugging purposes only! */ 940 for (i = 0; i < sess->num_conns; i++) 941 { 942 serf_debug__closed_conn(sess->conns[i]->bkt_alloc); 943 } 944 } 945 svn_pool_destroy(iterpool); 946 947 return SVN_NO_ERROR; 948} 949 |
950/* Ensure that a handler is no longer scheduled on the connection. |
|
977 | 951 |
952 Eventually serf will have a reliable way to cancel existing requests, 953 but currently it doesn't even have a way to relyable identify a request 954 after rescheduling, for auth reasons. 955 956 So the only thing we can do today is reset the connection, which 957 will cancel all outstanding requests and prepare the connection 958 for re-use. 959*/ 960static void 961svn_ra_serf__unschedule_handler(svn_ra_serf__handler_t *handler) 962{ 963 serf_connection_reset(handler->conn->conn); 964 handler->scheduled = FALSE; 965} 966 |
|
978svn_error_t * 979svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler, 980 apr_pool_t *scratch_pool) 981{ 982 svn_error_t *err; 983 984 /* Create a serf request based on HANDLER. */ 985 svn_ra_serf__request_create(handler); 986 987 /* Wait until the response logic marks its DONE status. */ 988 err = svn_ra_serf__context_run_wait(&handler->done, handler->session, 989 scratch_pool); 990 | 967svn_error_t * 968svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler, 969 apr_pool_t *scratch_pool) 970{ 971 svn_error_t *err; 972 973 /* Create a serf request based on HANDLER. */ 974 svn_ra_serf__request_create(handler); 975 976 /* Wait until the response logic marks its DONE status. */ 977 err = svn_ra_serf__context_run_wait(&handler->done, handler->session, 978 scratch_pool); 979 |
991 /* A callback invocation has been canceled. In this simple case of 992 context_run_one, we can keep the ra-session operational by resetting 993 the connection. 994 995 If we don't do this, the next context run will notice that the connection 996 is still in the error state and will just return SVN_ERR_CEASE_INVOCATION 997 (=the last error for the connection) again */ 998 if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION) | 980 if (handler->scheduled) |
999 { | 981 { |
1000 apr_status_t status = serf_connection_reset(handler->conn->conn); 1001 1002 if (status) 1003 err = svn_error_compose_create(err, 1004 svn_ra_serf__wrap_err(status, NULL)); | 982 /* We reset the connection (breaking pipelining, etc.), as 983 if we didn't the next data would still be handled by this handler, 984 which is done as far as our caller is concerned. */ 985 svn_ra_serf__unschedule_handler(handler); |
1005 } 1006 | 986 } 987 |
1007 if (handler->server_error) 1008 { 1009 err = svn_error_compose_create(err, handler->server_error->error); 1010 handler->server_error = NULL; 1011 } 1012 | |
1013 return svn_error_trace(err); 1014} 1015 1016 | 988 return svn_error_trace(err); 989} 990 991 |
1017/* 1018 * Expat callback invoked on a start element tag for an error response. 1019 */ 1020static svn_error_t * 1021start_error(svn_ra_serf__xml_parser_t *parser, 1022 svn_ra_serf__dav_props_t name, 1023 const char **attrs, 1024 apr_pool_t *scratch_pool) 1025{ 1026 svn_ra_serf__server_error_t *ctx = parser->user_data; | |
1027 | 992 |
1028 if (!ctx->in_error && 1029 strcmp(name.namespace, "DAV:") == 0 && 1030 strcmp(name.name, "error") == 0) 1031 { 1032 ctx->in_error = TRUE; 1033 } 1034 else if (ctx->in_error && strcmp(name.name, "human-readable") == 0) 1035 { 1036 const char *err_code; | |
1037 | 993 |
1038 err_code = svn_xml_get_attr_value("errcode", attrs); 1039 if (err_code) 1040 { 1041 apr_int64_t val; 1042 1043 SVN_ERR(svn_cstring_atoi64(&val, err_code)); 1044 ctx->error->apr_err = (apr_status_t)val; 1045 } 1046 1047 /* If there's no error code provided, or if the provided code is 1048 0 (which can happen sometimes depending on how the error is 1049 constructed on the server-side), just pick a generic error 1050 code to run with. */ 1051 if (! ctx->error->apr_err) 1052 { 1053 ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; 1054 } 1055 1056 /* Start collecting cdata. */ 1057 svn_stringbuf_setempty(ctx->cdata); 1058 ctx->collect_cdata = TRUE; 1059 } 1060 1061 return SVN_NO_ERROR; 1062} 1063 1064/* 1065 * Expat callback invoked on an end element tag for a PROPFIND response. 1066 */ 1067static svn_error_t * 1068end_error(svn_ra_serf__xml_parser_t *parser, 1069 svn_ra_serf__dav_props_t name, 1070 apr_pool_t *scratch_pool) 1071{ 1072 svn_ra_serf__server_error_t *ctx = parser->user_data; 1073 1074 if (ctx->in_error && 1075 strcmp(name.namespace, "DAV:") == 0 && 1076 strcmp(name.name, "error") == 0) 1077 { 1078 ctx->in_error = FALSE; 1079 } 1080 if (ctx->in_error && strcmp(name.name, "human-readable") == 0) 1081 { 1082 /* On the server dav_error_response_tag() will add a leading 1083 and trailing newline if DEBUG_CR is defined in mod_dav.h, 1084 so remove any such characters here. */ 1085 svn_stringbuf_strip_whitespace(ctx->cdata); 1086 1087 ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data, 1088 ctx->cdata->len); 1089 ctx->collect_cdata = FALSE; 1090 } 1091 1092 return SVN_NO_ERROR; 1093} 1094 1095/* 1096 * Expat callback invoked on CDATA elements in an error response. 1097 * 1098 * This callback can be called multiple times. 1099 */ 1100static svn_error_t * 1101cdata_error(svn_ra_serf__xml_parser_t *parser, 1102 const char *data, 1103 apr_size_t len, 1104 apr_pool_t *scratch_pool) 1105{ 1106 svn_ra_serf__server_error_t *ctx = parser->user_data; 1107 1108 if (ctx->collect_cdata) 1109 { 1110 svn_stringbuf_appendbytes(ctx->cdata, data, len); 1111 } 1112 1113 return SVN_NO_ERROR; 1114} 1115 1116 | |
1117static apr_status_t 1118drain_bucket(serf_bucket_t *bucket) 1119{ 1120 /* Read whatever is in the bucket, and just drop it. */ 1121 while (1) 1122 { 1123 apr_status_t status; 1124 const char *data; 1125 apr_size_t len; 1126 1127 status = serf_bucket_read(bucket, SERF_READ_ALL_AVAIL, &data, &len); 1128 if (status) 1129 return status; 1130 } 1131} 1132 1133 | 994static apr_status_t 995drain_bucket(serf_bucket_t *bucket) 996{ 997 /* Read whatever is in the bucket, and just drop it. */ 998 while (1) 999 { 1000 apr_status_t status; 1001 const char *data; 1002 apr_size_t len; 1003 1004 status = serf_bucket_read(bucket, SERF_READ_ALL_AVAIL, &data, &len); 1005 if (status) 1006 return status; 1007 } 1008} 1009 1010 |
1134static svn_ra_serf__server_error_t * 1135begin_error_parsing(svn_ra_serf__xml_start_element_t start, 1136 svn_ra_serf__xml_end_element_t end, 1137 svn_ra_serf__xml_cdata_chunk_handler_t cdata, 1138 apr_pool_t *result_pool) 1139{ 1140 svn_ra_serf__server_error_t *server_err; | |
1141 | 1011 |
1142 server_err = apr_pcalloc(result_pool, sizeof(*server_err)); 1143 server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL); 1144 server_err->contains_precondition_error = FALSE; 1145 server_err->cdata = svn_stringbuf_create_empty(server_err->error->pool); 1146 server_err->collect_cdata = FALSE; 1147 server_err->parser.pool = server_err->error->pool; 1148 server_err->parser.user_data = server_err; 1149 server_err->parser.start = start; 1150 server_err->parser.end = end; 1151 server_err->parser.cdata = cdata; 1152 server_err->parser.ignore_errors = TRUE; | |
1153 | 1012 |
1154 return server_err; 1155} 1156 | |
1157/* Implements svn_ra_serf__response_handler_t */ 1158svn_error_t * 1159svn_ra_serf__handle_discard_body(serf_request_t *request, 1160 serf_bucket_t *response, 1161 void *baton, 1162 apr_pool_t *pool) 1163{ 1164 apr_status_t status; --- 71 unchanged lines hidden (view full) --- 1236 void *baton, 1237 apr_pool_t *scratch_pool) 1238{ 1239 svn_ra_serf__handler_t *handler = baton; 1240 serf_bucket_t *hdrs; 1241 const char *val; 1242 1243 /* This function is just like handle_multistatus_only() except for the | 1013/* Implements svn_ra_serf__response_handler_t */ 1014svn_error_t * 1015svn_ra_serf__handle_discard_body(serf_request_t *request, 1016 serf_bucket_t *response, 1017 void *baton, 1018 apr_pool_t *pool) 1019{ 1020 apr_status_t status; --- 71 unchanged lines hidden (view full) --- 1092 void *baton, 1093 apr_pool_t *scratch_pool) 1094{ 1095 svn_ra_serf__handler_t *handler = baton; 1096 serf_bucket_t *hdrs; 1097 const char *val; 1098 1099 /* This function is just like handle_multistatus_only() except for the |
1244 XML parsing callbacks. We want to look for the human-readable element. */ | 1100 XML parsing callbacks. We want to look for the -readable element. */ |
1245 1246 /* We should see this just once, in order to initialize SERVER_ERROR. 1247 At that point, the core error processing will take over. If we choose 1248 not to parse an error, then we'll never return here (because we 1249 change the response handler). */ 1250 SVN_ERR_ASSERT(handler->server_error == NULL); 1251 1252 hdrs = serf_bucket_response_get_headers(response); 1253 val = serf_bucket_headers_get(hdrs, "Content-Type"); | 1101 1102 /* We should see this just once, in order to initialize SERVER_ERROR. 1103 At that point, the core error processing will take over. If we choose 1104 not to parse an error, then we'll never return here (because we 1105 change the response handler). */ 1106 SVN_ERR_ASSERT(handler->server_error == NULL); 1107 1108 hdrs = serf_bucket_response_get_headers(response); 1109 val = serf_bucket_headers_get(hdrs, "Content-Type"); |
1254 if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) | 1110 if (val 1111 && (handler->sline.code < 200 || handler->sline.code >= 300) 1112 && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) |
1255 { 1256 svn_ra_serf__server_error_t *server_err; 1257 | 1113 { 1114 svn_ra_serf__server_error_t *server_err; 1115 |
1258 server_err = begin_error_parsing(start_error, end_error, cdata_error, 1259 handler->handler_pool); | 1116 SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler, 1117 FALSE, 1118 handler->handler_pool, 1119 handler->handler_pool)); |
1260 | 1120 |
1261 /* Get the parser to set our DONE flag. */ 1262 server_err->parser.done = &handler->done; 1263 | |
1264 handler->server_error = server_err; 1265 } 1266 else 1267 { | 1121 handler->server_error = server_err; 1122 } 1123 else 1124 { |
1268 /* The body was not text/xml, so we don't know what to do with it. | 1125 /* The body was not text/xml, or we got a success code. |
1269 Toss anything that arrives. */ 1270 handler->discard_body = TRUE; 1271 } 1272 1273 /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it 1274 to call the response handler again. That will start up the XML parsing, 1275 or it will be dropped on the floor (per the decision above). */ 1276 return SVN_NO_ERROR; 1277} 1278 1279 | 1126 Toss anything that arrives. */ 1127 handler->discard_body = TRUE; 1128 } 1129 1130 /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it 1131 to call the response handler again. That will start up the XML parsing, 1132 or it will be dropped on the floor (per the decision above). */ 1133 return SVN_NO_ERROR; 1134} 1135 1136 |
1280/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric 1281 status code into *STATUS_CODE_OUT. Ignores leading whitespace. */ 1282static svn_error_t * 1283parse_dav_status(int *status_code_out, svn_stringbuf_t *buf, 1284 apr_pool_t *scratch_pool) 1285{ 1286 svn_error_t *err; 1287 const char *token; 1288 char *tok_status; 1289 svn_stringbuf_t *temp_buf = svn_stringbuf_dup(buf, scratch_pool); 1290 1291 svn_stringbuf_strip_whitespace(temp_buf); 1292 token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status); 1293 if (token) 1294 token = apr_strtok(NULL, " \t\r\n", &tok_status); 1295 if (!token) 1296 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 1297 _("Malformed DAV:status CDATA '%s'"), 1298 buf->data); 1299 err = svn_cstring_atoi(status_code_out, token); 1300 if (err) 1301 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err, 1302 _("Malformed DAV:status CDATA '%s'"), 1303 buf->data); 1304 1305 return SVN_NO_ERROR; 1306} 1307 1308/* 1309 * Expat callback invoked on a start element tag for a 207 response. 1310 */ 1311static svn_error_t * 1312start_207(svn_ra_serf__xml_parser_t *parser, 1313 svn_ra_serf__dav_props_t name, 1314 const char **attrs, 1315 apr_pool_t *scratch_pool) 1316{ 1317 svn_ra_serf__server_error_t *ctx = parser->user_data; 1318 1319 if (!ctx->in_error && 1320 strcmp(name.namespace, "DAV:") == 0 && 1321 strcmp(name.name, "multistatus") == 0) 1322 { 1323 ctx->in_error = TRUE; 1324 } 1325 else if (ctx->in_error && strcmp(name.name, "responsedescription") == 0) 1326 { 1327 /* Start collecting cdata. */ 1328 svn_stringbuf_setempty(ctx->cdata); 1329 ctx->collect_cdata = TRUE; 1330 } 1331 else if (ctx->in_error && 1332 strcmp(name.namespace, "DAV:") == 0 && 1333 strcmp(name.name, "status") == 0) 1334 { 1335 /* Start collecting cdata. */ 1336 svn_stringbuf_setempty(ctx->cdata); 1337 ctx->collect_cdata = TRUE; 1338 } 1339 1340 return SVN_NO_ERROR; 1341} 1342 1343/* 1344 * Expat callback invoked on an end element tag for a 207 response. 1345 */ 1346static svn_error_t * 1347end_207(svn_ra_serf__xml_parser_t *parser, 1348 svn_ra_serf__dav_props_t name, 1349 apr_pool_t *scratch_pool) 1350{ 1351 svn_ra_serf__server_error_t *ctx = parser->user_data; 1352 1353 if (ctx->in_error && 1354 strcmp(name.namespace, "DAV:") == 0 && 1355 strcmp(name.name, "multistatus") == 0) 1356 { 1357 ctx->in_error = FALSE; 1358 } 1359 if (ctx->in_error && strcmp(name.name, "responsedescription") == 0) 1360 { 1361 /* Remove leading newline added by DEBUG_CR on server */ 1362 svn_stringbuf_strip_whitespace(ctx->cdata); 1363 1364 ctx->collect_cdata = FALSE; 1365 ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data, 1366 ctx->cdata->len); 1367 if (ctx->contains_precondition_error) 1368 ctx->error->apr_err = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH; 1369 else 1370 ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; 1371 } 1372 else if (ctx->in_error && 1373 strcmp(name.namespace, "DAV:") == 0 && 1374 strcmp(name.name, "status") == 0) 1375 { 1376 int status_code; 1377 1378 ctx->collect_cdata = FALSE; 1379 1380 SVN_ERR(parse_dav_status(&status_code, ctx->cdata, parser->pool)); 1381 if (status_code == 412) 1382 ctx->contains_precondition_error = TRUE; 1383 } 1384 1385 return SVN_NO_ERROR; 1386} 1387 1388/* 1389 * Expat callback invoked on CDATA elements in a 207 response. 1390 * 1391 * This callback can be called multiple times. 1392 */ 1393static svn_error_t * 1394cdata_207(svn_ra_serf__xml_parser_t *parser, 1395 const char *data, 1396 apr_size_t len, 1397 apr_pool_t *scratch_pool) 1398{ 1399 svn_ra_serf__server_error_t *ctx = parser->user_data; 1400 1401 if (ctx->collect_cdata) 1402 { 1403 svn_stringbuf_appendbytes(ctx->cdata, data, len); 1404 } 1405 1406 return SVN_NO_ERROR; 1407} 1408 1409/* Implements svn_ra_serf__response_handler_t */ 1410svn_error_t * 1411svn_ra_serf__handle_multistatus_only(serf_request_t *request, 1412 serf_bucket_t *response, 1413 void *baton, 1414 apr_pool_t *scratch_pool) 1415{ 1416 svn_ra_serf__handler_t *handler = baton; 1417 1418 /* This function is just like expect_empty_body() except for the 1419 XML parsing callbacks. We are looking for very limited pieces of 1420 the multistatus response. */ 1421 1422 /* We should see this just once, in order to initialize SERVER_ERROR. 1423 At that point, the core error processing will take over. If we choose 1424 not to parse an error, then we'll never return here (because we 1425 change the response handler). */ 1426 SVN_ERR_ASSERT(handler->server_error == NULL); 1427 1428 { 1429 serf_bucket_t *hdrs; 1430 const char *val; 1431 1432 hdrs = serf_bucket_response_get_headers(response); 1433 val = serf_bucket_headers_get(hdrs, "Content-Type"); 1434 if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1435 { 1436 svn_ra_serf__server_error_t *server_err; 1437 1438 server_err = begin_error_parsing(start_207, end_207, cdata_207, 1439 handler->handler_pool); 1440 1441 /* Get the parser to set our DONE flag. */ 1442 server_err->parser.done = &handler->done; 1443 1444 handler->server_error = server_err; 1445 } 1446 else 1447 { 1448 /* The body was not text/xml, so we don't know what to do with it. 1449 Toss anything that arrives. */ 1450 handler->discard_body = TRUE; 1451 } 1452 } 1453 1454 /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it 1455 to call the response handler again. That will start up the XML parsing, 1456 or it will be dropped on the floor (per the decision above). */ 1457 return SVN_NO_ERROR; 1458} 1459 1460 1461/* Conforms to Expat's XML_StartElementHandler */ 1462static void 1463start_xml(void *userData, const char *raw_name, const char **attrs) 1464{ 1465 svn_ra_serf__xml_parser_t *parser = userData; 1466 svn_ra_serf__dav_props_t name; 1467 apr_pool_t *scratch_pool; 1468 svn_error_t *err; 1469 1470 if (parser->error) 1471 return; 1472 1473 if (!parser->state) 1474 svn_ra_serf__xml_push_state(parser, 0); 1475 1476 /* ### get a real scratch_pool */ 1477 scratch_pool = parser->state->pool; 1478 1479 svn_ra_serf__define_ns(&parser->state->ns_list, attrs, parser->state->pool); 1480 1481 svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name); 1482 1483 err = parser->start(parser, name, attrs, scratch_pool); 1484 if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) 1485 err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); 1486 1487 parser->error = err; 1488} 1489 1490 1491/* Conforms to Expat's XML_EndElementHandler */ 1492static void 1493end_xml(void *userData, const char *raw_name) 1494{ 1495 svn_ra_serf__xml_parser_t *parser = userData; 1496 svn_ra_serf__dav_props_t name; 1497 svn_error_t *err; 1498 apr_pool_t *scratch_pool; 1499 1500 if (parser->error) 1501 return; 1502 1503 /* ### get a real scratch_pool */ 1504 scratch_pool = parser->state->pool; 1505 1506 svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name); 1507 1508 err = parser->end(parser, name, scratch_pool); 1509 if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) 1510 err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); 1511 1512 parser->error = err; 1513} 1514 1515 1516/* Conforms to Expat's XML_CharacterDataHandler */ 1517static void 1518cdata_xml(void *userData, const char *data, int len) 1519{ 1520 svn_ra_serf__xml_parser_t *parser = userData; 1521 svn_error_t *err; 1522 apr_pool_t *scratch_pool; 1523 1524 if (parser->error) 1525 return; 1526 1527 if (!parser->state) 1528 svn_ra_serf__xml_push_state(parser, 0); 1529 1530 /* ### get a real scratch_pool */ 1531 scratch_pool = parser->state->pool; 1532 1533 err = parser->cdata(parser, data, len, scratch_pool); 1534 if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) 1535 err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); 1536 1537 parser->error = err; 1538} 1539 1540/* Flip the requisite bits in CTX to indicate that processing of the 1541 response is complete, adding the current "done item" to the list of 1542 completed items. */ 1543static void 1544add_done_item(svn_ra_serf__xml_parser_t *ctx) 1545{ 1546 /* Make sure we don't add to DONE_LIST twice. */ 1547 if (!*ctx->done) 1548 { 1549 *ctx->done = TRUE; 1550 if (ctx->done_list) 1551 { 1552 ctx->done_item->data = ctx->user_data; 1553 ctx->done_item->next = *ctx->done_list; 1554 *ctx->done_list = ctx->done_item; 1555 } 1556 } 1557} 1558 1559 1560static svn_error_t * 1561write_to_pending(svn_ra_serf__xml_parser_t *ctx, 1562 const char *data, 1563 apr_size_t len, 1564 apr_pool_t *scratch_pool) 1565{ 1566 if (ctx->pending == NULL) 1567 { 1568 ctx->pending = apr_pcalloc(ctx->pool, sizeof(*ctx->pending)); 1569 ctx->pending->buf = svn_spillbuf__create(PARSE_CHUNK_SIZE, 1570 SPILL_SIZE, 1571 ctx->pool); 1572 } 1573 1574 /* Copy the data into one or more chunks in the spill buffer. */ 1575 return svn_error_trace(svn_spillbuf__write(ctx->pending->buf, 1576 data, len, 1577 scratch_pool)); 1578} 1579 1580 1581static svn_error_t * 1582inject_to_parser(svn_ra_serf__xml_parser_t *ctx, 1583 const char *data, 1584 apr_size_t len, 1585 const serf_status_line *sl) 1586{ 1587 int xml_status; 1588 1589 xml_status = XML_Parse(ctx->xmlp, data, (int) len, 0); 1590 1591 if (! ctx->ignore_errors) 1592 { 1593 SVN_ERR(ctx->error); 1594 1595 if (xml_status != XML_STATUS_OK) 1596 { 1597 if (sl == NULL) 1598 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 1599 _("XML parsing failed")); 1600 1601 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 1602 _("XML parsing failed: (%d %s)"), 1603 sl->code, sl->reason); 1604 } 1605 } 1606 1607 return SVN_NO_ERROR; 1608} 1609 1610/* Apr pool cleanup handler to release an XML_Parser in success and error 1611 conditions */ 1612static apr_status_t 1613xml_parser_cleanup(void *baton) 1614{ 1615 XML_Parser *xmlp = baton; 1616 1617 if (*xmlp) 1618 { 1619 (void) XML_ParserFree(*xmlp); 1620 *xmlp = NULL; 1621 } 1622 1623 return APR_SUCCESS; 1624} 1625 1626/* Limit the amount of pending content to parse at once to < 100KB per 1627 iteration. This number is chosen somewhat arbitrarely. Making it lower 1628 will have a drastical negative impact on performance, whereas increasing it 1629 increases the risk for connection timeouts. 1630 */ 1631#define PENDING_TO_PARSE PARSE_CHUNK_SIZE * 5 1632 1633svn_error_t * 1634svn_ra_serf__process_pending(svn_ra_serf__xml_parser_t *parser, 1635 svn_boolean_t *network_eof, 1636 apr_pool_t *scratch_pool) 1637{ 1638 svn_boolean_t pending_empty = FALSE; 1639 apr_size_t cur_read = 0; 1640 1641 /* Fast path exit: already paused, nothing to do, or already done. */ 1642 if (parser->paused || parser->pending == NULL || *parser->done) 1643 { 1644 *network_eof = parser->pending ? parser->pending->network_eof : FALSE; 1645 return SVN_NO_ERROR; 1646 } 1647 1648 /* Parsing the pending conten in the spillbuf will result in many disc i/o 1649 operations. This can be so slow that we don't run the network event 1650 processing loop often enough, resulting in timed out connections. 1651 1652 So we limit the amounts of bytes parsed per iteration. 1653 */ 1654 while (cur_read < PENDING_TO_PARSE) 1655 { 1656 const char *data; 1657 apr_size_t len; 1658 1659 /* Get a block of content, stopping the loop when we run out. */ 1660 SVN_ERR(svn_spillbuf__read(&data, &len, parser->pending->buf, 1661 scratch_pool)); 1662 if (data) 1663 { 1664 /* Inject the content into the XML parser. */ 1665 SVN_ERR(inject_to_parser(parser, data, len, NULL)); 1666 1667 /* If the XML parsing callbacks paused us, then we're done for now. */ 1668 if (parser->paused) 1669 break; 1670 1671 cur_read += len; 1672 } 1673 else 1674 { 1675 /* The buffer is empty. */ 1676 pending_empty = TRUE; 1677 break; 1678 } 1679 } 1680 1681 /* If the PENDING structures are empty *and* we consumed all content from 1682 the network, then we're completely done with the parsing. */ 1683 if (pending_empty && 1684 parser->pending->network_eof) 1685 { 1686 int xml_status; 1687 SVN_ERR_ASSERT(parser->xmlp != NULL); 1688 1689 /* Tell the parser that no more content will be parsed. */ 1690 xml_status = XML_Parse(parser->xmlp, NULL, 0, 1); 1691 1692 apr_pool_cleanup_run(parser->pool, &parser->xmlp, xml_parser_cleanup); 1693 parser->xmlp = NULL; 1694 1695 if (! parser->ignore_errors) 1696 { 1697 SVN_ERR(parser->error); 1698 1699 if (xml_status != XML_STATUS_OK) 1700 { 1701 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 1702 _("XML parsing failed")); 1703 } 1704 } 1705 1706 add_done_item(parser); 1707 } 1708 1709 *network_eof = parser->pending ? parser->pending->network_eof : FALSE; 1710 1711 return SVN_NO_ERROR; 1712} 1713#undef PENDING_TO_PARSE 1714 1715 1716/* ### this is still broken conceptually. just shifting incrementally... */ 1717static svn_error_t * 1718handle_server_error(serf_request_t *request, 1719 serf_bucket_t *response, 1720 apr_pool_t *scratch_pool) 1721{ 1722 svn_ra_serf__server_error_t server_err = { 0 }; 1723 serf_bucket_t *hdrs; 1724 const char *val; 1725 apr_status_t err; 1726 1727 hdrs = serf_bucket_response_get_headers(response); 1728 val = serf_bucket_headers_get(hdrs, "Content-Type"); 1729 if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1730 { 1731 /* ### we should figure out how to reuse begin_error_parsing */ 1732 1733 server_err.error = svn_error_create(APR_SUCCESS, NULL, NULL); 1734 server_err.contains_precondition_error = FALSE; 1735 server_err.cdata = svn_stringbuf_create_empty(scratch_pool); 1736 server_err.collect_cdata = FALSE; 1737 server_err.parser.pool = server_err.error->pool; 1738 server_err.parser.user_data = &server_err; 1739 server_err.parser.start = start_error; 1740 server_err.parser.end = end_error; 1741 server_err.parser.cdata = cdata_error; 1742 server_err.parser.done = &server_err.done; 1743 server_err.parser.ignore_errors = TRUE; 1744 1745 /* We don't care about any errors except for SERVER_ERR.ERROR */ 1746 svn_error_clear(svn_ra_serf__handle_xml_parser(request, 1747 response, 1748 &server_err.parser, 1749 scratch_pool)); 1750 1751 /* ### checking DONE is silly. the above only parses whatever has 1752 ### been received at the network interface. totally wrong. but 1753 ### it is what we have for now (maintaining historical code), 1754 ### until we fully migrate. */ 1755 if (server_err.done && server_err.error->apr_err == APR_SUCCESS) 1756 { 1757 svn_error_clear(server_err.error); 1758 server_err.error = SVN_NO_ERROR; 1759 } 1760 1761 return svn_error_trace(server_err.error); 1762 } 1763 1764 /* The only error that we will return is from the XML response body. 1765 Otherwise, ignore the entire body but allow SUCCESS/EOF/EAGAIN to 1766 surface. */ 1767 err = drain_bucket(response); 1768 if (err && !SERF_BUCKET_READ_ERROR(err)) 1769 return svn_ra_serf__wrap_err(err, NULL); 1770 1771 return SVN_NO_ERROR; 1772} 1773 1774 1775/* Implements svn_ra_serf__response_handler_t */ 1776svn_error_t * 1777svn_ra_serf__handle_xml_parser(serf_request_t *request, 1778 serf_bucket_t *response, 1779 void *baton, 1780 apr_pool_t *pool) 1781{ 1782 serf_status_line sl; 1783 apr_status_t status; 1784 svn_ra_serf__xml_parser_t *ctx = baton; 1785 svn_error_t *err; 1786 1787 /* ### get the HANDLER rather than fetching this. */ 1788 status = serf_bucket_response_status(response, &sl); 1789 if (SERF_BUCKET_READ_ERROR(status)) 1790 { 1791 return svn_ra_serf__wrap_err(status, NULL); 1792 } 1793 1794 /* Woo-hoo. Nothing here to see. */ 1795 if (sl.code == 404 && !ctx->ignore_errors) 1796 { 1797 err = handle_server_error(request, response, pool); 1798 1799 if (err && APR_STATUS_IS_EOF(err->apr_err)) 1800 add_done_item(ctx); 1801 1802 return svn_error_trace(err); 1803 } 1804 1805 if (!ctx->xmlp) 1806 { 1807 ctx->xmlp = XML_ParserCreate(NULL); 1808 apr_pool_cleanup_register(ctx->pool, &ctx->xmlp, xml_parser_cleanup, 1809 apr_pool_cleanup_null); 1810 XML_SetUserData(ctx->xmlp, ctx); 1811 XML_SetElementHandler(ctx->xmlp, start_xml, end_xml); 1812 if (ctx->cdata) 1813 { 1814 XML_SetCharacterDataHandler(ctx->xmlp, cdata_xml); 1815 } 1816 } 1817 1818 while (1) 1819 { 1820 const char *data; 1821 apr_size_t len; 1822 1823 status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); 1824 if (SERF_BUCKET_READ_ERROR(status)) 1825 { 1826 return svn_ra_serf__wrap_err(status, NULL); 1827 } 1828 1829 /* Note: once the callbacks invoked by inject_to_parser() sets the 1830 PAUSED flag, then it will not be cleared. write_to_pending() will 1831 only save the content. Logic outside of serf_context_run() will 1832 clear that flag, as appropriate, along with processing the 1833 content that we have placed into the PENDING buffer. 1834 1835 We want to save arriving content into the PENDING structures if 1836 the parser has been paused, or we already have data in there (so 1837 the arriving data is appended, rather than injected out of order) */ 1838 if (ctx->paused || HAS_PENDING_DATA(ctx->pending)) 1839 { 1840 err = write_to_pending(ctx, data, len, pool); 1841 } 1842 else 1843 { 1844 err = inject_to_parser(ctx, data, len, &sl); 1845 if (err) 1846 { 1847 /* Should have no errors if IGNORE_ERRORS is set. */ 1848 SVN_ERR_ASSERT(!ctx->ignore_errors); 1849 } 1850 } 1851 if (err) 1852 { 1853 SVN_ERR_ASSERT(ctx->xmlp != NULL); 1854 1855 apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup); 1856 add_done_item(ctx); 1857 return svn_error_trace(err); 1858 } 1859 1860 if (APR_STATUS_IS_EAGAIN(status)) 1861 { 1862 return svn_ra_serf__wrap_err(status, NULL); 1863 } 1864 1865 if (APR_STATUS_IS_EOF(status)) 1866 { 1867 if (ctx->pending != NULL) 1868 ctx->pending->network_eof = TRUE; 1869 1870 /* We just hit the end of the network content. If we have nothing 1871 in the PENDING structures, then we're completely done. */ 1872 if (!HAS_PENDING_DATA(ctx->pending)) 1873 { 1874 int xml_status; 1875 SVN_ERR_ASSERT(ctx->xmlp != NULL); 1876 1877 xml_status = XML_Parse(ctx->xmlp, NULL, 0, 1); 1878 1879 apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup); 1880 1881 if (! ctx->ignore_errors) 1882 { 1883 SVN_ERR(ctx->error); 1884 1885 if (xml_status != XML_STATUS_OK) 1886 { 1887 return svn_error_create( 1888 SVN_ERR_XML_MALFORMED, NULL, 1889 _("The XML response contains invalid XML")); 1890 } 1891 } 1892 1893 add_done_item(ctx); 1894 } 1895 1896 return svn_ra_serf__wrap_err(status, NULL); 1897 } 1898 1899 /* feed me! */ 1900 } 1901 /* not reached */ 1902} 1903 1904 | |
1905apr_status_t 1906svn_ra_serf__credentials_callback(char **username, char **password, 1907 serf_request_t *request, void *baton, 1908 int code, const char *authn_type, 1909 const char *realm, 1910 apr_pool_t *pool) 1911{ 1912 svn_ra_serf__handler_t *handler = baton; --- 9 unchanged lines hidden (view full) --- 1922 session->auth_state wasn't set (eg. if the credentials provider was 1923 cancelled by the user). */ 1924 if (!session->auth_state) 1925 { 1926 err = svn_auth_first_credentials(&creds, 1927 &session->auth_state, 1928 SVN_AUTH_CRED_SIMPLE, 1929 realm, | 1137apr_status_t 1138svn_ra_serf__credentials_callback(char **username, char **password, 1139 serf_request_t *request, void *baton, 1140 int code, const char *authn_type, 1141 const char *realm, 1142 apr_pool_t *pool) 1143{ 1144 svn_ra_serf__handler_t *handler = baton; --- 9 unchanged lines hidden (view full) --- 1154 session->auth_state wasn't set (eg. if the credentials provider was 1155 cancelled by the user). */ 1156 if (!session->auth_state) 1157 { 1158 err = svn_auth_first_credentials(&creds, 1159 &session->auth_state, 1160 SVN_AUTH_CRED_SIMPLE, 1161 realm, |
1930 session->wc_callbacks->auth_baton, | 1162 session->auth_baton, |
1931 session->pool); 1932 } 1933 else 1934 { 1935 err = svn_auth_next_credentials(&creds, 1936 session->auth_state, 1937 session->pool); 1938 } --- 64 unchanged lines hidden (view full) --- 2003 /* ### need to verify whether this already gets init'd on every 2004 ### successful exit. for an error-exit, it will (properly) be 2005 ### ignored by the caller. */ 2006 *serf_status = APR_SUCCESS; 2007 2008 if (!response) 2009 { 2010 /* Uh-oh. Our connection died. */ | 1163 session->pool); 1164 } 1165 else 1166 { 1167 err = svn_auth_next_credentials(&creds, 1168 session->auth_state, 1169 session->pool); 1170 } --- 64 unchanged lines hidden (view full) --- 1235 /* ### need to verify whether this already gets init'd on every 1236 ### successful exit. for an error-exit, it will (properly) be 1237 ### ignored by the caller. */ 1238 *serf_status = APR_SUCCESS; 1239 1240 if (!response) 1241 { 1242 /* Uh-oh. Our connection died. */ |
1243 handler->scheduled = FALSE; 1244 |
|
2011 if (handler->response_error) 2012 { 2013 /* Give a handler chance to prevent request requeue. */ 2014 SVN_ERR(handler->response_error(request, response, 0, 2015 handler->response_error_baton)); 2016 2017 svn_ra_serf__request_create(handler); 2018 } --- 102 unchanged lines hidden (view full) --- 2121 { 2122 SVN_ERR(svn_auth_save_credentials(handler->session->auth_state, 2123 handler->session->pool)); 2124 handler->session->auth_attempts = 0; 2125 handler->session->auth_state = NULL; 2126 } 2127 handler->conn->last_status_code = handler->sline.code; 2128 | 1245 if (handler->response_error) 1246 { 1247 /* Give a handler chance to prevent request requeue. */ 1248 SVN_ERR(handler->response_error(request, response, 0, 1249 handler->response_error_baton)); 1250 1251 svn_ra_serf__request_create(handler); 1252 } --- 102 unchanged lines hidden (view full) --- 1355 { 1356 SVN_ERR(svn_auth_save_credentials(handler->session->auth_state, 1357 handler->session->pool)); 1358 handler->session->auth_attempts = 0; 1359 handler->session->auth_state = NULL; 1360 } 1361 handler->conn->last_status_code = handler->sline.code; 1362 |
2129 if (handler->sline.code == 405 2130 || handler->sline.code == 408 2131 || handler->sline.code == 409 2132 || handler->sline.code >= 500) | 1363 if (handler->sline.code >= 400) |
2133 { 2134 /* 405 Method Not allowed. 2135 408 Request Timeout 2136 409 Conflict: can indicate a hook error. 2137 5xx (Internal) Server error. */ 2138 serf_bucket_t *hdrs; 2139 const char *val; 2140 2141 hdrs = serf_bucket_response_get_headers(response); 2142 val = serf_bucket_headers_get(hdrs, "Content-Type"); 2143 if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 2144 { 2145 svn_ra_serf__server_error_t *server_err; 2146 | 1364 { 1365 /* 405 Method Not allowed. 1366 408 Request Timeout 1367 409 Conflict: can indicate a hook error. 1368 5xx (Internal) Server error. */ 1369 serf_bucket_t *hdrs; 1370 const char *val; 1371 1372 hdrs = serf_bucket_response_get_headers(response); 1373 val = serf_bucket_headers_get(hdrs, "Content-Type"); 1374 if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1375 { 1376 svn_ra_serf__server_error_t *server_err; 1377 |
2147 server_err = begin_error_parsing(start_error, end_error, cdata_error, 2148 handler->handler_pool); 2149 /* Get the parser to set our DONE flag. */ 2150 server_err->parser.done = &handler->done; | 1378 SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler, 1379 FALSE, 1380 handler->handler_pool, 1381 handler->handler_pool)); |
2151 2152 handler->server_error = server_err; 2153 } 2154 else 2155 { 2156 handler->discard_body = TRUE; | 1382 1383 handler->server_error = server_err; 1384 } 1385 else 1386 { 1387 handler->discard_body = TRUE; |
2157 2158 if (!handler->session->pending_error) 2159 { 2160 apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; 2161 2162 /* 405 == Method Not Allowed (Occurs when trying to lock a working 2163 copy path which no longer exists at HEAD in the repository. */ 2164 if (handler->sline.code == 405 2165 && strcmp(handler->method, "LOCK") == 0) 2166 apr_err = SVN_ERR_FS_OUT_OF_DATE; 2167 2168 handler->session->pending_error = 2169 svn_error_createf(apr_err, NULL, 2170 _("%s request on '%s' failed: %d %s"), 2171 handler->method, handler->path, 2172 handler->sline.code, handler->sline.reason); 2173 } | |
2174 } 2175 } | 1388 } 1389 } |
1390 else if (handler->sline.code <= 199) 1391 { 1392 handler->discard_body = TRUE; 1393 } |
|
2176 2177 /* Stop processing the above, on every packet arrival. */ 2178 handler->reading_body = TRUE; 2179 2180 process_body: 2181 2182 /* We've been instructed to ignore the body. Drain whatever is present. */ 2183 if (handler->discard_body) 2184 { 2185 *serf_status = drain_bucket(response); 2186 | 1394 1395 /* Stop processing the above, on every packet arrival. */ 1396 handler->reading_body = TRUE; 1397 1398 process_body: 1399 1400 /* We've been instructed to ignore the body. Drain whatever is present. */ 1401 if (handler->discard_body) 1402 { 1403 *serf_status = drain_bucket(response); 1404 |
2187 /* If the handler hasn't set done (which it shouldn't have) and 2188 we now have the EOF, go ahead and set it so that we can stop 2189 our context loops. 2190 */ 2191 if (!handler->done && APR_STATUS_IS_EOF(*serf_status)) 2192 handler->done = TRUE; 2193 | |
2194 return SVN_NO_ERROR; 2195 } 2196 2197 /* If we are supposed to parse the body as a server_error, then do 2198 that now. */ 2199 if (handler->server_error != NULL) 2200 { | 1405 return SVN_NO_ERROR; 1406 } 1407 1408 /* If we are supposed to parse the body as a server_error, then do 1409 that now. */ 1410 if (handler->server_error != NULL) 1411 { |
2201 err = svn_ra_serf__handle_xml_parser(request, response, 2202 &handler->server_error->parser, 2203 scratch_pool); 2204 2205 /* If we do not receive an error or it is a non-transient error, return 2206 immediately. 2207 2208 APR_EOF will be returned when parsing is complete. 2209 2210 APR_EAGAIN & WAIT_CONN may be intermittently returned as we proceed through 2211 parsing and the network has no more data right now. If we receive that, 2212 clear the error and return - allowing serf to wait for more data. 2213 */ 2214 if (!err || SERF_BUCKET_READ_ERROR(err->apr_err)) 2215 return svn_error_trace(err); 2216 2217 if (!APR_STATUS_IS_EOF(err->apr_err)) 2218 { 2219 *serf_status = err->apr_err; 2220 svn_error_clear(err); 2221 return SVN_NO_ERROR; 2222 } 2223 2224 /* Clear the EOF. We don't need it. */ 2225 svn_error_clear(err); 2226 2227 /* If the parsing is done, and we did not extract an error, then 2228 simply toss everything, and anything else that might arrive. 2229 The higher-level code will need to investigate HANDLER->SLINE, 2230 as we have no further information for them. */ 2231 if (handler->done 2232 && handler->server_error->error->apr_err == APR_SUCCESS) 2233 { 2234 svn_error_clear(handler->server_error->error); 2235 2236 /* Stop parsing for a server error. */ 2237 handler->server_error = NULL; 2238 2239 /* If anything arrives after this, then just discard it. */ 2240 handler->discard_body = TRUE; 2241 } 2242 2243 *serf_status = APR_EOF; 2244 return SVN_NO_ERROR; | 1412 return svn_error_trace( 1413 svn_ra_serf__handle_server_error(handler->server_error, 1414 handler, 1415 request, response, 1416 serf_status, 1417 scratch_pool)); |
2245 } 2246 2247 /* Pass the body along to the registered response handler. */ 2248 err = handler->response_handler(request, response, 2249 handler->response_baton, 2250 scratch_pool); 2251 2252 if (err --- 13 unchanged lines hidden (view full) --- 2266 2267 2268/* Implements serf_response_handler_t for handle_response. Storing 2269 errors in handler->session->pending_error if appropriate. */ 2270static apr_status_t 2271handle_response_cb(serf_request_t *request, 2272 serf_bucket_t *response, 2273 void *baton, | 1418 } 1419 1420 /* Pass the body along to the registered response handler. */ 1421 err = handler->response_handler(request, response, 1422 handler->response_baton, 1423 scratch_pool); 1424 1425 if (err --- 13 unchanged lines hidden (view full) --- 1439 1440 1441/* Implements serf_response_handler_t for handle_response. Storing 1442 errors in handler->session->pending_error if appropriate. */ 1443static apr_status_t 1444handle_response_cb(serf_request_t *request, 1445 serf_bucket_t *response, 1446 void *baton, |
2274 apr_pool_t *scratch_pool) | 1447 apr_pool_t *response_pool) |
2275{ 2276 svn_ra_serf__handler_t *handler = baton; 2277 svn_error_t *err; 2278 apr_status_t inner_status; 2279 apr_status_t outer_status; | 1448{ 1449 svn_ra_serf__handler_t *handler = baton; 1450 svn_error_t *err; 1451 apr_status_t inner_status; 1452 apr_status_t outer_status; |
1453 apr_pool_t *scratch_pool = response_pool; /* Scratch pool needed? */ |
|
2280 2281 err = svn_error_trace(handle_response(request, response, 2282 handler, &inner_status, 2283 scratch_pool)); 2284 2285 /* Select the right status value to return. */ 2286 outer_status = save_error(handler->session, err); 2287 if (!outer_status) 2288 outer_status = inner_status; 2289 | 1454 1455 err = svn_error_trace(handle_response(request, response, 1456 handler, &inner_status, 1457 scratch_pool)); 1458 1459 /* Select the right status value to return. */ 1460 outer_status = save_error(handler->session, err); 1461 if (!outer_status) 1462 outer_status = inner_status; 1463 |
2290 /* Make sure the DONE flag is set properly. */ | 1464 /* Make sure the DONE flag is set properly and requests are cleaned up. */ |
2291 if (APR_STATUS_IS_EOF(outer_status) || APR_STATUS_IS_EOF(inner_status)) | 1465 if (APR_STATUS_IS_EOF(outer_status) || APR_STATUS_IS_EOF(inner_status)) |
2292 handler->done = TRUE; | 1466 { 1467 svn_ra_serf__session_t *sess = handler->session; 1468 handler->done = TRUE; 1469 handler->scheduled = FALSE; 1470 outer_status = APR_EOF; |
2293 | 1471 |
1472 /* We use a cached handler->session here to allow handler to free the 1473 memory containing the handler */ 1474 save_error(sess, 1475 handler->done_delegate(request, handler->done_delegate_baton, 1476 scratch_pool)); 1477 } 1478 else if (SERF_BUCKET_READ_ERROR(outer_status) 1479 && handler->session->pending_error) 1480 { 1481 handler->discard_body = TRUE; /* Discard further data */ 1482 handler->done = TRUE; /* Mark as done */ 1483 /* handler->scheduled is still TRUE, as we still expect data. 1484 If we would return an error outer-status the connection 1485 would have to be restarted. With scheduled still TRUE 1486 destroying the handler's pool will still reset the 1487 connection, avoiding the posibility of returning 1488 an error for this handler when a new request is 1489 scheduled. */ 1490 outer_status = APR_EAGAIN; /* Exit context loop */ 1491 } 1492 |
|
2294 return outer_status; 2295} 2296 2297/* Perform basic request setup, with special handling for HEAD requests, 2298 and finer-grained callbacks invoked (if non-NULL) to produce the request 2299 headers and body. */ 2300static svn_error_t * 2301setup_request(serf_request_t *request, --- 5 unchanged lines hidden (view full) --- 2307 serf_bucket_t *body_bkt; 2308 serf_bucket_t *headers_bkt; 2309 const char *accept_encoding; 2310 2311 if (handler->body_delegate) 2312 { 2313 serf_bucket_alloc_t *bkt_alloc = serf_request_get_alloc(request); 2314 | 1493 return outer_status; 1494} 1495 1496/* Perform basic request setup, with special handling for HEAD requests, 1497 and finer-grained callbacks invoked (if non-NULL) to produce the request 1498 headers and body. */ 1499static svn_error_t * 1500setup_request(serf_request_t *request, --- 5 unchanged lines hidden (view full) --- 1506 serf_bucket_t *body_bkt; 1507 serf_bucket_t *headers_bkt; 1508 const char *accept_encoding; 1509 1510 if (handler->body_delegate) 1511 { 1512 serf_bucket_alloc_t *bkt_alloc = serf_request_get_alloc(request); 1513 |
2315 /* ### should pass the scratch_pool */ | |
2316 SVN_ERR(handler->body_delegate(&body_bkt, handler->body_delegate_baton, | 1514 SVN_ERR(handler->body_delegate(&body_bkt, handler->body_delegate_baton, |
2317 bkt_alloc, request_pool)); | 1515 bkt_alloc, request_pool, scratch_pool)); |
2318 } 2319 else 2320 { 2321 body_bkt = NULL; 2322 } 2323 2324 if (handler->custom_accept_encoding) 2325 { --- 7 unchanged lines hidden (view full) --- 2333 else 2334 { 2335 accept_encoding = NULL; 2336 } 2337 2338 SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt, 2339 handler->session, handler->method, handler->path, 2340 body_bkt, handler->body_type, accept_encoding, | 1516 } 1517 else 1518 { 1519 body_bkt = NULL; 1520 } 1521 1522 if (handler->custom_accept_encoding) 1523 { --- 7 unchanged lines hidden (view full) --- 1531 else 1532 { 1533 accept_encoding = NULL; 1534 } 1535 1536 SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt, 1537 handler->session, handler->method, handler->path, 1538 body_bkt, handler->body_type, accept_encoding, |
2341 request_pool, scratch_pool)); | 1539 !handler->no_dav_headers, request_pool, 1540 scratch_pool)); |
2342 2343 if (handler->header_delegate) 2344 { | 1541 1542 if (handler->header_delegate) 1543 { |
2345 /* ### should pass the scratch_pool */ | |
2346 SVN_ERR(handler->header_delegate(headers_bkt, 2347 handler->header_delegate_baton, | 1544 SVN_ERR(handler->header_delegate(headers_bkt, 1545 handler->header_delegate_baton, |
2348 request_pool)); | 1546 request_pool, scratch_pool)); |
2349 } 2350 | 1547 } 1548 |
2351 return APR_SUCCESS; | 1549 return SVN_NO_ERROR; |
2352} 2353 2354/* Implements the serf_request_setup_t interface (which sets up both a 2355 request and its response handler callback). Handles errors for 2356 setup_request_cb */ 2357static apr_status_t 2358setup_request_cb(serf_request_t *request, 2359 void *setup_baton, 2360 serf_bucket_t **req_bkt, 2361 serf_response_acceptor_t *acceptor, 2362 void **acceptor_baton, 2363 serf_response_handler_t *s_handler, 2364 void **s_handler_baton, | 1550} 1551 1552/* Implements the serf_request_setup_t interface (which sets up both a 1553 request and its response handler callback). Handles errors for 1554 setup_request_cb */ 1555static apr_status_t 1556setup_request_cb(serf_request_t *request, 1557 void *setup_baton, 1558 serf_bucket_t **req_bkt, 1559 serf_response_acceptor_t *acceptor, 1560 void **acceptor_baton, 1561 serf_response_handler_t *s_handler, 1562 void **s_handler_baton, |
2365 apr_pool_t *pool) | 1563 apr_pool_t *request_pool) |
2366{ 2367 svn_ra_serf__handler_t *handler = setup_baton; | 1564{ 1565 svn_ra_serf__handler_t *handler = setup_baton; |
1566 apr_pool_t *scratch_pool; |
|
2368 svn_error_t *err; 2369 | 1567 svn_error_t *err; 1568 |
2370 /* ### construct a scratch_pool? serf gives us a pool that will live for 2371 ### the duration of the request. */ 2372 apr_pool_t *scratch_pool = pool; | 1569 /* Construct a scratch_pool? serf gives us a pool that will live for 1570 the duration of the request. But requests are retried in some cases */ 1571 scratch_pool = svn_pool_create(request_pool); |
2373 2374 if (strcmp(handler->method, "HEAD") == 0) 2375 *acceptor = accept_head; 2376 else 2377 *acceptor = accept_response; | 1572 1573 if (strcmp(handler->method, "HEAD") == 0) 1574 *acceptor = accept_head; 1575 else 1576 *acceptor = accept_response; |
2378 *acceptor_baton = handler->session; | 1577 *acceptor_baton = handler; |
2379 2380 *s_handler = handle_response_cb; 2381 *s_handler_baton = handler; 2382 2383 err = svn_error_trace(setup_request(request, handler, req_bkt, | 1578 1579 *s_handler = handle_response_cb; 1580 *s_handler_baton = handler; 1581 1582 err = svn_error_trace(setup_request(request, handler, req_bkt, |
2384 pool /* request_pool */, scratch_pool)); | 1583 request_pool, scratch_pool)); |
2385 | 1584 |
1585 svn_pool_destroy(scratch_pool); |
|
2386 return save_error(handler->session, err); 2387} 2388 2389void 2390svn_ra_serf__request_create(svn_ra_serf__handler_t *handler) 2391{ | 1586 return save_error(handler->session, err); 1587} 1588 1589void 1590svn_ra_serf__request_create(svn_ra_serf__handler_t *handler) 1591{ |
2392 SVN_ERR_ASSERT_NO_RETURN(handler->handler_pool != NULL); | 1592 SVN_ERR_ASSERT_NO_RETURN(handler->handler_pool != NULL 1593 && !handler->scheduled); |
2393 | 1594 |
2394 /* In case HANDLER is re-queued, reset the various transient fields. 2395 2396 ### prior to recent changes, HANDLER was constant. maybe we should 2397 ### break out these processing fields, apart from the request 2398 ### definition. */ | 1595 /* In case HANDLER is re-queued, reset the various transient fields. */ |
2399 handler->done = FALSE; 2400 handler->server_error = NULL; 2401 handler->sline.version = 0; 2402 handler->location = NULL; 2403 handler->reading_body = FALSE; 2404 handler->discard_body = FALSE; | 1596 handler->done = FALSE; 1597 handler->server_error = NULL; 1598 handler->sline.version = 0; 1599 handler->location = NULL; 1600 handler->reading_body = FALSE; 1601 handler->discard_body = FALSE; |
1602 handler->scheduled = TRUE; |
|
2405 | 1603 |
2406 /* ### do we ever alter the >response_handler? */ | 1604 /* Keeping track of the returned request object would be nice, but doesn't 1605 work the way we would expect in ra_serf.. |
2407 | 1606 |
2408 /* ### do we need to hold onto the returned request object, or just 2409 ### not worry about it (the serf ctx will manage it). */ | 1607 Serf sometimes creates a new request for us (and destroys the old one) 1608 without telling, like when authentication failed (401/407 response. 1609 1610 We 'just' trust serf to do the right thing and expect it to tell us 1611 when the state of the request changes. 1612 1613 ### I fixed a request leak in serf in r2258 on auth failures. 1614 */ |
2410 (void) serf_connection_request_create(handler->conn->conn, 2411 setup_request_cb, handler); 2412} 2413 2414 2415svn_error_t * 2416svn_ra_serf__discover_vcc(const char **vcc_url, 2417 svn_ra_serf__session_t *session, | 1615 (void) serf_connection_request_create(handler->conn->conn, 1616 setup_request_cb, handler); 1617} 1618 1619 1620svn_error_t * 1621svn_ra_serf__discover_vcc(const char **vcc_url, 1622 svn_ra_serf__session_t *session, |
2418 svn_ra_serf__connection_t *conn, 2419 apr_pool_t *pool) | 1623 apr_pool_t *scratch_pool) |
2420{ 2421 const char *path; 2422 const char *relative_path; 2423 const char *uuid; 2424 2425 /* If we've already got the information our caller seeks, just return it. */ 2426 if (session->vcc_url && session->repos_root_str) 2427 { 2428 *vcc_url = session->vcc_url; 2429 return SVN_NO_ERROR; 2430 } 2431 | 1624{ 1625 const char *path; 1626 const char *relative_path; 1627 const char *uuid; 1628 1629 /* If we've already got the information our caller seeks, just return it. */ 1630 if (session->vcc_url && session->repos_root_str) 1631 { 1632 *vcc_url = session->vcc_url; 1633 return SVN_NO_ERROR; 1634 } 1635 |
2432 /* If no connection is provided, use the default one. */ 2433 if (! conn) 2434 { 2435 conn = session->conns[0]; 2436 } 2437 | |
2438 path = session->session_url.path; 2439 *vcc_url = NULL; 2440 uuid = NULL; 2441 2442 do 2443 { 2444 apr_hash_t *props; 2445 svn_error_t *err; 2446 | 1636 path = session->session_url.path; 1637 *vcc_url = NULL; 1638 uuid = NULL; 1639 1640 do 1641 { 1642 apr_hash_t *props; 1643 svn_error_t *err; 1644 |
2447 err = svn_ra_serf__fetch_node_props(&props, conn, | 1645 err = svn_ra_serf__fetch_node_props(&props, session, |
2448 path, SVN_INVALID_REVNUM, | 1646 path, SVN_INVALID_REVNUM, |
2449 base_props, pool, pool); | 1647 base_props, 1648 scratch_pool, scratch_pool); |
2450 if (! err) 2451 { 2452 apr_hash_t *ns_props; 2453 2454 ns_props = apr_hash_get(props, "DAV:", 4); 2455 *vcc_url = svn_prop_get_value(ns_props, 2456 "version-controlled-configuration"); 2457 --- 11 unchanged lines hidden (view full) --- 2469 return svn_error_trace(err); /* found a _real_ error */ 2470 } 2471 else 2472 { 2473 /* This happens when the file is missing in HEAD. */ 2474 svn_error_clear(err); 2475 2476 /* Okay, strip off a component from PATH. */ | 1649 if (! err) 1650 { 1651 apr_hash_t *ns_props; 1652 1653 ns_props = apr_hash_get(props, "DAV:", 4); 1654 *vcc_url = svn_prop_get_value(ns_props, 1655 "version-controlled-configuration"); 1656 --- 11 unchanged lines hidden (view full) --- 1668 return svn_error_trace(err); /* found a _real_ error */ 1669 } 1670 else 1671 { 1672 /* This happens when the file is missing in HEAD. */ 1673 svn_error_clear(err); 1674 1675 /* Okay, strip off a component from PATH. */ |
2477 path = svn_urlpath__dirname(path, pool); 2478 2479 /* An error occurred on conns. serf 0.4.0 remembers that 2480 the connection had a problem. We need to reset it, in 2481 order to use it again. */ 2482 serf_connection_reset(conn->conn); | 1676 path = svn_urlpath__dirname(path, scratch_pool); |
2483 } 2484 } 2485 } 2486 while ((path[0] != '\0') 2487 && (! (path[0] == '/' && path[1] == '\0'))); 2488 2489 if (!*vcc_url) 2490 { --- 9 unchanged lines hidden (view full) --- 2500 session->vcc_url = apr_pstrdup(session->pool, *vcc_url); 2501 } 2502 2503 /* Update our cached repository root URL. */ 2504 if (!session->repos_root_str) 2505 { 2506 svn_stringbuf_t *url_buf; 2507 | 1677 } 1678 } 1679 } 1680 while ((path[0] != '\0') 1681 && (! (path[0] == '/' && path[1] == '\0'))); 1682 1683 if (!*vcc_url) 1684 { --- 9 unchanged lines hidden (view full) --- 1694 session->vcc_url = apr_pstrdup(session->pool, *vcc_url); 1695 } 1696 1697 /* Update our cached repository root URL. */ 1698 if (!session->repos_root_str) 1699 { 1700 svn_stringbuf_t *url_buf; 1701 |
2508 url_buf = svn_stringbuf_create(path, pool); | 1702 url_buf = svn_stringbuf_create(path, scratch_pool); |
2509 2510 svn_path_remove_components(url_buf, 2511 svn_path_component_count(relative_path)); 2512 2513 /* Now recreate the root_url. */ 2514 session->repos_root = session->session_url; 2515 session->repos_root.path = 2516 (char *)svn_fspath__canonicalize(url_buf->data, session->pool); --- 11 unchanged lines hidden (view full) --- 2528 2529 return SVN_NO_ERROR; 2530} 2531 2532svn_error_t * 2533svn_ra_serf__get_relative_path(const char **rel_path, 2534 const char *orig_path, 2535 svn_ra_serf__session_t *session, | 1703 1704 svn_path_remove_components(url_buf, 1705 svn_path_component_count(relative_path)); 1706 1707 /* Now recreate the root_url. */ 1708 session->repos_root = session->session_url; 1709 session->repos_root.path = 1710 (char *)svn_fspath__canonicalize(url_buf->data, session->pool); --- 11 unchanged lines hidden (view full) --- 1722 1723 return SVN_NO_ERROR; 1724} 1725 1726svn_error_t * 1727svn_ra_serf__get_relative_path(const char **rel_path, 1728 const char *orig_path, 1729 svn_ra_serf__session_t *session, |
2536 svn_ra_serf__connection_t *conn, | |
2537 apr_pool_t *pool) 2538{ 2539 const char *decoded_root, *decoded_orig; 2540 2541 if (! session->repos_root.path) 2542 { 2543 const char *vcc_url; 2544 2545 /* This should only happen if we haven't detected HTTP v2 2546 support from the server. */ 2547 assert(! SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); 2548 2549 /* We don't actually care about the VCC_URL, but this API 2550 promises to populate the session's root-url cache, and that's 2551 what we really want. */ 2552 SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, | 1730 apr_pool_t *pool) 1731{ 1732 const char *decoded_root, *decoded_orig; 1733 1734 if (! session->repos_root.path) 1735 { 1736 const char *vcc_url; 1737 1738 /* This should only happen if we haven't detected HTTP v2 1739 support from the server. */ 1740 assert(! SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); 1741 1742 /* We don't actually care about the VCC_URL, but this API 1743 promises to populate the session's root-url cache, and that's 1744 what we really want. */ 1745 SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, |
2553 conn ? conn : session->conns[0], | |
2554 pool)); 2555 } 2556 2557 decoded_root = svn_path_uri_decode(session->repos_root.path, pool); 2558 decoded_orig = svn_path_uri_decode(orig_path, pool); 2559 *rel_path = svn_urlpath__skip_ancestor(decoded_root, decoded_orig); 2560 SVN_ERR_ASSERT(*rel_path != NULL); 2561 return SVN_NO_ERROR; 2562} 2563 2564svn_error_t * 2565svn_ra_serf__report_resource(const char **report_target, 2566 svn_ra_serf__session_t *session, | 1746 pool)); 1747 } 1748 1749 decoded_root = svn_path_uri_decode(session->repos_root.path, pool); 1750 decoded_orig = svn_path_uri_decode(orig_path, pool); 1751 *rel_path = svn_urlpath__skip_ancestor(decoded_root, decoded_orig); 1752 SVN_ERR_ASSERT(*rel_path != NULL); 1753 return SVN_NO_ERROR; 1754} 1755 1756svn_error_t * 1757svn_ra_serf__report_resource(const char **report_target, 1758 svn_ra_serf__session_t *session, |
2567 svn_ra_serf__connection_t *conn, | |
2568 apr_pool_t *pool) 2569{ 2570 /* If we have HTTP v2 support, we want to report against the 'me' 2571 resource. */ 2572 if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) 2573 *report_target = apr_pstrdup(pool, session->me_resource); 2574 2575 /* Otherwise, we'll use the default VCC. */ 2576 else | 1759 apr_pool_t *pool) 1760{ 1761 /* If we have HTTP v2 support, we want to report against the 'me' 1762 resource. */ 1763 if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) 1764 *report_target = apr_pstrdup(pool, session->me_resource); 1765 1766 /* Otherwise, we'll use the default VCC. */ 1767 else |
2577 SVN_ERR(svn_ra_serf__discover_vcc(report_target, session, conn, pool)); | 1768 SVN_ERR(svn_ra_serf__discover_vcc(report_target, session, pool)); |
2578 2579 return SVN_NO_ERROR; 2580} 2581 2582svn_error_t * 2583svn_ra_serf__error_on_status(serf_status_line sline, 2584 const char *path, 2585 const char *location) 2586{ 2587 switch(sline.code) 2588 { 2589 case 301: 2590 case 302: | 1769 1770 return SVN_NO_ERROR; 1771} 1772 1773svn_error_t * 1774svn_ra_serf__error_on_status(serf_status_line sline, 1775 const char *path, 1776 const char *location) 1777{ 1778 switch(sline.code) 1779 { 1780 case 301: 1781 case 302: |
1782 case 303: |
|
2591 case 307: | 1783 case 307: |
1784 case 308: |
|
2592 return svn_error_createf(SVN_ERR_RA_DAV_RELOCATED, NULL, 2593 (sline.code == 301) | 1785 return svn_error_createf(SVN_ERR_RA_DAV_RELOCATED, NULL, 1786 (sline.code == 301) |
2594 ? _("Repository moved permanently to '%s';" 2595 " please relocate") 2596 : _("Repository moved temporarily to '%s';" 2597 " please relocate"), location); | 1787 ? _("Repository moved permanently to '%s'") 1788 : _("Repository moved temporarily to '%s'"), 1789 location); |
2598 case 403: 2599 return svn_error_createf(SVN_ERR_RA_DAV_FORBIDDEN, NULL, 2600 _("Access to '%s' forbidden"), path); 2601 2602 case 404: 2603 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 2604 _("'%s' path not found"), path); | 1790 case 403: 1791 return svn_error_createf(SVN_ERR_RA_DAV_FORBIDDEN, NULL, 1792 _("Access to '%s' forbidden"), path); 1793 1794 case 404: 1795 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 1796 _("'%s' path not found"), path); |
1797 case 405: 1798 return svn_error_createf(SVN_ERR_RA_DAV_METHOD_NOT_ALLOWED, NULL, 1799 _("HTTP method is not allowed on '%s'"), 1800 path); 1801 case 409: 1802 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL, 1803 _("'%s' conflicts"), path); 1804 case 412: 1805 return svn_error_createf(SVN_ERR_RA_DAV_PRECONDITION_FAILED, NULL, 1806 _("Precondition on '%s' failed"), path); |
|
2605 case 423: 2606 return svn_error_createf(SVN_ERR_FS_NO_LOCK_TOKEN, NULL, 2607 _("'%s': no lock token available"), path); 2608 2609 case 411: 2610 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 2611 _("DAV request failed: 411 Content length required. The " 2612 "server or an intermediate proxy does not accept " 2613 "chunked encoding. Try setting 'http-chunked-requests' " 2614 "to 'auto' or 'no' in your client configuration.")); | 1807 case 423: 1808 return svn_error_createf(SVN_ERR_FS_NO_LOCK_TOKEN, NULL, 1809 _("'%s': no lock token available"), path); 1810 1811 case 411: 1812 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 1813 _("DAV request failed: 411 Content length required. The " 1814 "server or an intermediate proxy does not accept " 1815 "chunked encoding. Try setting 'http-chunked-requests' " 1816 "to 'auto' or 'no' in your client configuration.")); |
1817 case 500: 1818 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 1819 _("Unexpected server error %d '%s' on '%s'"), 1820 sline.code, sline.reason, path); |
|
2615 case 501: 2616 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 2617 _("The requested feature is not supported by " 2618 "'%s'"), path); 2619 } 2620 | 1821 case 501: 1822 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 1823 _("The requested feature is not supported by " 1824 "'%s'"), path); 1825 } 1826 |
2621 if (sline.code >= 300) | 1827 if (sline.code >= 300 || sline.code <= 199) |
2622 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, | 1828 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, |
2623 _("Unexpected HTTP status %d '%s' on '%s'\n"), | 1829 _("Unexpected HTTP status %d '%s' on '%s'"), |
2624 sline.code, sline.reason, path); 2625 2626 return SVN_NO_ERROR; 2627} 2628 2629svn_error_t * | 1830 sline.code, sline.reason, path); 1831 1832 return SVN_NO_ERROR; 1833} 1834 1835svn_error_t * |
1836svn_ra_serf__unexpected_status(svn_ra_serf__handler_t *handler) 1837{ 1838 /* Is it a standard error status? */ 1839 if (handler->sline.code != 405) 1840 SVN_ERR(svn_ra_serf__error_on_status(handler->sline, 1841 handler->path, 1842 handler->location)); 1843 1844 switch (handler->sline.code) 1845 { 1846 case 201: 1847 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 1848 _("Path '%s' unexpectedly created"), 1849 handler->path); 1850 case 204: 1851 return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, 1852 _("Path '%s' already exists"), 1853 handler->path); 1854 1855 case 405: 1856 return svn_error_createf(SVN_ERR_RA_DAV_METHOD_NOT_ALLOWED, NULL, 1857 _("The HTTP method '%s' is not allowed" 1858 " on '%s'"), 1859 handler->method, handler->path); 1860 default: 1861 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 1862 _("Unexpected HTTP status %d '%s' on '%s' " 1863 "request to '%s'"), 1864 handler->sline.code, handler->sline.reason, 1865 handler->method, handler->path); 1866 } 1867} 1868 1869svn_error_t * |
|
2630svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *ra_session, 2631 svn_delta_shim_callbacks_t *callbacks) 2632{ 2633 svn_ra_serf__session_t *session = ra_session->priv; 2634 2635 session->shim_callbacks = callbacks; 2636 return SVN_NO_ERROR; 2637} 2638 | 1870svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *ra_session, 1871 svn_delta_shim_callbacks_t *callbacks) 1872{ 1873 svn_ra_serf__session_t *session = ra_session->priv; 1874 1875 session->shim_callbacks = callbacks; 1876 return SVN_NO_ERROR; 1877} 1878 |
2639 2640/* Conforms to Expat's XML_StartElementHandler */ 2641static void 2642expat_start(void *userData, const char *raw_name, const char **attrs) | 1879/* Shared/standard done_delegate handler */ 1880static svn_error_t * 1881response_done(serf_request_t *request, 1882 void *handler_baton, 1883 apr_pool_t *scratch_pool) |
2643{ | 1884{ |
2644 struct expat_ctx_t *ectx = userData; | 1885 svn_ra_serf__handler_t *handler = handler_baton; |
2645 | 1886 |
2646 if (ectx->inner_error != NULL) 2647 return; | 1887 assert(handler->done); |
2648 | 1888 |
2649 ectx->inner_error = svn_error_trace( 2650 svn_ra_serf__xml_cb_start(ectx->xmlctx, 2651 raw_name, attrs)); | 1889 if (handler->no_fail_on_http_failure_status) 1890 return SVN_NO_ERROR; |
2652 | 1891 |
2653#ifdef EXPAT_HAS_STOPPARSER 2654 if (ectx->inner_error) 2655 (void) XML_StopParser(ectx->parser, 0 /* resumable */); 2656#endif 2657} | 1892 if (handler->server_error) 1893 return svn_ra_serf__server_error_create(handler, scratch_pool); |
2658 | 1894 |
2659 2660/* Conforms to Expat's XML_EndElementHandler */ 2661static void 2662expat_end(void *userData, const char *raw_name) 2663{ 2664 struct expat_ctx_t *ectx = userData; 2665 2666 if (ectx->inner_error != NULL) 2667 return; 2668 2669 ectx->inner_error = svn_error_trace( 2670 svn_ra_serf__xml_cb_end(ectx->xmlctx, raw_name)); 2671 2672#ifdef EXPAT_HAS_STOPPARSER 2673 if (ectx->inner_error) 2674 (void) XML_StopParser(ectx->parser, 0 /* resumable */); 2675#endif 2676} 2677 2678 2679/* Conforms to Expat's XML_CharacterDataHandler */ 2680static void 2681expat_cdata(void *userData, const char *data, int len) 2682{ 2683 struct expat_ctx_t *ectx = userData; 2684 2685 if (ectx->inner_error != NULL) 2686 return; 2687 2688 ectx->inner_error = svn_error_trace( 2689 svn_ra_serf__xml_cb_cdata(ectx->xmlctx, data, len)); 2690 2691#ifdef EXPAT_HAS_STOPPARSER 2692 if (ectx->inner_error) 2693 (void) XML_StopParser(ectx->parser, 0 /* resumable */); 2694#endif 2695} 2696 2697 2698/* Implements svn_ra_serf__response_handler_t */ 2699static svn_error_t * 2700expat_response_handler(serf_request_t *request, 2701 serf_bucket_t *response, 2702 void *baton, 2703 apr_pool_t *scratch_pool) 2704{ 2705 struct expat_ctx_t *ectx = baton; 2706 2707 if (!ectx->parser) | 1895 if (handler->sline.code >= 400 || handler->sline.code <= 199) |
2708 { | 1896 { |
2709 ectx->parser = XML_ParserCreate(NULL); 2710 apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser, 2711 xml_parser_cleanup, apr_pool_cleanup_null); 2712 XML_SetUserData(ectx->parser, ectx); 2713 XML_SetElementHandler(ectx->parser, expat_start, expat_end); 2714 XML_SetCharacterDataHandler(ectx->parser, expat_cdata); | 1897 return svn_error_trace(svn_ra_serf__unexpected_status(handler)); |
2715 } 2716 | 1898 } 1899 |
2717 /* ### TODO: sline.code < 200 should really be handled by the core */ 2718 if ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300)) | 1900 if ((handler->sline.code >= 300 && handler->sline.code < 399) 1901 && !handler->no_fail_on_http_redirect_status) |
2719 { | 1902 { |
2720 /* By deferring to expect_empty_body(), it will make a choice on 2721 how to handle the body. Whatever the decision, the core handler 2722 will take over, and we will not be called again. */ 2723 return svn_error_trace(svn_ra_serf__expect_empty_body( 2724 request, response, ectx->handler, 2725 scratch_pool)); | 1903 return svn_error_trace(svn_ra_serf__unexpected_status(handler)); |
2726 } 2727 | 1904 } 1905 |
2728 while (1) 2729 { 2730 apr_status_t status; 2731 const char *data; 2732 apr_size_t len; 2733 int expat_status; | 1906 return SVN_NO_ERROR; 1907} |
2734 | 1908 |
2735 status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); 2736 if (SERF_BUCKET_READ_ERROR(status)) 2737 return svn_ra_serf__wrap_err(status, NULL); | 1909/* Pool cleanup handler for request handlers. |
2738 | 1910 |
2739#if 0 2740 /* ### move restart/skip into the core handler */ 2741 ectx->handler->read_size += len; 2742#endif | 1911 If a serf context run stops for some outside error, like when the user 1912 cancels a request via ^C in the context loop, the handler is still 1913 registered in the serf context. With the pool cleanup there would be 1914 handlers registered in no freed memory. |
2743 | 1915 |
2744 /* ### move PAUSED behavior to a new response handler that can feed 2745 ### an inner handler, or can pause for a while. */ 2746 2747 /* ### should we have an IGNORE_ERRORS flag like the v1 parser? */ 2748 2749 expat_status = XML_Parse(ectx->parser, data, (int)len, 0 /* isFinal */); 2750 2751 /* We need to check INNER_ERROR first. This is an error from the 2752 callbacks that has been "dropped off" for us to retrieve. On 2753 current Expat parsers, we stop the parser when an error occurs, 2754 so we want to ignore EXPAT_STATUS (which reports the stoppage). 2755 2756 If an error is not present, THEN we go ahead and look for parsing 2757 errors. */ 2758 if (ectx->inner_error) 2759 { 2760 apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser, 2761 xml_parser_cleanup); 2762 return svn_error_trace(ectx->inner_error); 2763 } 2764 if (expat_status == XML_STATUS_ERROR) 2765 return svn_error_createf(SVN_ERR_XML_MALFORMED, 2766 ectx->inner_error, 2767 _("The %s response contains invalid XML" 2768 " (%d %s)"), 2769 ectx->handler->method, 2770 ectx->handler->sline.code, 2771 ectx->handler->sline.reason); 2772 2773 /* The parsing went fine. What has the bucket told us? */ 2774 2775 if (APR_STATUS_IS_EOF(status)) 2776 { 2777 /* Tell expat we've reached the end of the content. Ignore the 2778 return status. We just don't care. */ 2779 (void) XML_Parse(ectx->parser, NULL, 0, 1 /* isFinal */); 2780 2781 svn_ra_serf__xml_context_destroy(ectx->xmlctx); 2782 apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser, 2783 xml_parser_cleanup); 2784 2785 /* ### should check XMLCTX to see if it has returned to the 2786 ### INITIAL state. we may have ended early... */ 2787 } 2788 2789 if (status && !SERF_BUCKET_READ_ERROR(status)) 2790 { 2791 return svn_ra_serf__wrap_err(status, NULL); 2792 } | 1916 This fallback kills the connection for this case, which will make serf 1917 unregister any outstanding requests on it. */ 1918static apr_status_t 1919handler_cleanup(void *baton) 1920{ 1921 svn_ra_serf__handler_t *handler = baton; 1922 if (handler->scheduled) 1923 { 1924 svn_ra_serf__unschedule_handler(handler); |
2793 } 2794 | 1925 } 1926 |
2795 /* NOTREACHED */ | 1927 return APR_SUCCESS; |
2796} 2797 | 1928} 1929 |
2798 | |
2799svn_ra_serf__handler_t * | 1930svn_ra_serf__handler_t * |
2800svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx, 2801 apr_pool_t *result_pool) | 1931svn_ra_serf__create_handler(svn_ra_serf__session_t *session, 1932 apr_pool_t *result_pool) |
2802{ 2803 svn_ra_serf__handler_t *handler; | 1933{ 1934 svn_ra_serf__handler_t *handler; |
2804 struct expat_ctx_t *ectx; | |
2805 | 1935 |
2806 ectx = apr_pcalloc(result_pool, sizeof(*ectx)); 2807 ectx->xmlctx = xmlctx; 2808 ectx->parser = NULL; 2809 ectx->cleanup_pool = result_pool; 2810 2811 | |
2812 handler = apr_pcalloc(result_pool, sizeof(*handler)); 2813 handler->handler_pool = result_pool; | 1936 handler = apr_pcalloc(result_pool, sizeof(*handler)); 1937 handler->handler_pool = result_pool; |
2814 handler->response_handler = expat_response_handler; 2815 handler->response_baton = ectx; | |
2816 | 1938 |
2817 ectx->handler = handler; | 1939 apr_pool_cleanup_register(result_pool, handler, handler_cleanup, 1940 apr_pool_cleanup_null); |
2818 | 1941 |
1942 handler->session = session; 1943 handler->conn = session->conns[0]; 1944 1945 /* Setup the default done handler, to handle server errors */ 1946 handler->done_delegate_baton = handler; 1947 handler->done_delegate = response_done; 1948 |
|
2819 return handler; 2820} | 1949 return handler; 1950} |
1951 |
|