1 | # muxrpc
|
2 |
|
3 | combined rpc and multiplexing, with pull-streams.
|
4 |
|
5 | [![build status](https://secure.travis-ci.org/ssbc/muxrpc.png)](http://travis-ci.org/ssbc/muxrpc)
|
6 |
|
7 |
|
8 | ## example
|
9 |
|
10 | ``` js
|
11 |
|
12 | var MRPC = require('muxrpc')
|
13 | var pull = require('pull-stream')
|
14 |
|
15 | //we need a manifest of methods we wish to expose.
|
16 | var api = {
|
17 | //async is a normal async function
|
18 | hello: 'async',
|
19 |
|
20 | //source is a pull-stream (readable)
|
21 | stuff: 'source'
|
22 |
|
23 | //TODO: sink and duplex pull-streams
|
24 | }
|
25 |
|
26 | //pass the api into the constructor, and then pass the object you are wrapping
|
27 | //(if there is a local api)
|
28 | var client = MRPC(api, null) () //remoteApi, localApi
|
29 | var server = MRPC(null, api) ({
|
30 | hello: function (name, cb) {
|
31 | cb(null, 'hello, ' + name + '!')
|
32 | },
|
33 | stuff: function () {
|
34 | return pull.values([1, 2, 3, 4, 5])
|
35 | }
|
36 | })
|
37 |
|
38 | // pass in a cb for the stream end event
|
39 | var a = client.createStream(console.log.bind(console, 'stream is closed'))
|
40 | var b = server.createStream()
|
41 | // or subscribe to the 'closed' event
|
42 | b.once('closed', console.log.bind(console, 'stream is closed'))
|
43 |
|
44 | pull(a, b, a) //pipe together
|
45 |
|
46 | client.hello('world', function (err, value) {
|
47 | if(err) throw err
|
48 | console.log(value)
|
49 | // hello, world!
|
50 | })
|
51 |
|
52 | pull(client.stuff(), pull.drain(console.log))
|
53 | // 1
|
54 | // 2
|
55 | // 3
|
56 | // 4
|
57 | // 5
|
58 | ```
|
59 |
|
60 | ## Manifest
|
61 |
|
62 | like multilevel, a [manifest is required](https://github.com/juliangruber/multilevel#plugins)
|
63 | except it works a little differently, and since muxrpc works with any api,
|
64 | not assuming leveldb then you must write the manifest yourself.
|
65 |
|
66 | The manifest is simply an object mapping to strings, or nested objects.
|
67 |
|
68 | ``` js
|
69 | {
|
70 | foo: 'async', //a function with a callback.
|
71 | bar: 'sync', //a function that returns a value
|
72 | //(note this is converted to an async function for the client)
|
73 | allTheFoos: 'source' //a source pull-stream (aka, readable)
|
74 | writeFoos: 'sink', //a sink pull-stream (aka, writable)
|
75 | fooPhone: 'duplex', //a duplex pull-stream
|
76 |
|
77 | //create nested objects like this:
|
78 | bar: {
|
79 | ...
|
80 | }
|
81 | }
|
82 |
|
83 | ```
|
84 |
|
85 | ## Permissions
|
86 |
|
87 | If you are exposing an api over a network connection,
|
88 | then you probably want some sort of authorization system.
|
89 | `muxrpc@4` and earlier had a `rpc.permissions()` method on
|
90 | the rpc object, but this has been removed.
|
91 | Now you must pass a permissions function, which is called with
|
92 | the `name` (a path) and `args`, if this function does not throw
|
93 | an error, then the call is allowed.
|
94 |
|
95 | In some cases, a simple allow/deny list is sufficient.
|
96 | A helper function, is provided, which was a part of muxrpc@4
|
97 |
|
98 | ``` js
|
99 |
|
100 | var Permissions = require('muxrpc/permissions')
|
101 |
|
102 | var api = {
|
103 | foo: 'async',
|
104 | bar: 'async',
|
105 | auth: 'async'
|
106 | }
|
107 |
|
108 | //set initial settings
|
109 | var perms = Perms({allow: ['auth']})
|
110 |
|
111 | var rpc = muxrpc(null, api, serializer)({
|
112 | foo: function (val, cb) {
|
113 | cb(null, {okay: 'foo'})
|
114 | },
|
115 | bar: function (val, cb) {
|
116 | cb(null, {okay: 'bar'})
|
117 | },
|
118 | auth: function (pass) {
|
119 | //implement an auth function that sets the permissions,
|
120 | //using allow or deny lists.
|
121 |
|
122 | if(pass === 'whatever')
|
123 | perms({deny: ['bar']}) //allow everything except "bar"
|
124 | else if(pass === 's3cr3tz')
|
125 | perms({}) //allow everything!!!
|
126 | else return cb(new Error('ACCESS DENIED'))
|
127 |
|
128 | //else we ARE authorized.
|
129 | cb(null, 'ACCESS GRANTED')
|
130 | }
|
131 | }, perms)
|
132 |
|
133 | //Get a stream to connect to the remote. As in the above example!
|
134 | var ss = rpc.createStream()
|
135 |
|
136 | ```
|
137 |
|
138 | ## bootstrapping
|
139 |
|
140 | sometimes you don't know the remote manifest yet. if you pass a callback
|
141 | instead of `remoteApi` a an async method `manifest` is called on the remote
|
142 | which should return a manifest. This then used as the remote manifest
|
143 | and the callback is called.
|
144 |
|
145 | ``` js
|
146 |
|
147 | var manifest = { hello: 'sync', manifest: 'sync' }
|
148 |
|
149 | var bob = Muxrpc(null, manifest) ({
|
150 | hello: function (n) {
|
151 | if(this._emit) this._emit('hello', n)
|
152 | console.log('hello from ' + this.id)
|
153 | return n + ':' + this.id
|
154 | },
|
155 | manifest: function () {
|
156 | return manifest
|
157 | }
|
158 | })
|
159 |
|
160 | var alice = Muxrpc(function (err, alice) {
|
161 | //alice now knows the bob's api
|
162 | })
|
163 | var as = alice.createStream()
|
164 | pull(as, bob.createStream(), as)
|
165 | ```
|
166 |
|
167 | ## License
|
168 |
|
169 | MIT
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|