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

Last change on this file since 15232 was 15232, checked in by sjamaan, 11 years ago

Move the client example up a bit

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