1 /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
3 ** Redistribution and use in source and binary forms, with or without
4 ** modification, are permitted provided that the following conditions
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.
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
26 #include "xmlrpc_config.h"
31 #include "mallocvar.h"
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"
39 /* The libwww interface */
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
53 /* Include our libwww SSL headers, if available. */
58 #include "xmlrpc_libwww_transport.h"
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)
64 #define XMLRPC_CLIENT_USE_TIMEOUT (2)
67 struct xmlrpc_client_transport {
69 HTList *xmlrpc_conversions;
71 /* This is a collection of all the cookies that servers have set
72 via responses to prior requests. It's not implemented today.
77 static struct xmlrpc_client_transport clientTransport;
81 /*----------------------------------------------------------------------------
82 This object represents one RPC.
83 -----------------------------------------------------------------------------*/
84 struct xmlrpc_client_transport * clientTransportP;
86 /* These fields are used when performing synchronous calls. */
90 /* Low-level information used by libwww. */
92 HTChunk * response_data;
93 HTParentAnchor * source_anchor;
94 HTAnchor * dest_anchor;
96 xmlrpc_transport_asynch_complete complete;
97 struct xmlrpc_call_info * callInfoP;
103 createCookieJar(xmlrpc_env * const envP ATTR_UNUSED,
104 void ** const cookieJarP ATTR_UNUSED) {
106 /* Cookies not implemented yet */
112 destroyCookieJar(void * cookieJarP ATTR_UNUSED) {
114 /* Cookies not implemented yet */
120 initLibwww(const char * const appname,
121 const char * const appversion) {
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);
128 /* Ilya Goldberg <igg@mit.edu> provided the following code to access
129 ** SSL-protected servers. */
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,
135 HTSSL_protMethod_set(HTSSL_V23);
137 /* Set the certificate verification depth to 2 in order to be able to
138 ** validate self-signed certificates */
139 HTSSL_verifyDepth_set(2);
141 /* Register SSL stuff for handling ssl access. The parameter we pass
142 ** is NO because we can't be pre-emptive with POST */
144 #endif /* HAVE_LIBWWW_SSL */
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);
153 /* We attempt to disable all of libwww's chatty, interactive
154 ** prompts. Let's hope this works. */
155 HTAlert_setInteractive(NO);
157 /* Here are some alternate setup calls which will help greatly
158 ** with debugging, should the need arise.
160 ** HTProfile_newNoCacheClient(appname, appversion);
161 ** HTAlert_setInteractive(YES);
162 ** HTPrint_setCallback(printer);
163 ** HTTrace_setCallback(tracer); */
169 create(xmlrpc_env * const envP,
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.
184 So we use a global variable ('clientTransport') for our transport state.
186 struct xmlrpc_client_transport * const clientTransportP = &clientTransport;
187 *handlePP = clientTransportP;
189 clientTransportP->saved_flags = flags;
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);
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,
204 HTThroughLine, 10.0, 0.0, 0.0);
206 if (envP->fault_occurred)
207 destroyCookieJar(clientTransportP->cookieJarP);
209 if (getenv("XMLRPC_LIBWWW_TRACE"))
210 clientTransportP->tracingOn = TRUE;
212 clientTransportP->tracingOn = FALSE;
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);
224 if (!(clientTransportP->saved_flags & XMLRPC_CLIENT_SKIP_LIBWWW_INIT)) {
227 destroyCookieJar(clientTransportP->cookieJarP);
232 /*=========================================================================
233 ** HTTP Error Reporting
234 **=======================================================================*/
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.
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);
251 if (errStack == NULL)
252 xmlrpc_asprintf(msgP, "Libwww supplied no error details");
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.
260 HTDialog_errorMessage(requestP, HT_A_MESSAGE, HT_MSG_NULL,
261 "An error occurred", errStack);
264 xmlrpc_asprintf(msgP, "Libwww supplied some error detail, "
265 "but its HTDialog_errorMessage() subroutine "
266 "mysteriously failed to interpret it for us.");
275 set_fault_from_http_request(xmlrpc_env * const envP,
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);
285 /* No error. Don't set one in *envP */
287 const char * libwwwMsg;
288 formatLibwwwError(requestP, &libwwwMsg);
291 xmlrpc_env_set_fault_formatted(
292 envP, XMLRPC_NETWORK_ERROR,
293 "Unable to complete the HTTP request. %s", libwwwMsg);
295 xmlrpc_env_set_fault_formatted(
296 envP, XMLRPC_NETWORK_ERROR,
297 "HTTP request completed with HTTp error %d. %s",
300 xmlrpc_strfree(libwwwMsg);
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
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;
325 /* Avoid unused variable warning */
326 if (clientTransportP->cookieJarP == clientTransportP->cookieJarP) {}
327 /* Cookies are not implemented today */
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
341 -----------------------------------------------------------------------------*/
342 HTAssocList * hisCookiesP;
344 hisCookiesP = HTAssocList_new();
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.
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);
372 return cookiesForHost(host, clientTransportP->cookieJarP);
378 deleteSourceAnchor(HTParentAnchor * const anchor) {
380 /* We need to clear the document first, or else libwww won't
381 ** really delete the anchor. */
382 HTAnchor_setDocument(anchor, NULL);
384 /* XXX - Deleting this anchor causes HTLibTerminate to dump core. */
385 /* HTAnchor_delete(anchor); */
391 createSourceAnchor(xmlrpc_env * const envP,
392 HTParentAnchor ** const sourceAnchorPP,
393 xmlrpc_mem_block * const xmlP) {
395 HTParentAnchor * const sourceAnchorP = HTTmpAnchor(NULL);
397 if (sourceAnchorP == NULL)
398 xmlrpc_env_set_fault_formatted(
399 envP, XMLRPC_INTERNAL_ERROR,
400 "Unable to build source anchor. HTTmpAnchor() failed.");
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));
407 *sourceAnchorPP = sourceAnchorP;
414 createDestAnchor(xmlrpc_env * const envP,
415 HTAnchor ** const destAnchorPP,
416 const xmlrpc_server_info * const serverP) {
418 *destAnchorPP = HTAnchor_findAddress(serverP->_server_url);
420 if (*destAnchorPP == NULL)
421 xmlrpc_env_set_fault_formatted(
422 envP, XMLRPC_INTERNAL_ERROR,
423 "Could not build destination anchor. HTAnchor_findAddress() "
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) {
439 HTRqHd request_headers;
440 HTStream *target_stream;
442 /* Allocate our structure. */
444 XMLRPC_FAIL_IF_NULL(rpcP, envP, XMLRPC_INTERNAL_ERROR,
445 "Out of memory in rpcCreate()");
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;
454 /* Start cookie handler. */
456 HTCookie_setCallbacks(setCookie, NULL, findCookie, NULL);
457 HTCookie_setCookieMode(HT_COOKIE_ACCEPT |
459 HT_COOKIE_SAME_HOST);
461 /* Cookies aren't implemented today; reset. */
462 HTCookie_setCookieMode(0);
464 /* Create a HTRequest object. */
465 rpcP->request = HTRequest_new();
466 XMLRPC_FAIL_IF_NULL(rpcP, envP, XMLRPC_INTERNAL_ERROR,
467 "HTRequest_new failed");
469 /* Install ourselves as the request context. */
470 HTRequest_setContext(rpcP->request, rpcP);
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);
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);
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
486 HTRequest_setConversion(rpcP->request,
487 clientTransportP->xmlrpc_conversions, NO);
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);
497 createSourceAnchor(envP, &rpcP->source_anchor, xmlP);
499 if (!envP->fault_occurred) {
500 createDestAnchor(envP, &rpcP->dest_anchor, serverP);
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);
509 if (envP->fault_occurred) {
512 HTRequest_delete(rpcP->request);
513 if (rpcP->response_data)
514 HTChunk_delete(rpcP->response_data);
524 rpcDestroy(rpc * const rpcP) {
526 XMLRPC_ASSERT_PTR_OK(rpcP);
527 XMLRPC_ASSERT(rpcP->request != XMLRPC_BAD_POINTER);
528 XMLRPC_ASSERT(rpcP->response_data != XMLRPC_BAD_POINTER);
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.
541 HTRequest_delete(rpcP->request);
542 rpcP->request = XMLRPC_BAD_POINTER;
543 HTChunk_delete(rpcP->response_data);
544 rpcP->response_data = XMLRPC_BAD_POINTER;
546 /* This anchor points to private data, so we're allowed to delete it. */
547 deleteSourceAnchor(rpcP->source_anchor);
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.
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);
562 HTCookie_deleteCallbacks();
563 HTCookie_terminate();
571 extract_response_chunk(xmlrpc_env * const envP,
573 xmlrpc_mem_block ** const responseXmlPP) {
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");
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));
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);
605 synch_terminate_handler(HTRequest * const request,
606 HTResponse * const response ATTR_UNUSED,
607 void * const param ATTR_UNUSED,
609 /*----------------------------------------------------------------------------
610 This is a libwww request completion handler.
612 HTEventList_newLoop() calls this when it completes a request (with this
613 registered as the completion handler).
614 -----------------------------------------------------------------------------*/
617 rpcP = HTRequest_context(request);
619 rpcP->is_done = TRUE;
620 rpcP->http_status = status;
622 HTEventList_stopLoop();
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 -----------------------------------------------------------------------------*/
640 XMLRPC_ASSERT_ENV_OK(envP);
641 XMLRPC_ASSERT_PTR_OK(serverP);
642 XMLRPC_ASSERT_PTR_OK(xmlP);
643 XMLRPC_ASSERT_PTR_OK(responsePP);
645 rpcCreate(envP, clientTransportP, serverP, xmlP, NULL, NULL, &rpcP);
646 if (!envP->fault_occurred) {
649 /* Install our request handler. */
650 HTRequest_addAfter(rpcP->request, &synch_terminate_handler,
651 NULL, NULL, HT_ALL, HT_FILTER_LAST, NO);
653 /* Start our request running. */
654 ok = HTPostAnchor(rpcP->source_anchor,
658 xmlrpc_env_set_fault(
659 envP, XMLRPC_NETWORK_ERROR,
660 "Libwww HTPostAnchor() failed to start POST request");
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.
669 while (!rpcP->is_done)
670 HTEventList_newLoop();
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,
678 /* XXX - Check to make sure response type is text/xml here. */
680 extract_response_chunk(envP, rpcP, responsePP);
689 /*=========================================================================
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.
696 static int outstanding_asynch_calls = 0;
697 static int event_loop_flags = 0;
698 static int timer_called = 0;
701 register_asynch_call(void) {
702 XMLRPC_ASSERT(outstanding_asynch_calls >= 0);
703 outstanding_asynch_calls++;
709 unregister_asynch_call(void) {
711 XMLRPC_ASSERT(outstanding_asynch_calls > 0);
712 outstanding_asynch_calls--;
713 if (outstanding_asynch_calls == 0)
714 HTEventList_stopLoop();
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);
728 HTEventList_stopLoop();
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. */
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.
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.
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
753 -----------------------------------------------------------------------------*/
754 if (outstanding_asynch_calls > 0) {
757 event_loop_flags = flags;
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.
763 if (event_loop_flags & XMLRPC_CLIENT_USE_TIMEOUT) {
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. */
773 timer = HTTimer_new(NULL, &timer_callback, NULL,
774 milliseconds, YES, NO);
775 XMLRPC_ASSERT(timer != NULL);
777 HTEventList_newLoop();
778 HTTimer_delete(timer);
781 /* Run our event loop without a timer. */
782 HTEventList_newLoop();
785 /* Reset our flags, so we don't interfere with direct calls to the
786 ** libwww event loop functions. */
787 event_loop_flags = 0;
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
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,
815 asynch_terminate_handler(HTRequest * const request,
816 HTResponse * const response ATTR_UNUSED,
817 void * const param ATTR_UNUSED,
819 /*----------------------------------------------------------------------------
820 Handle the completion of a libwww request.
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.
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 -----------------------------------------------------------------------------*/
833 xmlrpc_mem_block * responseXmlP;
835 XMLRPC_ASSERT_PTR_OK(request);
837 xmlrpc_env_init(&env);
839 rpcP = HTRequest_context(request);
841 /* Unregister this call from the event loop. Among other things, this
842 ** may decide to stop the event loop.
844 unregister_asynch_call();
846 /* Give up if an error occurred. */
848 set_fault_from_http_request(&env, status, request);
850 /* XXX - Check to make sure response type is text/xml here. */
851 extract_response_chunk(&env, rpcP, &responseXmlP);
853 rpcP->complete(rpcP->callInfoP, responseXmlP, env);
855 if (!env.fault_occurred)
856 XMLRPC_MEMBLOCK_FREE(char, responseXmlP);
860 xmlrpc_env_clean(&env);
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
877 Unless we return failure, we arrange to have complete() called when
880 This does the 'send_request' operation for a Libwww client transport.
881 -----------------------------------------------------------------------------*/
884 XMLRPC_ASSERT_PTR_OK(envP);
885 XMLRPC_ASSERT_PTR_OK(serverP);
886 XMLRPC_ASSERT_PTR_OK(xmlP);
887 XMLRPC_ASSERT_PTR_OK(callInfoP);
889 rpcCreate(envP, clientTransportP, serverP, xmlP, complete, callInfoP,
891 if (!envP->fault_occurred) {
894 /* Install our request handler. */
895 HTRequest_addAfter(rpcP->request, &asynch_terminate_handler,
896 NULL, NULL, HT_ALL, HT_FILTER_LAST, NO);
898 /* Register our asynchronous call with the event loop. This means
899 the user's callback is guaranteed to be called eventually.
901 register_asynch_call();
903 /* This makes the TCP connection and sends the XML to the server
904 as an HTTP POST request.
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.
913 ok = HTPostAnchor(rpcP->source_anchor,
917 unregister_asynch_call();
918 xmlrpc_env_set_fault(envP, XMLRPC_NETWORK_ERROR,
919 "Libwww (HTPostAnchor()) failed to start the "
922 if (envP->fault_occurred)
929 struct xmlrpc_client_transport_ops xmlrpc_libwww_transport_ops = {