61d16acdb4c74d424cd84cd81f4ad1c000097093
[xmlrpc-c] / lib / libwww_transport / xmlrpc_libwww_transport.c
1 /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
2 **
3 ** Redistribution and use in source and binary forms, with or without
4 ** modification, are permitted provided that the following conditions
5 ** are met:
6 ** 1. Redistributions of source code must retain the above copyright
7 **    notice, this list of conditions and the following disclaimer.
8 ** 2. Redistributions in binary form must reproduce the above copyright
9 **    notice, this list of conditions and the following disclaimer in the
10 **    documentation and/or other materials provided with the distribution.
11 ** 3. The name of the author may not be used to endorse or promote products
12 **    derived from this software without specific prior written permission. 
13 **  
14 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 ** SUCH DAMAGE. */
25
26 #include "xmlrpc_config.h"
27
28 #include <stddef.h>
29
30 #include "bool.h"
31 #include "mallocvar.h"
32
33 #include "xmlrpc-c/base.h"
34 #include "xmlrpc-c/base_int.h"
35 #include "xmlrpc-c/string_int.h"
36 #include "xmlrpc-c/client.h"
37 #include "xmlrpc-c/client_int.h"
38
39 /* The libwww interface */
40
41 /* These headers mistakenly define the macro PACKAGE.  As
42    xmlrpc_config.h already defines PACKAGE according to the package we're
43    actually part of, this causes a conflict.  So we undef here and then
44    to avoid possible problems with an incorrect PACKAGE, we undef it again
45    after.
46 */
47 #undef PACKAGE
48 #include "WWWLib.h"
49 #include "WWWHTTP.h"
50 #include "WWWInit.h"
51 #undef PACKAGE
52
53 /* Include our libwww SSL headers, if available. */
54 #if HAVE_LIBWWW_SSL
55 #include "WWWSSL.h"
56 #endif
57
58 #include "xmlrpc_libwww_transport.h"
59
60 /* This value was discovered by Rick Blair. His efforts shaved two seconds
61 ** off of every request processed. Many thanks. */
62 #define SMALLEST_LEGAL_LIBWWW_TIMEOUT (21)
63
64 #define XMLRPC_CLIENT_USE_TIMEOUT   (2)
65
66
67 struct xmlrpc_client_transport {
68     int saved_flags;
69     HTList *xmlrpc_conversions;
70     void * cookieJarP;
71         /* This is a collection of all the cookies that servers have set
72            via responses to prior requests.  It's not implemented today.
73         */
74     bool tracingOn;
75 };
76
77 static struct xmlrpc_client_transport clientTransport;
78
79
80 typedef struct {
81 /*----------------------------------------------------------------------------
82    This object represents one RPC.
83 -----------------------------------------------------------------------------*/
84     struct xmlrpc_client_transport * clientTransportP;
85
86     /* These fields are used when performing synchronous calls. */
87     bool is_done;
88     int http_status;
89
90     /* Low-level information used by libwww. */
91     HTRequest *      request;
92     HTChunk *        response_data;
93     HTParentAnchor * source_anchor;
94     HTAnchor *       dest_anchor;
95
96     xmlrpc_transport_asynch_complete complete;
97     struct xmlrpc_call_info * callInfoP; 
98 } rpc;
99
100
101
102 static void
103 createCookieJar(xmlrpc_env * const envP ATTR_UNUSED,
104                 void **      const cookieJarP ATTR_UNUSED) {
105
106     /* Cookies not implemented yet */
107 }
108
109
110
111 static void
112 destroyCookieJar(void * cookieJarP ATTR_UNUSED) {
113
114     /* Cookies not implemented yet */
115 }
116
117
118
119 static void
120 initLibwww(const char * const appname,
121            const char * const appversion) {
122     
123     /* We initialize the library using a robot profile, because we don't
124     ** care about redirects or HTTP authentication, and we want to
125     ** reduce our application footprint as much as possible. */
126     HTProfile_newRobot(appname, appversion);
127
128     /* Ilya Goldberg <igg@mit.edu> provided the following code to access
129     ** SSL-protected servers. */
130 #if HAVE_LIBWWW_SSL
131     /* Set the SSL protocol method. By default, it is the highest
132     ** available protocol. Setting it up to SSL_V23 allows the client
133     ** to negotiate with the server and set up either TSLv1, SSLv3,
134     ** or SSLv2 */
135     HTSSL_protMethod_set(HTSSL_V23);
136     
137     /* Set the certificate verification depth to 2 in order to be able to
138     ** validate self-signed certificates */
139     HTSSL_verifyDepth_set(2);
140     
141     /* Register SSL stuff for handling ssl access. The parameter we pass
142     ** is NO because we can't be pre-emptive with POST */
143     HTSSLhttps_init(NO);
144 #endif /* HAVE_LIBWWW_SSL */
145     
146     /* For interoperability with Frontier, we need to tell libwww *not*
147     ** to send 'Expect: 100-continue' headers. But if we're not sending
148     ** these, we shouldn't wait for them. So set our built-in delays to
149     ** the smallest legal values. */
150     HTTP_setBodyWriteDelay (SMALLEST_LEGAL_LIBWWW_TIMEOUT,
151                             SMALLEST_LEGAL_LIBWWW_TIMEOUT);
152     
153     /* We attempt to disable all of libwww's chatty, interactive
154     ** prompts. Let's hope this works. */
155     HTAlert_setInteractive(NO);
156     
157     /* Here are some alternate setup calls which will help greatly
158     ** with debugging, should the need arise.
159     **
160     ** HTProfile_newNoCacheClient(appname, appversion);
161     ** HTAlert_setInteractive(YES);
162     ** HTPrint_setCallback(printer);
163     ** HTTrace_setCallback(tracer); */
164 }
165
166
167
168 static void 
169 create(xmlrpc_env *                      const envP,
170        int                               const flags,
171        const char *                      const appname,
172        const char *                      const appversion,
173        const struct xmlrpc_xportparms *  const transportParmsP ATTR_UNUSED,
174        size_t                            const parm_size ATTR_UNUSED,
175        struct xmlrpc_client_transport ** const handlePP) {
176 /*----------------------------------------------------------------------------
177    This does the 'create' operation for a Libwww client transport.
178 -----------------------------------------------------------------------------*/
179     /* The Libwww transport is not re-entrant -- you can have only one
180        per program instance.  Even if we changed the Xmlrpc-c code not
181        to use global variables, that wouldn't help because Libwww
182        itself is not re-entrant.  
183        
184        So we use a global variable ('clientTransport') for our transport state.
185     */
186     struct xmlrpc_client_transport * const clientTransportP = &clientTransport;
187     *handlePP = clientTransportP;
188
189     clientTransportP->saved_flags = flags;
190
191     createCookieJar(envP, &clientTransportP->cookieJarP);
192     if (!envP->fault_occurred) {
193         if (!(clientTransportP->saved_flags & 
194               XMLRPC_CLIENT_SKIP_LIBWWW_INIT))
195             initLibwww(appname, appversion);
196
197         /* Set up our list of conversions for XML-RPC requests. This is a
198         ** massively stripped-down version of the list in libwww's HTInit.c.
199         ** XXX - This is hackish; 10.0 is an arbitrary, large quality factor
200         ** designed to override the built-in converter for XML. */
201         clientTransportP->xmlrpc_conversions = HTList_new();
202         HTConversion_add(clientTransportP->xmlrpc_conversions, 
203                          "text/xml", "*/*",
204                          HTThroughLine, 10.0, 0.0, 0.0);
205
206         if (envP->fault_occurred)
207             destroyCookieJar(clientTransportP->cookieJarP);
208     }
209     if (getenv("XMLRPC_LIBWWW_TRACE"))
210         clientTransportP->tracingOn = TRUE;
211     else
212         clientTransportP->tracingOn = FALSE;
213 }
214
215
216
217 static void 
218 destroy(struct xmlrpc_client_transport * const clientTransportP) {
219 /*----------------------------------------------------------------------------
220    This does the 'destroy' operation for a Libwww client transport.
221 -----------------------------------------------------------------------------*/
222     XMLRPC_ASSERT(clientTransportP != NULL);
223
224     if (!(clientTransportP->saved_flags & XMLRPC_CLIENT_SKIP_LIBWWW_INIT)) {
225         HTProfile_delete();
226     }
227     destroyCookieJar(clientTransportP->cookieJarP);
228 }
229
230
231
232 /*=========================================================================
233 **  HTTP Error Reporting
234 **=======================================================================*/
235
236 static void
237 formatLibwwwError(HTRequest *   const requestP,
238                   const char ** const msgP) {
239 /*----------------------------------------------------------------------------
240    When something fails in a Libwww request, Libwww generates a stack
241    of error information (precious little information, of course, in the
242    Unix tradition) and attaches it to the request object.  We make a message
243    out of that information.
244
245    We rely on Libwww's HTDialog_errorMessage() to do the bulk of the
246    formatting; we might be able to coax more information out of the request
247    if we interpreted the error stack directly.
248 -----------------------------------------------------------------------------*/
249     HTList * const errStack = HTRequest_error(requestP);
250     
251     if (errStack == NULL)
252         xmlrpc_asprintf(msgP, "Libwww supplied no error details");
253     else {
254         /* Get an error message from libwww.  The middle three
255            parameters to HTDialog_errorMessage appear to be ignored.
256            XXX - The documentation for this API is terrible, so we may
257            be using it incorrectly.  
258         */
259         const char * msg =
260             HTDialog_errorMessage(requestP, HT_A_MESSAGE, HT_MSG_NULL,
261                                   "An error occurred", errStack);
262         
263         if (msg == NULL)
264             xmlrpc_asprintf(msgP, "Libwww supplied some error detail, "
265                             "but its HTDialog_errorMessage() subroutine "
266                             "mysteriously failed to interpret it for us.");
267         else
268             *msgP = msg;
269     }
270 }
271
272
273
274 static void 
275 set_fault_from_http_request(xmlrpc_env * const envP,
276                             int          const status,
277                             HTRequest *  const requestP) {
278 /*----------------------------------------------------------------------------
279    Assuming 'requestP' identifies a completed libwww HTTP request, set
280    *envP according to its success/error status.
281 -----------------------------------------------------------------------------*/
282     XMLRPC_ASSERT_PTR_OK(requestP);
283
284     if (status == 200) {
285         /* No error.  Don't set one in *envP */
286     } else {
287         const char * libwwwMsg;
288         formatLibwwwError(requestP, &libwwwMsg);
289
290         if (status == -1)
291             xmlrpc_env_set_fault_formatted(
292                 envP, XMLRPC_NETWORK_ERROR,
293                 "Unable to complete the HTTP request.  %s", libwwwMsg);
294         else {
295             xmlrpc_env_set_fault_formatted(
296                 envP, XMLRPC_NETWORK_ERROR,
297                 "HTTP request completed with HTTp error %d.  %s",
298                 status, libwwwMsg);
299         }
300         xmlrpc_strfree(libwwwMsg);
301     }
302 }
303
304
305
306 static BOOL 
307 setCookie(HTRequest * const request,
308           HTCookie *  const cookieP ATTR_UNUSED,
309           void *      const param ATTR_UNUSED) {
310 /*----------------------------------------------------------------------------
311   This is the callback from libwww to tell us the server (according to
312   its response) wants us to store a cookie (and include it in future
313   requests).
314
315   We assume that the cookies "domain" is the server's host name
316   (there are options on the libwww connection to make libwww call this
317   callback only when that's the case).
318 -----------------------------------------------------------------------------*/
319     rpc * const rpcP = HTRequest_context(request);
320     struct xmlrpc_client_transport * const clientTransportP = 
321         rpcP->clientTransportP;
322
323     BOOL retval;
324
325     /* Avoid unused variable warning */
326     if (clientTransportP->cookieJarP == clientTransportP->cookieJarP) {}
327     /* Cookies are not implemented today */
328     retval = NO;
329
330     return retval;
331 }
332
333
334
335 static HTAssocList *
336 cookiesForHost(const char * const host ATTR_UNUSED,
337                void *       const cookieJarP ATTR_UNUSED) {
338 /*----------------------------------------------------------------------------
339   Find and return all the cookies in jar 'cookieJarP' that are for the
340   host 'host'.
341 -----------------------------------------------------------------------------*/
342     HTAssocList * hisCookiesP;
343
344     hisCookiesP = HTAssocList_new();
345
346     if (hisCookiesP) {
347         /* Cookies are not implemented yet */
348         /* Library/Examples/cookie.c in the w3c-libwww source tree contains
349            an example of constructing the cookie list we are supposed to
350            return.  But today, we return an empty list.
351         */
352     }
353     return hisCookiesP;
354 }
355
356
357
358 static HTAssocList *
359 findCookie(HTRequest * const request,
360            void *      const param ATTR_UNUSED) {
361 /*----------------------------------------------------------------------------
362   This is the callback from libwww to get the cookies to include in a
363   request (presumably values the server set via a prior response).
364 -----------------------------------------------------------------------------*/
365     rpc * const rpcP = HTRequest_context(request);
366     struct xmlrpc_client_transport * const clientTransportP = 
367         rpcP->clientTransportP;
368     const char * const addr = 
369         HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
370     const char * const host = HTParse(addr, "", PARSE_HOST);
371
372     return cookiesForHost(host, clientTransportP->cookieJarP);
373 }
374
375
376
377 static void 
378 deleteSourceAnchor(HTParentAnchor * const anchor) {
379
380     /* We need to clear the document first, or else libwww won't
381     ** really delete the anchor. */
382     HTAnchor_setDocument(anchor, NULL);
383
384     /* XXX - Deleting this anchor causes HTLibTerminate to dump core. */
385     /* HTAnchor_delete(anchor); */
386 }
387
388
389
390 static void
391 createSourceAnchor(xmlrpc_env *       const envP,
392                    HTParentAnchor **  const sourceAnchorPP,
393                    xmlrpc_mem_block * const xmlP) {
394
395     HTParentAnchor * const sourceAnchorP = HTTmpAnchor(NULL);
396
397     if (sourceAnchorP == NULL)
398         xmlrpc_env_set_fault_formatted(
399             envP, XMLRPC_INTERNAL_ERROR, 
400             "Unable to build source anchor.  HTTmpAnchor() failed.");
401     else {
402         HTAnchor_setDocument(sourceAnchorP,
403                              XMLRPC_MEMBLOCK_CONTENTS(char, xmlP));
404         HTAnchor_setFormat(sourceAnchorP, HTAtom_for("text/xml"));
405         HTAnchor_setLength(sourceAnchorP, XMLRPC_MEMBLOCK_SIZE(char, xmlP));
406
407         *sourceAnchorPP = sourceAnchorP;
408     }
409 }
410
411
412
413 static void
414 createDestAnchor(xmlrpc_env *               const envP,
415                  HTAnchor **                const destAnchorPP,
416                  const xmlrpc_server_info * const serverP) {
417                  
418     *destAnchorPP = HTAnchor_findAddress(serverP->_server_url);
419
420     if (*destAnchorPP == NULL)
421         xmlrpc_env_set_fault_formatted(
422             envP, XMLRPC_INTERNAL_ERROR,
423             "Could not build destination anchor.  HTAnchor_findAddress() "
424             "failed.");
425 }
426
427
428
429 static void
430 rpcCreate(xmlrpc_env *                       const envP,
431           struct xmlrpc_client_transport *   const clientTransportP,
432           const xmlrpc_server_info *         const serverP,
433           xmlrpc_mem_block *                 const xmlP,
434           xmlrpc_transport_asynch_complete         complete, 
435           struct xmlrpc_call_info *          const callInfoP,
436           rpc **                             const rpcPP) {
437
438     rpc *rpcP;
439     HTRqHd request_headers;
440     HTStream *target_stream;
441
442     /* Allocate our structure. */
443     MALLOCVAR(rpcP);
444     XMLRPC_FAIL_IF_NULL(rpcP, envP, XMLRPC_INTERNAL_ERROR,
445                         "Out of memory in rpcCreate()");
446
447     /* Set up our basic members. */
448     rpcP->clientTransportP = clientTransportP;
449     rpcP->is_done = FALSE;
450     rpcP->http_status = 0;
451     rpcP->complete = complete;
452     rpcP->callInfoP = callInfoP;
453
454     /* Start cookie handler. */
455     HTCookie_init();
456     HTCookie_setCallbacks(setCookie, NULL, findCookie, NULL);
457     HTCookie_setCookieMode(HT_COOKIE_ACCEPT | 
458                            HT_COOKIE_SEND | 
459                            HT_COOKIE_SAME_HOST);
460
461     /* Cookies aren't implemented today; reset. */
462     HTCookie_setCookieMode(0);
463     
464     /* Create a HTRequest object. */
465     rpcP->request = HTRequest_new();
466     XMLRPC_FAIL_IF_NULL(rpcP, envP, XMLRPC_INTERNAL_ERROR,
467                         "HTRequest_new failed");
468     
469     /* Install ourselves as the request context. */
470     HTRequest_setContext(rpcP->request, rpcP);
471
472     /* XXX - Disable the 'Expect:' header so we can talk to Frontier. */
473     request_headers = HTRequest_rqHd(rpcP->request);
474     request_headers = request_headers & ~HT_C_EXPECT;
475     HTRequest_setRqHd(rpcP->request, request_headers);
476
477     /* Send an authorization header if we need one. */
478     if (serverP->_http_basic_auth)
479         HTRequest_addCredentials(rpcP->request, "Authorization",
480                                  serverP->_http_basic_auth);
481     
482     /* Make sure there is no XML conversion handler to steal our data.
483     ** The 'override' parameter is currently ignored by libwww, so our
484     ** list of conversions must be designed to co-exist with the built-in
485     ** conversions. */
486     HTRequest_setConversion(rpcP->request, 
487                             clientTransportP->xmlrpc_conversions, NO);
488
489     /* Set up our response buffer. */
490     target_stream = HTStreamToChunk(rpcP->request, &rpcP->response_data, 0);
491     XMLRPC_FAIL_IF_NULL(rpcP->response_data, envP, XMLRPC_INTERNAL_ERROR,
492                         "HTStreamToChunk failed");
493     XMLRPC_ASSERT(target_stream != NULL);
494     HTRequest_setOutputStream(rpcP->request, target_stream);
495     HTRequest_setOutputFormat(rpcP->request, WWW_SOURCE);
496
497     createSourceAnchor(envP, &rpcP->source_anchor, xmlP);
498
499     if (!envP->fault_occurred) {
500         createDestAnchor(envP, &rpcP->dest_anchor, serverP);
501
502         if (envP->fault_occurred)
503             /* See below for comments about deleting the source and dest
504             ** anchors. This is a bit of a black art. */
505             deleteSourceAnchor(rpcP->source_anchor);
506     }
507     
508  cleanup:
509     if (envP->fault_occurred) {
510         if (rpcP) {
511             if (rpcP->request)
512                 HTRequest_delete(rpcP->request);
513             if (rpcP->response_data)
514                 HTChunk_delete(rpcP->response_data);
515             free(rpcP);
516         }
517     }
518     *rpcPP = rpcP;
519 }
520
521
522
523 static void 
524 rpcDestroy(rpc * const rpcP) {
525
526     XMLRPC_ASSERT_PTR_OK(rpcP);
527     XMLRPC_ASSERT(rpcP->request != XMLRPC_BAD_POINTER);
528     XMLRPC_ASSERT(rpcP->response_data != XMLRPC_BAD_POINTER);
529
530     /* Junji Kanemaru reports on 05.04.11 that with asynch calls, he
531        get a segfault, and reversing the order of deleting the request
532        and the response chunk buffer cured it.  But we find no reason
533        that should be so, so we're waiting for someone to arrive at an
534        explanation before changing anything.  HTRequest_delete() does
535        destroy the output stream, and the output stream refers to the
536        response chunk, but HTRequest_delete() explicitly refrains from
537        destroying the response chunk.  And the response chunk does not
538        refer to the request.
539     */
540
541     HTRequest_delete(rpcP->request);
542     rpcP->request = XMLRPC_BAD_POINTER;
543     HTChunk_delete(rpcP->response_data);
544     rpcP->response_data = XMLRPC_BAD_POINTER;
545
546     /* This anchor points to private data, so we're allowed to delete it.  */
547     deleteSourceAnchor(rpcP->source_anchor);
548
549     /* WARNING: We can't delete the destination anchor, because this points
550     ** to something in the outside world, and lives in a libwww hash table.
551     ** Under certain circumstances, this anchor may have been reissued to
552     ** somebody else. So over time, the anchor cache will grow. If this
553     ** is a problem for your application, read the documentation for
554     ** HTAnchor_deleteAll.
555     **
556     ** However, we CAN check to make sure that no documents have been
557     ** attached to the anchor. This assertion may fail if you're using
558     ** libwww for something else, so please feel free to comment it out. */
559     /* XMLRPC_ASSERT(HTAnchor_document(rpcP->dest_anchor) == NULL);
560      */
561
562     HTCookie_deleteCallbacks();
563     HTCookie_terminate();
564
565     free(rpcP);
566 }
567
568
569
570 static void
571 extract_response_chunk(xmlrpc_env *        const envP,
572                        rpc *               const rpcP,
573                        xmlrpc_mem_block ** const responseXmlPP) {
574
575     /* Check to make sure that w3c-libwww actually sent us some data.
576     ** XXX - This may happen if libwww is shut down prematurely, believe it
577     ** or not--we'll get a 200 OK and no data. Gag me with a bogus design
578     ** decision. This may also fail if some naughty libwww converter
579     ** ate our data unexpectedly. */
580     if (!HTChunk_data(rpcP->response_data))
581         xmlrpc_env_set_fault(envP, XMLRPC_NETWORK_ERROR,
582                              "w3c-libwww returned no data");
583     else {
584         *responseXmlPP = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
585         if (!envP->fault_occurred) {
586             if (rpcP->clientTransportP->tracingOn) {
587                 fprintf(stderr, "HTTP chunk received: %u bytes: '%.*s'",
588                         HTChunk_size(rpcP->response_data),
589                         HTChunk_size(rpcP->response_data),
590                         HTChunk_data(rpcP->response_data));
591             }
592
593             XMLRPC_MEMBLOCK_APPEND(char, envP, *responseXmlPP,
594                                    HTChunk_data(rpcP->response_data),
595                                    HTChunk_size(rpcP->response_data));
596             if (envP->fault_occurred)
597                 XMLRPC_MEMBLOCK_FREE(char, *responseXmlPP);
598         }
599     }
600 }
601
602
603
604 static int 
605 synch_terminate_handler(HTRequest *  const request,
606                         HTResponse * const response ATTR_UNUSED,
607                         void *       const param ATTR_UNUSED,
608                         int          const status) {
609 /*----------------------------------------------------------------------------
610    This is a libwww request completion handler.
611
612    HTEventList_newLoop() calls this when it completes a request (with this
613    registered as the completion handler).
614 -----------------------------------------------------------------------------*/
615     rpc *rpcP;
616
617     rpcP = HTRequest_context(request);
618
619     rpcP->is_done = TRUE;
620     rpcP->http_status = status;
621
622     HTEventList_stopLoop();
623
624     return HT_OK;
625 }
626
627
628
629 static void
630 call(xmlrpc_env *                     const envP,
631      struct xmlrpc_client_transport * const clientTransportP,
632      const xmlrpc_server_info *       const serverP,
633      xmlrpc_mem_block *               const xmlP,
634      xmlrpc_mem_block **              const responsePP) {
635 /*----------------------------------------------------------------------------
636    This does the 'call' operation for a Libwww client transport.
637 -----------------------------------------------------------------------------*/
638     rpc * rpcP;
639
640     XMLRPC_ASSERT_ENV_OK(envP);
641     XMLRPC_ASSERT_PTR_OK(serverP);
642     XMLRPC_ASSERT_PTR_OK(xmlP);
643     XMLRPC_ASSERT_PTR_OK(responsePP);
644
645     rpcCreate(envP, clientTransportP, serverP, xmlP, NULL, NULL, &rpcP);
646     if (!envP->fault_occurred) {
647         int ok;
648         
649         /* Install our request handler. */
650         HTRequest_addAfter(rpcP->request, &synch_terminate_handler, 
651                            NULL, NULL, HT_ALL, HT_FILTER_LAST, NO);
652
653         /* Start our request running. */
654         ok = HTPostAnchor(rpcP->source_anchor, 
655                           rpcP->dest_anchor, 
656                           rpcP->request);
657         if (!ok)
658             xmlrpc_env_set_fault(
659                 envP, XMLRPC_NETWORK_ERROR,
660                 "Libwww HTPostAnchor() failed to start POST request");
661         else {
662             /* Run our event-processing loop.  HTEventList_newLoop()
663                is what calls synch_terminate_handler(), by virtue of
664                it being registered as a handler.  It may return for
665                other reasons than the request being complete, though.
666                so we call it in a loop until synch_terminate_handler()
667                really has been called.
668             */
669             while (!rpcP->is_done)
670                 HTEventList_newLoop();
671         
672             /* Fail if we didn't get a "200 OK" response from the server */
673             if (rpcP->http_status != 200)
674                 set_fault_from_http_request(
675                     envP, rpcP->http_status, 
676                     rpcP->request);
677             else {
678                 /* XXX - Check to make sure response type is text/xml here. */
679                 
680                 extract_response_chunk(envP, rpcP, responsePP);
681             }
682         }
683         rpcDestroy(rpcP);
684     }
685 }
686
687
688
689 /*=========================================================================
690 **  Event Loop
691 **=========================================================================
692 **  We manage a fair bit of internal state about our event loop. This is
693 **  needed to determine when (and if) we should exit the loop.
694 */
695
696 static int outstanding_asynch_calls = 0;
697 static int event_loop_flags = 0;
698 static int timer_called = 0;
699
700 static void 
701 register_asynch_call(void) {
702     XMLRPC_ASSERT(outstanding_asynch_calls >= 0);
703     outstanding_asynch_calls++;
704 }
705
706
707
708 static void 
709 unregister_asynch_call(void) {
710
711     XMLRPC_ASSERT(outstanding_asynch_calls > 0);
712     outstanding_asynch_calls--;
713     if (outstanding_asynch_calls == 0)
714         HTEventList_stopLoop();
715 }
716
717
718
719 static int 
720 timer_callback(HTTimer *   const timer     ATTR_UNUSED,
721                void *      const user_data ATTR_UNUSED,
722                HTEventType const event     ATTR_UNUSED) {
723 /*----------------------------------------------------------------------------
724   A handy timer callback which cancels the running event loop. 
725 -----------------------------------------------------------------------------*/
726     XMLRPC_ASSERT(event == HTEvent_TIMEOUT);
727     timer_called = 1;
728     HTEventList_stopLoop();
729     
730     /* XXX - The meaning of this return value is undocumented, but close
731     ** inspection of libwww's source suggests that we want to return HT_OK. */
732     return HT_OK;
733 }
734
735
736
737 static void 
738 eventLoopRun(int            const flags, 
739              xmlrpc_timeout const milliseconds) {
740 /*----------------------------------------------------------------------------
741    Process all responses from outstanding requests as they come in.
742    Return when there are no more outstanding responses.
743
744    Or, if 'flags' has the XMLRPC_CLIENT_USE_TIMEOUT flag set, return
745    when 'milliseconds' milliseconds have elapsed, regardless of whether
746    there are still outstanding responses.
747
748    The processing we do consists of telling libwww to process the
749    completion of the libwww request.  That normally includes calling
750    the xmlrpc_libwww_transport request termination handler, because
751    the submitter of the libwww request would have registered that as a
752    callback.
753 -----------------------------------------------------------------------------*/
754     if (outstanding_asynch_calls > 0) {
755         HTTimer *timer;
756
757         event_loop_flags = flags;
758         
759         /* Run an appropriate event loop.  The HTEeventList_newLoop()
760            is what calls asynch_terminate_handler(), by virtue of it
761            being registered as a handler.
762         */
763         if (event_loop_flags & XMLRPC_CLIENT_USE_TIMEOUT) {
764             
765             /* Run our event loop with a timer. Note that we need to be very
766             ** careful about race conditions--timers can be fired in either
767             ** HTimer_new or HTEventList_newLoop. And if our callback were to
768             ** get called before we entered the loop, we would never exit.
769             ** So we use a private flag of our own--we can't even rely on
770             ** HTTimer_hasTimerExpired, because that only checks the time,
771             ** not whether our callback has been run. Yuck. */
772             timer_called = 0;
773             timer = HTTimer_new(NULL, &timer_callback, NULL,
774                                 milliseconds, YES, NO);
775             XMLRPC_ASSERT(timer != NULL);
776             if (!timer_called)
777                 HTEventList_newLoop();
778             HTTimer_delete(timer);
779             
780         } else {
781             /* Run our event loop without a timer. */
782             HTEventList_newLoop();
783         }
784         
785         /* Reset our flags, so we don't interfere with direct calls to the
786         ** libwww event loop functions. */
787         event_loop_flags = 0;
788     } else {
789         /* There are *no* calls to process.  This may mean that none
790            of the asynch calls were ever set up, and the client's
791            callbacks have already been called with an error, or that
792            all outstanding calls were completed during a previous
793            synchronous call.  
794         */
795     }
796 }
797
798
799
800 static void 
801 finishAsynch(
802     struct xmlrpc_client_transport * const clientTransportP ATTR_UNUSED,
803     xmlrpc_timeoutType               const timeoutType,
804     xmlrpc_timeout                   const timeout) {
805 /*----------------------------------------------------------------------------
806    This does the 'finish_asynch' operation for a Libwww client transport.
807 -----------------------------------------------------------------------------*/
808     eventLoopRun(timeoutType == timeout_yes ? XMLRPC_CLIENT_USE_TIMEOUT : 0,
809                  timeout);
810 }
811
812
813
814 static int 
815 asynch_terminate_handler(HTRequest *  const request,
816                          HTResponse * const response ATTR_UNUSED,
817                          void *       const param ATTR_UNUSED,
818                          int          const status) {
819 /*----------------------------------------------------------------------------
820    Handle the completion of a libwww request.
821
822    This is the bottom half of the xmlrpc_libwww_transport asynchronous
823    call dispatcher.  It's what the dispatcher registers with libwww as
824    a "local after filter" so that libwww calls it when a request that
825    xmlrpc_libwww_transport submitted to it is complete.
826
827    We destroy the RPC, including the request which is our argument.
828    Strange as that may seem, it is apparently legal for an after filter
829    to destroy the request that was passed to it -- or not.
830 -----------------------------------------------------------------------------*/
831     xmlrpc_env env;
832     rpc * rpcP;
833     xmlrpc_mem_block * responseXmlP;
834
835     XMLRPC_ASSERT_PTR_OK(request);
836
837     xmlrpc_env_init(&env);
838
839     rpcP = HTRequest_context(request);
840
841     /* Unregister this call from the event loop. Among other things, this
842     ** may decide to stop the event loop.
843     **/
844     unregister_asynch_call();
845
846     /* Give up if an error occurred. */
847     if (status != 200)
848         set_fault_from_http_request(&env, status, request);
849     else {
850         /* XXX - Check to make sure response type is text/xml here. */
851         extract_response_chunk(&env, rpcP, &responseXmlP);
852     }
853     rpcP->complete(rpcP->callInfoP, responseXmlP, env);
854
855     if (!env.fault_occurred)
856         XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
857
858     rpcDestroy(rpcP);
859     
860     xmlrpc_env_clean(&env);
861     return HT_OK;
862 }
863
864
865
866 static void 
867 sendRequest(xmlrpc_env *                     const envP, 
868             struct xmlrpc_client_transport * const clientTransportP,
869             const xmlrpc_server_info *       const serverP,
870             xmlrpc_mem_block *               const xmlP,
871             xmlrpc_transport_asynch_complete       complete,
872             struct xmlrpc_call_info *        const callInfoP) {
873 /*----------------------------------------------------------------------------
874    Initiate an XML-RPC rpc asynchronously.  Don't wait for it to go to
875    the server.
876
877    Unless we return failure, we arrange to have complete() called when
878    the rpc completes.
879
880    This does the 'send_request' operation for a Libwww client transport.
881 -----------------------------------------------------------------------------*/
882     rpc * rpcP;
883
884     XMLRPC_ASSERT_PTR_OK(envP);
885     XMLRPC_ASSERT_PTR_OK(serverP);
886     XMLRPC_ASSERT_PTR_OK(xmlP);
887     XMLRPC_ASSERT_PTR_OK(callInfoP);
888
889     rpcCreate(envP, clientTransportP, serverP, xmlP, complete, callInfoP, 
890               &rpcP);
891     if (!envP->fault_occurred) {
892         int ok;
893
894         /* Install our request handler. */
895         HTRequest_addAfter(rpcP->request, &asynch_terminate_handler, 
896                            NULL, NULL, HT_ALL, HT_FILTER_LAST, NO);
897
898         /* Register our asynchronous call with the event loop.  This means
899            the user's callback is guaranteed to be called eventually.
900         */
901         register_asynch_call();
902
903         /* This makes the TCP connection and sends the XML to the server
904            as an HTTP POST request.
905
906            There was a comment here that said this might return failure
907            (!ok) and still invoke our completion handler
908            (asynch_terminate_handler().  The code attempted to deal with
909            that.  Well, it's impossible to deal with that, so if it really
910            happens, we must fix Libwww.  -Bryan 04.11.23.
911         */
912
913         ok = HTPostAnchor(rpcP->source_anchor, 
914                           rpcP->dest_anchor, 
915                           rpcP->request);
916         if (!ok) {
917             unregister_asynch_call();
918             xmlrpc_env_set_fault(envP, XMLRPC_NETWORK_ERROR,
919                                  "Libwww (HTPostAnchor()) failed to start the "
920                                  "POST request.");
921         }
922         if (envP->fault_occurred)
923             rpcDestroy(rpcP);
924     }
925 }
926
927
928
929 struct xmlrpc_client_transport_ops xmlrpc_libwww_transport_ops = {
930     NULL,
931     NULL,
932     &create,
933     &destroy,
934     &sendRequest,
935     &call,
936     &finishAsynch,
937 };