Deleted Added
full compact
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