-
Notifications
You must be signed in to change notification settings - Fork 0
/
server-core.ss
107 lines (94 loc) · 3.63 KB
/
server-core.ss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#lang scheme
(require web-server/servlet
web-server/http
xml/xml
"serialise.ss"
"protocol.ss")
;; XML-RPC Environment
;; Bindings for the XML-RPC server are modeled as a simple
;; hash-table. We shouldn't need a more complex environment
;; model for an XML-RPC server; the namespace is flat.
(define environment (make-hash))
;; add-handler : symbol (any -> any) -> void
;; Adds a new identifier and associated procedure to the
;; environment.
(define (add-handler id fun)
(hash-set! environment id fun))
;; handler-exists? : symbol -> (U #t #f)
;; Checks to see if the requisite handler is bound in the environment.
(define (handler-exists? id)
(hash-ref environment id (lambda () #f)))
;; invoke-handler : sym (list-of any) -> methodResponse
;; Invokes the given handler on the data passed in from
;; the call if the handler exists.
;;
;; There might be other checks we could do at this point
;; to keep things from falling over in an ugly way; for
;; the moment, I do an arity check, which is more than the
;; spec calls for, I suspect.
(define (invoke-handler name args)
(let* ([fun (hash-ref environment name)]
[arity (procedure-arity fun)]
[arg-length (length args)])
(cond
[(= arity arg-length)
(let* ([result (apply fun args)]
[serialised-result (serialise result)])
;; (printf "result: ~s~n" result)
;; (printf "serialized-result: ~s~n" serialised-result)
(make-response serialised-result))]
[else
(make-handler-fault
(format "You invoked '~a' with ~a parameters; '~a' expects ~a."
name arg-length name arity)
101
)])
))
(define (make-response serialised-result)
(let* ([response `(methodResponse
(params
(param
;; Is there an inconsistent wrapping of 'value'
;; around this?
,serialised-result)))]
[output (string->bytes/utf-8 (xexpr->string response))])
(make-response/full
200 "Okay" (current-seconds)
#"text/xml" '()
(list output))))
;; make-handler-fault : string num -> methodResponse
;; Makes the XML-RPC 'fault' method response.
;; The error codes thrown by this library should be chosen
;; in a less arbitrary way, and documented.
(define (make-handler-fault string code)
(let ([errorHash (make-hash)])
(hash-set!
errorHash 'faultString string)
(hash-set!
errorHash 'faultCode code)
`(methodResponse (fault ,(serialise errorHash)))))
;; extract-xmlrpc-bindings : request -> string
;; The bindings come in all kinds of messed up, it seems.
;; This *must* be tested against clients other than ours
;; to decide whether this is a sensible way to handle the bindings
;; or not.
(define (extract-xmlrpc-bindings request)
;; struct:request looks like:
;; method uri headers/raw bindings/raw
;; host-ip host-port client-ip
(bytes->string/utf-8 (request-post-data/raw request)))
;; handle-xmlrpc-servlet-request* : request -> methodResponse
;; Returns the value of the computation requested by the user,
;; or returns a fault.
(define (handle-xmlrpc-servlet-request* request)
(let ([call (decode-xmlrpc-call
(extract-xmlrpc-bindings request))])
(let ([name (rpc-call-name call)]
[args (rpc-call-args call)])
(if (handler-exists? name)
(invoke-handler name args)
(make-handler-fault
(format "No handler found on server for '~a'" name)
100)))))
; Provides ---------------------------------------
(provide (all-defined-out))