]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_ra_serf/multistatus.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_ra_serf / multistatus.c
1 /*
2  * multistatus.c : parse multistatus (error) responses.
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
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24
25 \f
26 #include <assert.h>
27
28 #include <apr.h>
29
30 #include <serf.h>
31 #include <serf_bucket_types.h>
32
33 #include "svn_private_config.h"
34 #include "svn_hash.h"
35 #include "svn_dirent_uri.h"
36 #include "svn_path.h"
37 #include "svn_string.h"
38 #include "svn_xml.h"
39 #include "svn_props.h"
40 #include "svn_dirent_uri.h"
41
42 #include "private/svn_dep_compat.h"
43 #include "private/svn_fspath.h"
44
45 #include "ra_serf.h"
46
47 /* The current state of our XML parsing. */
48 typedef enum iprops_state_e {
49   INITIAL = XML_STATE_INITIAL,
50   MS_MULTISTATUS,
51
52   MS_RESPONSE,
53   MS_RESPONSE_HREF,
54
55   MS_PROPSTAT,
56   MS_PROPSTAT_PROP,
57   MS_PROPSTAT_PROP_NAME,
58   MS_PROPSTAT_STATUS,
59   MS_PROPSTAT_RESPONSEDESCRIPTION,
60   MS_PROPSTAT_ERROR,
61   MS_PROPSTAT_ERROR_HUMANREADABLE,
62
63   MS_RESPONSE_STATUS,
64   MS_RESPONSE_RESPONSEDESCRIPTION,
65   MS_RESPONSE_ERROR,
66   MS_RESPONSE_ERROR_HUMANREADABLE,
67
68   MS_MULTISTATUS_RESPONSEDESCRIPTION,
69
70   D_ERROR,
71   S_ERROR,
72   M_ERROR_HUMANREADABLE
73 } iprops_state_e;
74
75 /*
76   <D:multistatus xmlns:D="DAV:">
77     <D:response>
78       <D:href>http://something</D:href>
79       <!-- Possibly multiple D:href elements -->
80       <D:status>HTTP/1.1 500 failed</D:status>
81       <D:error>
82         <S:human-readable code="12345">
83           Some Subversion error
84         </S:human-readable>
85       </D:error>
86       <D:responsedescription>
87         Human readable description
88       </D:responsedescription>
89       <D:location>http://redirected</D:location>
90     </D:response>
91     ...
92   </D:multistatus>
93
94   Or for property operations:
95
96   <D:multistatus xmlns:D="DAV:">
97     <D:response>
98       <D:href>http://somewhere-else</D:href>
99       <D:propstat>
100         <D:propname><C:myprop /></D:propname>
101         <D:status>HTTP/1.1 499 failed</D:status>
102         <D:error>
103           <S:human-readable code="12345">
104             Some Subversion error
105           </S:human-readable>
106         </D:error>
107         <D:responsedescription>
108           Human readable description
109         </D:responsedescription>
110       </D:propstat>
111       <D:status>HTTP/1.1 499 failed</D:status>
112       <D:error>
113         <S:human-readable code="12345">
114           Some Subversion error
115         </S:human-readable>
116       </D:error>
117       <D:responsedescription>
118         Human readable description
119       </D:responsedescription>
120       <D:location>http://redirected</D:location>
121     <D:responsedescription>
122       Global description
123     <D:responsedescription>
124   </D:multistatus>
125
126   Or on request failures
127   <D:error>
128     <X:some-error xmlns="QQ" />
129     <D:human-readable code="12345">
130           Some Subversion error
131     </D:human-readable>
132   </D:error>
133  */
134
135 #define D_ "DAV:"
136 #define S_ SVN_XML_NAMESPACE
137 #define M_ "http://apache.org/dav/xmlns"
138 static const svn_ra_serf__xml_transition_t multistatus_ttable[] = {
139   { INITIAL, D_, "multistatus", MS_MULTISTATUS,
140     FALSE, { NULL }, FALSE },
141
142   { MS_MULTISTATUS, D_, "responsedescription", MS_MULTISTATUS_RESPONSEDESCRIPTION,
143     TRUE, { NULL }, TRUE },
144
145   /* <response> */
146   { MS_MULTISTATUS, D_, "response", MS_RESPONSE,
147     FALSE, { NULL }, TRUE },
148
149   { MS_RESPONSE, D_, "href", MS_RESPONSE_HREF,
150     TRUE, { NULL }, TRUE },
151
152   /* <propstat> */
153   { MS_RESPONSE, D_, "propstat", MS_PROPSTAT,
154     FALSE, { NULL }, TRUE },
155
156   { MS_PROPSTAT, D_, "prop", MS_PROPSTAT_PROP,
157     FALSE, { NULL }, FALSE },
158
159   { MS_PROPSTAT_PROP, "", "*", MS_PROPSTAT_PROP_NAME,
160     FALSE, { NULL }, FALSE },
161
162   { MS_PROPSTAT, D_, "status", MS_PROPSTAT_STATUS,
163     TRUE, { NULL }, TRUE },
164
165   { MS_PROPSTAT, D_, "responsedescription", MS_PROPSTAT_RESPONSEDESCRIPTION,
166     TRUE, { NULL }, TRUE },
167
168   { MS_PROPSTAT, D_, "error", MS_PROPSTAT_ERROR,
169     FALSE, { NULL }, FALSE },
170
171   { MS_PROPSTAT_ERROR, M_, "human-readable", MS_PROPSTAT_ERROR_HUMANREADABLE,
172     TRUE, { "?errcode", NULL }, TRUE },
173   /* </propstat> */
174
175
176   { MS_RESPONSE, D_, "status", MS_RESPONSE_STATUS,
177     TRUE, { NULL }, TRUE },
178
179   { MS_RESPONSE, D_, "responsedescription", MS_RESPONSE_RESPONSEDESCRIPTION,
180     TRUE, { NULL }, TRUE },
181
182   { MS_RESPONSE, D_, "error", MS_RESPONSE_ERROR,
183     FALSE, { NULL }, TRUE },
184
185   { MS_RESPONSE_ERROR, M_, "human-readable", MS_RESPONSE_ERROR_HUMANREADABLE,
186     TRUE, { "?errcode", NULL }, TRUE },
187
188   /* </response> */
189
190   { MS_MULTISTATUS, D_, "responsedescription", MS_MULTISTATUS_RESPONSEDESCRIPTION,
191     TRUE, { NULL }, TRUE },
192
193
194   { INITIAL, D_, "error", D_ERROR,
195     FALSE, { NULL }, TRUE },
196
197   { D_ERROR, S_, "error", S_ERROR,
198     FALSE, { NULL }, FALSE },
199
200   { D_ERROR, M_, "human-readable", M_ERROR_HUMANREADABLE,
201     TRUE, { "?errcode", NULL }, TRUE },
202
203   { 0 }
204 };
205
206 /* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric
207    status code into *STATUS_CODE_OUT.  Ignores leading whitespace. */
208 static svn_error_t *
209 parse_status_line(int *status_code_out,
210                   const char **reason,
211                   const char *status_line,
212                   apr_pool_t *result_pool,
213                   apr_pool_t *scratch_pool)
214 {
215   svn_error_t *err;
216   const char *token;
217   char *tok_status;
218   svn_stringbuf_t *temp_buf = svn_stringbuf_create(status_line, scratch_pool);
219
220   svn_stringbuf_strip_whitespace(temp_buf);
221   token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status);
222   if (token)
223     token = apr_strtok(NULL, " \t\r\n", &tok_status);
224   if (!token)
225     return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
226                              _("Malformed DAV:status '%s'"),
227                              status_line);
228   err = svn_cstring_atoi(status_code_out, token);
229   if (err)
230     return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
231                              _("Malformed DAV:status '%s'"),
232                              status_line);
233
234   token = apr_strtok(NULL, " \t\r\n", &tok_status);
235
236   *reason = apr_pstrdup(result_pool, token);
237
238   return SVN_NO_ERROR;
239 }
240
241
242 typedef struct error_item_t
243 {
244   const char *path;
245   const char *propname;
246
247   int http_status;
248   const char *http_reason;
249   apr_status_t apr_err;
250
251   const char *message;
252 } error_item_t;
253
254 static svn_error_t *
255 multistatus_opened(svn_ra_serf__xml_estate_t *xes,
256                    void *baton,
257                    int entered_state,
258                    const svn_ra_serf__dav_props_t *tag,
259                    apr_pool_t *scratch_pool)
260 {
261   /*struct svn_ra_serf__server_error_t *server_error = baton;*/
262   const char *propname;
263
264   switch (entered_state)
265     {
266       case MS_PROPSTAT_PROP_NAME:
267         if (strcmp(tag->xmlns, SVN_DAV_PROP_NS_SVN) == 0)
268           propname = apr_pstrcat(scratch_pool, SVN_PROP_PREFIX, tag->name,
269                                  SVN_VA_NULL);
270         else
271           propname = tag->name;
272         svn_ra_serf__xml_note(xes, MS_PROPSTAT, "propname", propname);
273         break;
274       case S_ERROR:
275         /* This toggles an has error boolean in libsvn_ra_neon in 1.7 */
276         break;
277     }
278
279   return SVN_NO_ERROR;
280 }
281
282 static svn_error_t *
283 multistatus_closed(svn_ra_serf__xml_estate_t *xes,
284                    void *baton,
285                    int leaving_state,
286                    const svn_string_t *cdata,
287                    apr_hash_t *attrs,
288                    apr_pool_t *scratch_pool)
289 {
290   struct svn_ra_serf__server_error_t *server_error = baton;
291   const char *errcode;
292   const char *status;
293
294   switch (leaving_state)
295     {
296       case MS_RESPONSE_HREF:
297         {
298           apr_status_t result;
299           apr_uri_t uri;
300
301           result = apr_uri_parse(scratch_pool, cdata->data, &uri);
302           if (result)
303             return svn_ra_serf__wrap_err(result, NULL);
304           svn_ra_serf__xml_note(xes, MS_RESPONSE, "path",
305                                 svn_urlpath__canonicalize(uri.path, scratch_pool));
306         }
307         break;
308       case MS_RESPONSE_STATUS:
309         svn_ra_serf__xml_note(xes, MS_RESPONSE, "status", cdata->data);
310         break;
311       case MS_RESPONSE_ERROR_HUMANREADABLE:
312         svn_ra_serf__xml_note(xes, MS_RESPONSE, "human-readable", cdata->data);
313         errcode = svn_hash_gets(attrs, "errcode");
314         if (errcode)
315           svn_ra_serf__xml_note(xes, MS_RESPONSE, "errcode", errcode);
316         break;
317       case MS_RESPONSE:
318         if ((status = svn_hash__get_cstring(attrs, "status", NULL)) != NULL)
319           {
320             error_item_t *item;
321
322             item = apr_pcalloc(server_error->pool, sizeof(*item));
323
324             item->path = apr_pstrdup(server_error->pool,
325                                      svn_hash_gets(attrs, "path"));
326
327             SVN_ERR(parse_status_line(&item->http_status,
328                                       &item->http_reason,
329                                       status,
330                                       server_error->pool,
331                                       scratch_pool));
332
333             /* Do we have a mod_dav specific message? */
334             item->message = svn_hash_gets(attrs, "human-readable");
335
336             if (item->message)
337               {
338                 if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL)
339                   {
340                     apr_int64_t val;
341
342                     SVN_ERR(svn_cstring_atoi64(&val, errcode));
343                     item->apr_err = (apr_status_t)val;
344                   }
345
346                 item->message = apr_pstrdup(server_error->pool, item->message);
347               }
348             else
349               item->message = apr_pstrdup(server_error->pool,
350                                           svn_hash_gets(attrs, "description"));
351
352             APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
353           }
354         break;
355
356
357       case MS_PROPSTAT_STATUS:
358         svn_ra_serf__xml_note(xes, MS_PROPSTAT, "status", cdata->data);
359         break;
360       case MS_PROPSTAT_ERROR_HUMANREADABLE:
361         svn_ra_serf__xml_note(xes, MS_PROPSTAT, "human-readable", cdata->data);
362         errcode = svn_hash_gets(attrs, "errcode");
363         if (errcode)
364           svn_ra_serf__xml_note(xes, MS_PROPSTAT, "errcode", errcode);
365         break;
366       case MS_PROPSTAT_RESPONSEDESCRIPTION:
367         svn_ra_serf__xml_note(xes, MS_PROPSTAT, "description",
368                               cdata->data);
369         break;
370
371       case MS_PROPSTAT:
372         if ((status = svn_hash__get_cstring(attrs, "status", NULL)) != NULL)
373           {
374             apr_hash_t *response_attrs;
375             error_item_t *item;
376
377             response_attrs = svn_ra_serf__xml_gather_since(xes, MS_RESPONSE);
378             item = apr_pcalloc(server_error->pool, sizeof(*item));
379
380             item->path = apr_pstrdup(server_error->pool,
381                                      svn_hash_gets(response_attrs, "path"));
382             item->propname = apr_pstrdup(server_error->pool,
383                                          svn_hash_gets(attrs, "propname"));
384
385             SVN_ERR(parse_status_line(&item->http_status,
386                                       &item->http_reason,
387                                       status,
388                                       server_error->pool,
389                                       scratch_pool));
390
391             /* Do we have a mod_dav specific message? */
392             item->message = svn_hash_gets(attrs, "human-readable");
393
394             if (item->message)
395               {
396                 if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL)
397                   {
398                     apr_int64_t val;
399
400                     SVN_ERR(svn_cstring_atoi64(&val, errcode));
401                     item->apr_err = (apr_status_t)val;
402                   }
403
404                 item->message = apr_pstrdup(server_error->pool, item->message);
405               }
406             else
407               item->message = apr_pstrdup(server_error->pool,
408                                           svn_hash_gets(attrs, "description"));
409
410
411             APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
412           }
413         break;
414
415       case M_ERROR_HUMANREADABLE:
416         svn_ra_serf__xml_note(xes, D_ERROR, "human-readable", cdata->data);
417         errcode = svn_hash_gets(attrs, "errcode");
418         if (errcode)
419           svn_ra_serf__xml_note(xes, D_ERROR, "errcode", errcode);
420         break;
421
422       case D_ERROR:
423         {
424           error_item_t *item;
425
426           item = apr_pcalloc(server_error->pool, sizeof(*item));
427
428           item->http_status = server_error->handler->sline.code;
429
430           /* Do we have a mod_dav specific message? */
431           item->message = svn_hash__get_cstring(attrs, "human-readable",
432                                                 NULL);
433
434           if (item->message)
435             {
436               if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL)
437                 {
438                   apr_int64_t val;
439
440                   SVN_ERR(svn_cstring_atoi64(&val, errcode));
441                   item->apr_err = (apr_status_t)val;
442                 }
443
444               item->message = apr_pstrdup(server_error->pool, item->message);
445             }
446
447
448           APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
449         }
450     }
451   return SVN_NO_ERROR;
452 }
453
454 svn_error_t *
455 svn_ra_serf__server_error_create(svn_ra_serf__handler_t *handler,
456                                  apr_pool_t *scratch_pool)
457 {
458   svn_ra_serf__server_error_t *server_error = handler->server_error;
459   svn_error_t *err = NULL;
460   int i;
461
462   for (i = 0; i < server_error->items->nelts; i++)
463     {
464       const error_item_t *item;
465       apr_status_t status;
466       const char *message;
467       svn_error_t *new_err;
468
469       item = APR_ARRAY_IDX(server_error->items, i, error_item_t *);
470
471       if (!item->apr_err && item->http_status == 200)
472         {
473           continue; /* Success code */
474         }
475       else if (!item->apr_err && item->http_status == 424 && item->propname)
476         {
477           continue; /* Failed because other PROPPATCH operations failed */
478         }
479
480       if (item->apr_err)
481         status = item->apr_err;
482       else
483         switch (item->http_status)
484           {
485             case 0:
486               continue; /* Not an error */
487             case 301:
488             case 302:
489             case 303:
490             case 307:
491             case 308:
492               status = SVN_ERR_RA_DAV_RELOCATED;
493               break;
494             case 403:
495               status = SVN_ERR_RA_DAV_FORBIDDEN;
496               break;
497             case 404:
498               status = SVN_ERR_FS_NOT_FOUND;
499               break;
500             case 409:
501               status = SVN_ERR_FS_CONFLICT;
502               break;
503             case 412:
504               status = SVN_ERR_RA_DAV_PRECONDITION_FAILED;
505               break;
506             case 423:
507               status = SVN_ERR_FS_NO_LOCK_TOKEN;
508               break;
509             case 500:
510               status = SVN_ERR_RA_DAV_REQUEST_FAILED;
511               break;
512             case 501:
513               status = SVN_ERR_UNSUPPORTED_FEATURE;
514               break;
515             default:
516               if (err)
517                 status = err->apr_err; /* Just use previous */
518               else
519                 status = SVN_ERR_RA_DAV_REQUEST_FAILED;
520               break;
521         }
522
523       if (item->message && *item->message)
524         {
525           svn_stringbuf_t *sb = svn_stringbuf_create(item->message,
526                                                      scratch_pool);
527
528           svn_stringbuf_strip_whitespace(sb);
529           message = sb->data;
530         }
531       else if (item->propname)
532         {
533           message = apr_psprintf(scratch_pool,
534                                  _("Property operation on '%s' failed"),
535                                  item->propname);
536         }
537       else
538         {
539           /* Yuck: Older servers sometimes assume that we get convertable
540                    apr error codes, while mod_dav_svn just produces a blank
541                    text error, because err->message is NULL. */
542           serf_status_line sline;
543           svn_error_t *tmp_err;
544
545           memset(&sline, 0, sizeof(sline));
546           sline.code = item->http_status;
547           sline.reason = item->http_reason;
548
549           tmp_err = svn_ra_serf__error_on_status(sline, item->path, NULL);
550
551           message = (tmp_err && tmp_err->message)
552                        ? apr_pstrdup(scratch_pool, tmp_err->message)
553                        : _("<blank error>");
554           svn_error_clear(tmp_err);
555         }
556
557       SVN_ERR_ASSERT(status > 0);
558       new_err = svn_error_create(status, NULL, message);
559
560       if (item->propname)
561         new_err = svn_error_createf(new_err->apr_err, new_err,
562                                     _("While handling the '%s' property on '%s':"),
563                                     item->propname, item->path);
564       else if (item->path)
565         new_err = svn_error_createf(new_err->apr_err, new_err,
566                                     _("While handling the '%s' path:"),
567                                     item->path);
568
569       err = svn_error_compose_create(
570                     err,
571                     new_err);
572     }
573
574   /* Theoretically a 207 status can have a 'global' description without a
575      global STATUS that summarizes the final result of property/href
576      operations.
577
578      We should wrap that around the existing errors if there is one.
579
580      But currently I don't see how mod_dav ever sets that value */
581
582   if (!err)
583     {
584       /* We should fail.... but why... Who installed us? */
585       err = svn_error_trace(svn_ra_serf__unexpected_status(handler));
586     }
587
588   return err;
589 }
590
591
592 svn_error_t *
593 svn_ra_serf__setup_error_parsing(svn_ra_serf__server_error_t **server_err,
594                                  svn_ra_serf__handler_t *handler,
595                                  svn_boolean_t expect_207_only,
596                                  apr_pool_t *result_pool,
597                                  apr_pool_t *scratch_pool)
598 {
599   svn_ra_serf__server_error_t *ms_baton;
600   svn_ra_serf__handler_t *tmp_handler;
601
602   int *expected_status = apr_pcalloc(result_pool,
603                                      2 * sizeof(expected_status[0]));
604
605   expected_status[0] = handler->sline.code;
606
607   ms_baton = apr_pcalloc(result_pool, sizeof(*ms_baton));
608   ms_baton->pool = result_pool;
609
610   ms_baton->items = apr_array_make(result_pool, 4, sizeof(error_item_t *));
611   ms_baton->handler = handler;
612
613   ms_baton->xmlctx = svn_ra_serf__xml_context_create(multistatus_ttable,
614                                                      multistatus_opened,
615                                                      multistatus_closed,
616                                                      NULL,
617                                                      ms_baton,
618                                                      ms_baton->pool);
619
620   tmp_handler = svn_ra_serf__create_expat_handler(handler->session,
621                                                   ms_baton->xmlctx,
622                                                   expected_status,
623                                                   result_pool);
624
625   /* Ugly way to obtain expat_handler() */
626   tmp_handler->sline = handler->sline;
627   ms_baton->response_handler = tmp_handler->response_handler;
628   ms_baton->response_baton = tmp_handler->response_baton;
629
630   *server_err = ms_baton;
631   return SVN_NO_ERROR;
632 }
633
634
635
636 /* Implements svn_ra_serf__response_handler_t */
637 svn_error_t *
638 svn_ra_serf__handle_multistatus_only(serf_request_t *request,
639                                      serf_bucket_t *response,
640                                      void *baton,
641                                      apr_pool_t *scratch_pool)
642 {
643   svn_ra_serf__handler_t *handler = baton;
644
645   /* This function is just like expect_empty_body() except for the
646      XML parsing callbacks. We are looking for very limited pieces of
647      the multistatus response.  */
648
649   /* We should see this just once, in order to initialize SERVER_ERROR.
650      At that point, the core error processing will take over. If we choose
651      not to parse an error, then we'll never return here (because we
652      change the response handler).  */
653   SVN_ERR_ASSERT(handler->server_error == NULL);
654
655     {
656       serf_bucket_t *hdrs;
657       const char *val;
658
659       hdrs = serf_bucket_response_get_headers(response);
660       val = serf_bucket_headers_get(hdrs, "Content-Type");
661       if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
662         {
663           svn_ra_serf__server_error_t *server_err;
664
665           SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err,
666                                                    handler,
667                                                    TRUE,
668                                                    handler->handler_pool,
669                                                    handler->handler_pool));
670
671           handler->server_error = server_err;
672         }
673       else
674         {
675           /* The body was not text/xml, so we don't know what to do with it.
676              Toss anything that arrives.  */
677           handler->discard_body = TRUE;
678         }
679     }
680
681   /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it
682      to call the response handler again. That will start up the XML parsing,
683      or it will be dropped on the floor (per the decision above).  */
684   return SVN_NO_ERROR;
685 }
686
687 svn_error_t *
688 svn_ra_serf__handle_server_error(svn_ra_serf__server_error_t *server_error,
689                                  svn_ra_serf__handler_t *handler,
690                                  serf_request_t *request,
691                                  serf_bucket_t *response,
692                                  apr_status_t *serf_status,
693                                  apr_pool_t *scratch_pool)
694 {
695   svn_error_t *err;
696
697   err = server_error->response_handler(request, response,
698                                        server_error->response_baton,
699                                        scratch_pool);
700  /* If we do not receive an error or it is a non-transient error, return
701      immediately.
702
703      APR_EOF will be returned when parsing is complete.
704
705      APR_EAGAIN & WAIT_CONN may be intermittently returned as we proceed through
706      parsing and the network has no more data right now.  If we receive that,
707      clear the error and return - allowing serf to wait for more data.
708      */
709   if (!err || SERF_BUCKET_READ_ERROR(err->apr_err))
710     return svn_error_trace(err);
711
712   if (!APR_STATUS_IS_EOF(err->apr_err))
713     {
714       *serf_status = err->apr_err;
715       svn_error_clear(err);
716       return SVN_NO_ERROR;
717     }
718
719   /* Clear the EOF. We don't need it as subversion error.  */
720   svn_error_clear(err);
721   *serf_status = APR_EOF;
722
723   /* On PROPPATCH we always get status 207, which may or may not imply an
724      error status, but let's keep it generic and just do the check for
725      any multistatus */
726   if (handler->sline.code == 207 /* MULTISTATUS */)
727     {
728       svn_boolean_t have_error = FALSE;
729       int i;
730
731       for (i = 0; i < server_error->items->nelts; i++)
732         {
733           const error_item_t *item;
734           item = APR_ARRAY_IDX(server_error->items, i, error_item_t *);
735
736           if (!item->apr_err && item->http_status == 200)
737             {
738               continue; /* Success code */
739             }
740
741           have_error = TRUE;
742           break;
743         }
744
745       if (! have_error)
746         handler->server_error = NULL; /* We didn't have a server error */
747     }
748
749   return SVN_NO_ERROR;
750 }