source: project/wiki/eggref/4/xml-rpc @ 15255

Last change on this file since 15255 was 15255, checked in by sjamaan, 10 years ago

Add more examples

File size: 14.4 KB
Line 
1[[tags: egg]]
2
3== xml-rpc
4
5[[toc:]]
6
7=== Description
8
9A library for [[http://www.xmlrpc.com|XML-RPC]] client/servers.
10
11=== Author
12
13[[/users/peter-bex|Peter Bex]] (inspired by and
14using code from [[/eggref/3/xml-rpc|an earlier egg]] by
15[[/users/felix-winkelmann|Felix Winkelmann]])
16
17=== Requirements
18
19* [[http-client]]
20* [[ssax]]
21* [[sxpath]]
22* [[base64]]
23
24=== Documentation
25
26This implementation of XML-RPC is extended to allow returning multiple
27values. Errors during the execution of a server-method are propagated
28to the client as "fault" responses.
29
30==== Client
31
32Usage:
33  (use xml-rpc-client)
34
35<procedure>(xml-rpc-server uri)</procedure>
36
37Returns a procedure that, when called with the name of a remote
38XML-RPC method, will return a procedure that passes its arguments
39to the XML-RPC server which is given in {{uri}} (which can be an URI
40object or a string representing an URI).
41
42To determine how XML-RPC types are mapped to Scheme types and
43vice-versa, see [[#Low-level|below]].  It's important that you read
44this, because there is some ambiguity in how lists are mapped (either
45to arrays or to structs).
46
47Here is an example that fetches the current time of day from xml-rpc.org:
48
49<enscript highlight=scheme>
50(require-extension xml-rpc-client)
51
52(define time-server
53  (xml-rpc-server "http://xml-rpc.org/RPC2") )
54 
55(define get-current-time
56  (time-server "currentTime.getCurrentTime") )
57 
58(print (time->string (get-current-time)))
59</enscript>
60
61
62For lower-level access to the client (implementing custom handlers,
63for example), you can use the following procedures:
64
65<procedure>(xml-rpc-methodcall method-name args)</procedure>
66
67Constructs an SXML representation of a method call to the procedure
68{{method-name}} with an arguments list of {{args}}.
69
70<examples>
71<example>
72<init>
73(use xml-rpc-client)
74</init>
75<expr>
76(xml-rpc-methodcall 'scheme.makeList '(1 2 3 "testing"))
77</expr>
78<result>
79(methodCall
80  (methodName "scheme.makeList")
81  (params
82    (param (value (i4 "1")))
83    (param (value (i4 "2")))
84    (param (value (i4 "3")))
85    (param (value (string "testing")))))
86</result>
87</example>
88</examples>
89
90<procedure>(xml-rpc-response->values response-sxml)</procedure>
91
92This procedure accepts as {{response-sxml}} the SXML representation of
93a server's response, and either returns the values returned by the
94procedure call encoded in the response, or throws an exception of type
95{{exn xml-rpc}} in case the response contains invalid data.
96
97==== Server
98
99Usage:
100  (require-extension xml-rpc-server)
101
102<procedure>(make-xml-rpc-request-handler procedures)</procedure>
103
104This creates a procedure which accepts two arguments; an [intarweb]
105request object and a response object.  It will read an XML-RPC request
106from the request-port and respond to the request with the response
107object, writing to its port.
108
109The procedure that is requested to be called is looked up in the
110{{procedures}} argument, which is an alist of procedure name (symbols)
111to procedure (lambda) mappings. The procedures are called with exactly
112the arguments that are sent by the client, encoded in the call
113({{call-sxml}}). They will be converted to regular Scheme values
114before the procedure is invoked.
115
116To determine how XML-RPC types are mapped to Scheme types and
117vice-versa, see [[#Low-level|below]].  It's important that you read
118this, because there is some ambiguity in how lists are mapped (either
119to arrays or to structs).
120
121<procedure>(start-simple-xml-rpc-server procedures [port])</procedure>
122
123Create a standalone XML-RPC server on {{port}} (defaults to 8080),
124which accepts an XML-RPC request on any URL.
125
126You can also use slightly more low-level procedures to implement your own
127server to be exactly like you want it to be:
128
129<procedure>(xml-rpc-call->xml-rpc-response call-sxml procedures)</procedure>
130
131This procedure converts an XML-RPC procedure call described by
132{{call-sxml}} into an SXML representation of the result.  The
133procedure is looked up in {{procedures}}, invoked, and its return
134values are converted into the appropriate SXML structure describing
135a {{methodResponse}}.  If an error occurs inside the procedure,
136the procedure does not exist, or the XML is invalid, a
137{{methodResponse}} encoding the {{fault}} is constructed instead.
138
139<examples>
140<example>
141<init>
142(use xml-rpc-server)
143</init>
144<expr>
145(xml-rpc-call->xml-rpc-response
146  `(*TOP*
147     (*PI* xml "version=\"1.0\"")
148     (methodCall  ; (xml-rpc-methodcall 'scheme.makeList '(1 2 3))
149       (methodName "scheme.makeList")
150         (params
151           (param (value (int "1")))
152           (param (value (int "2")))
153           (param (value (int "3"))))))
154  `((scheme.makeList . ,list)))
155</expr>
156<result>
157(methodResponse
158  (params
159    (param (value
160             (array
161               (data
162                 (value (i4 "1"))
163                 (value (i4 "2"))
164                 (value (i4 "3"))))))))
165</result>
166</example>
167</examples>
168
169<procedure>(call-xml-rpc-proc call-sxml procedures)</procedure>
170
171This procedure accepts as {{call-sxml}} the SXML representation of a
172procedure call from a client and calls it, returning its values.
173
174This is exactly like {{xml-rpc-call->xml-rpc-response}}, except it
175does not construct an SXML result tree. Instead, the return values are
176those returned by the procedure being called.  In case the procedure
177could not be found or if the call contains an invalid XML structure,
178an exception of type {{(exn xml-rpc)}} is thrown.  The {{xml-rpc}}
179part of the condition contains a {{code}} property which contains the
180fault code. This is {{1}} in case the procedure could not be found and
181{{2}} in case the XML is bad.
182
183<examples>
184<example>
185<init>
186(use xml-rpc-server)
187</init>
188<expr>
189(call-xml-rpc-proc
190  `(*TOP*
191     (*PI* xml "version=\"1.0\"")
192     (methodCall
193      (methodName "Math.add")
194      (params
195       (param (value (int "1")))
196       (param (value (int "2")))
197       (param (value (int "3"))))))
198   `((Math.add . ,+)))
199</expr>
200<result>
2016
202</result>
203</example>
204</examples>
205
206==== Low-level
207
208Sometimes you want complete control over how Scheme values are mapped
209to XML-RPC values and vice versa.  For that, use this module.
210
211Usage:
212  (use xml-rpc-lolevel)
213
214===== Scheme to XML-RPC
215
216<parameter>(xml-rpc-unparsers [alist])</parameter>
217
218This parameter controls how Scheme values are encoded into XML-RPC
219values. The keys of this alist are predicate procedures, the values
220are conversion procedures.  If the predicate procedure returns true
221for its argument, it's a datatype that will be converted to SXML by
222the matching conversion procedure.
223
224Defaults to:
225
226<enscript highlight=scheme>
227`((,vector? . ,vector->xml-rpc-array)
228  (,(conjoin number? exact?) . ,number->xml-rpc-int)
229  (,number? . ,number->xml-rpc-double)
230  (,boolean? . ,boolean->xml-rpc-boolean)
231  (,string? . ,->xml-rpc-string)
232  (,symbol? . ,->xml-rpc-string)
233  (,u8vector? . ,u8vector->xml-rpc-base64)
234  (,blob? . ,blob->xml-rpc-base64)
235  (,hash-table? . ,hash-table->xml-rpc-struct)
236  ;; see below for an explantation of this predicate
237  (,nonempty-symbol-keyed-alist? . ,alist->xml-rpc-struct)
238  (,list? . ,list->xml-rpc-array))
239</enscript>
240
241Order matters in this alist; the converter corresponding to the first
242predicate returning a true value is used.
243
244The SXML returned by these conversion procedures is the element
245''inside'' the {{value}} element.
246
247<examples>
248<example>
249<init>
250(use xml-rpc-lolevel)
251</init>
252<expr>
253(number->xml-rpc-int 1)
254</expr>
255<result>
256(i4 "1")
257</result>
258</example>
259</examples>
260
261<procedure>(value->xml-rpc-fragment value)</procedure>
262
263This procedure converts any Scheme value to SXML for its XML-RPC
264representation.  It looks up the conversion procedure in the
265{{xml-rpc-unparsers}} parameter.
266
267<procedure>(nonempty-symbol-keyed-alist? obj)</procedure>
268
269Returns {{#t}} when {{obj}} is a ''nonempty'' list of pairs, each of
270which has a symbol as {{car}}.
271
272The idea behind this predicates is that it helps to do "The Right
273Thing" when you call an XML-RPC procedure. You can pass in regular
274lists or alists, and it will try to make the right decision whether
275to convert your lists to structs or arrays.
276
277The predicate returns true for nonempty lists only because it's much
278more likely that you will have empty regular lists than empty alists.
279However, it's important to be aware of this because you might end up
280with an empty alist. For absolute safety, remove this predicate from
281the parameter and use only hash-tables.
282
283* <procedure>(list->xml-rpc-array list)</procedure>
284* <procedure>(vector->xml-rpc-array vector)</procedure>
285* <procedure>(number->xml-rpc-int number)</procedure>
286* <procedure>(number->xml-rpc-double number)</procedure>
287* <procedure>(boolean->xml-rpc-boolean boolean)</procedure>
288* <procedure>(u8vector->xml-rpc-base64 u8vector)</procedure>
289* <procedure>(blob->xml-rpc-base64 blob)</procedure>
290* <procedure>(alist->xml-rpc-struct alist)</procedure>
291* <procedure>(hash-table->xml-rpc-struct hash-table)</procedure>
292
293These procedures pretty much do the obvious thing: they encode a
294Scheme object of the given type to an SXML representation for use in
295the XML-RPC request.  Again, the return values look like {{(i4 "1")}}
296and {{(string "foo")}}, ''not'' like {{(value (string "foo"))}}.
297Inside arrays and structs, the {{value}} is automatically wrapped
298around the right values.
299
300<procedure>(->xml-rpc-string obj)</procedure>
301
302This procedure converts the {{obj}} to string with {{->string}} and
303then encodes it in SXML as an XML-RPC string value.  This is useful
304for passing symbols, regular strings or numbers to procedures
305expecting string representation.
306
307<procedure>(vector->xml-rpc-iso8601 time-vector)</procedure>
308
309This procedure encodes a "time vector" (10-element vector, as returned
310by eg [[/man/4/Unit posix#seconds-local-time|seconds->local-time]]) to
311an iso8601 string representing the same date.  Currently this
312procedure is not in the parameter list by default, because it's
313impossible to differentiate between a regular vector that just happens
314to be 10 elements long and a "time-vector". The same problem exists
315for integers and the "seconds since the epoch" representation of time.
316
317Using [[srfi-19]] is a solution to this problem, as it provides a
318distinct datatype for date/time objects. But you would have to make
319your own conversion routines in this case.
320
321===== XML-RPC to Scheme
322
323<parameter>(xml-rpc-parsers [alist])</parameter>
324
325This parameter controls how XML-RPC values are decoded back into
326Scheme values. The keys of this alist are symbols, the values are
327conversion procedures.  If the name of a predicate procedure returns true for
328its argument, it's a datatype that will be converted to SXML by the
329matching conversion procedure.
330
331Defaults to:
332
333<enscript highlight=scheme>
334`((i4 . ,xml-rpc-int->number)
335  (int . ,xml-rpc-int->number)
336  (double . ,xml-rpc-double->number)
337  (boolean . ,xml-rpc-boolean->number)
338  (string . ,xml-rpc-string->string)
339  (base64 . ,xml-rpc-base64->u8vector)
340  (dateTime.iso8601 . ,xml-rpc-datetime->vector)
341  (array . ,xml-rpc-array->vector)
342  (struct . ,xml-rpc-struct->hash-table))
343</enscript>
344
345The SXML arguments to these conversion procedures is the element
346''inside'' the {{value}} element. In other words, the element name
347(or {{car}})of its SXML argument is equal to its key in this alist.
348
349<procedure>(xml-rpc-fragment->value sxml-fragment)</procedure>
350
351This procedure converts an SXML representation of an XML-RPC value to
352its Scheme representation.  It looks up the conversion procedure in
353the {{xml-rpc-parsers}} parameter.
354
355<examples>
356<example>
357<init>(use xml-rpc-lolevel)</init>
358<expr>
359(xml-rpc-fragment->value '(i4 "1"))
360</expr>
361<result>
3621
363</result>
364</example>
365</examples>
366
367* <procedure>(xml-rpc-int->number sxml-fragment)</procedure>
368* <procedure>(xml-rpc-double->number sxml-fragment)</procedure>
369* <procedure>(xml-rpc-boolean->number sxml-fragment)</procedure>
370* <procedure>(xml-rpc-string->string sxml-fragment)</procedure>
371* <procedure>(xml-rpc-array->vector sxml-fragment)</procedure>
372* <procedure>(xml-rpc-array->list sxml-fragment)</procedure>
373* <procedure>(xml-rpc-struct->alist sxml-fragment)</procedure>
374* <procedure>(xml-rpc-struct->hash-table sxml-fragment)</procedure>
375* <procedure>(xml-rpc-base64->string sxml-fragment)</procedure>
376* <procedure>(xml-rpc-base64->u8vector sxml-fragment)</procedure>
377* <procedure>(xml-rpc-base64->blob sxml-fragment)</procedure>
378* <procedure>(xml-rpc-datetime->vector sxml-fragment)</procedure>
379
380Convert the given {{sxml-fragment}} to the corresponding Scheme value.
381
382=== Examples
383
384A simple "hello" server:
385
386<enscript highlight=scheme>
387(require-extension xml-rpc-server)
388
389(define (say-hello var)
390  (sprintf "Hello, ~A!" var) )
391
392((start-simple-xml-rpc-server `((hello . ,say-hello)) 4242))
393</enscript>
394
395You can access it using this client:
396
397<enscript highlight=scheme>
398(require-extension xml-rpc-client)
399
400(define srv (xml-rpc-server "http://localhost:4242/RPC2"))
401(define hello (srv "hello"))
402
403(print "-> " (hello "you"))
404</enscript>
405
406Then run it as follows:
407
408  % csi -script hello.scm &
409  % csi -script client.scm
410 
411  -> Hello, you!
412
413=== Changelog
414
415* 2.0 Reimplementation in Chicken 4, based on [[http-client]]
416
417=== License
418
419  Copyright (c) 2009, Peter Bex
420  Parts Copyright (c) 2003-2006, Felix Winkelmann
421  All rights reserved.
422 
423  Redistribution and use in source and binary forms, with or without
424  modification, are permitted provided that the following conditions are
425  met:
426 
427    Redistributions of source code must retain the above copyright
428    notice, this list of conditions and the following disclaimer.
429 
430    Redistributions in binary form must reproduce the above copyright
431    notice, this list of conditions and the following disclaimer in the
432    documentation and/or other materials provided with the distribution.
433 
434    Neither the name of the author nor the names of its contributors may
435    be used to endorse or promote products derived from this software
436    without specific prior written permission.
437 
438  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
439  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
440  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
441  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
442  HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
443  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
444  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
445  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
446  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
447  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
448  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
449  DAMAGE.
Note: See TracBrowser for help on using the repository browser.