2
0

xmlrpc_libwww_transport.c 34 KB

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