rjr 0.12.2 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/README.md +49 -36
  2. data/Rakefile +2 -0
  3. data/bin/rjr-client +11 -9
  4. data/bin/rjr-server +12 -10
  5. data/examples/amqp.rb +29 -0
  6. data/examples/client.rb +32 -0
  7. data/examples/complete.rb +36 -0
  8. data/examples/local.rb +29 -0
  9. data/examples/server.rb +26 -0
  10. data/examples/tcp.rb +29 -0
  11. data/examples/web.rb +22 -0
  12. data/examples/ws.rb +29 -0
  13. data/lib/rjr/common.rb +7 -12
  14. data/lib/rjr/dispatcher.rb +171 -239
  15. data/lib/rjr/em_adapter.rb +33 -66
  16. data/lib/rjr/message.rb +43 -12
  17. data/lib/rjr/node.rb +197 -103
  18. data/lib/rjr/nodes/amqp.rb +216 -0
  19. data/lib/rjr/nodes/easy.rb +159 -0
  20. data/lib/rjr/nodes/local.rb +118 -0
  21. data/lib/rjr/{missing_node.rb → nodes/missing.rb} +4 -2
  22. data/lib/rjr/nodes/multi.rb +79 -0
  23. data/lib/rjr/nodes/tcp.rb +211 -0
  24. data/lib/rjr/nodes/web.rb +197 -0
  25. data/lib/rjr/nodes/ws.rb +187 -0
  26. data/lib/rjr/stats.rb +70 -0
  27. data/lib/rjr/thread_pool.rb +178 -123
  28. data/site/index.html +45 -0
  29. data/site/jquery-latest.js +9404 -0
  30. data/site/jrw.js +297 -0
  31. data/site/json.js +199 -0
  32. data/specs/dispatcher_spec.rb +244 -198
  33. data/specs/em_adapter_spec.rb +52 -80
  34. data/specs/message_spec.rb +223 -197
  35. data/specs/node_spec.rb +67 -163
  36. data/specs/nodes/amqp_spec.rb +82 -0
  37. data/specs/nodes/easy_spec.rb +13 -0
  38. data/specs/nodes/local_spec.rb +72 -0
  39. data/specs/nodes/multi_spec.rb +65 -0
  40. data/specs/nodes/tcp_spec.rb +75 -0
  41. data/specs/nodes/web_spec.rb +77 -0
  42. data/specs/nodes/ws_spec.rb +78 -0
  43. data/specs/stats_spec.rb +59 -0
  44. data/specs/thread_pool_spec.rb +44 -35
  45. metadata +40 -30
  46. data/lib/rjr/amqp_node.rb +0 -330
  47. data/lib/rjr/inspect.rb +0 -65
  48. data/lib/rjr/local_node.rb +0 -150
  49. data/lib/rjr/multi_node.rb +0 -65
  50. data/lib/rjr/tcp_node.rb +0 -323
  51. data/lib/rjr/thread_pool2.rb +0 -272
  52. data/lib/rjr/util.rb +0 -104
  53. data/lib/rjr/web_node.rb +0 -266
  54. data/lib/rjr/ws_node.rb +0 -289
  55. data/lib/rjr.rb +0 -16
  56. data/specs/amqp_node_spec.rb +0 -31
  57. data/specs/inspect_spec.rb +0 -60
  58. data/specs/local_node_spec.rb +0 -43
  59. data/specs/multi_node_spec.rb +0 -45
  60. data/specs/tcp_node_spec.rb +0 -33
  61. data/specs/util_spec.rb +0 -46
  62. data/specs/web_node_spec.rb +0 -32
  63. data/specs/ws_node_spec.rb +0 -32
  64. /data/lib/rjr/{tcp_node2.rb → nodes/tcp2.rb} +0 -0
  65. /data/lib/rjr/{udp_node.rb → nodes/udp.rb} +0 -0
data/site/jrw.js ADDED
@@ -0,0 +1,297 @@
1
+ /* Json-Rpc over HTTP and Websockets
2
+ *
3
+ * Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
4
+ * Licensed under the Apache License, Version 2.0
5
+ */
6
+
7
+ ///////////////////////////////////////////////////////
8
+ // Helpers to generate a uuid
9
+ function S4() {
10
+ return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
11
+ }
12
+ function guid() {
13
+ return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
14
+ }
15
+
16
+ ///////////////////////////////////////////////////////
17
+ // JRMessage
18
+
19
+ // Encapsulates a json-rpc message
20
+ function JRMessage(){
21
+ this.id = null;
22
+ this.rpc_method = null;
23
+ this.args = null;
24
+
25
+ this.data = null;
26
+ this.json = null;
27
+
28
+ this.error = null;
29
+ this.result = null;
30
+
31
+ this.onresponse = null;
32
+ this.success = null;
33
+
34
+ this.to_json = function(){
35
+ this.json = $.toJSON(this.data);
36
+ return this.json;
37
+ }
38
+
39
+ this.handle_response = function(res){
40
+ res.success = (res.error == undefined);
41
+ if(this.onresponse)
42
+ this.onresponse(res);
43
+ }
44
+ }
45
+
46
+ // Create new request message to send
47
+ JRMessage.new_request = function(rpc_method, args){
48
+ var msg = new JRMessage();
49
+ msg.id = guid();
50
+ msg.rpc_method = rpc_method;
51
+ msg.args = args;
52
+ msg.data = {jsonrpc: '2.0',
53
+ method: msg.rpc_method,
54
+ params: msg.args,
55
+ id: msg.id};
56
+ return msg;
57
+ }
58
+
59
+ // Internal helper to generate request from common args.
60
+ //
61
+ // rpc method, parameter list, and optional response callback
62
+ // will be extracted from the 'args' param in that order.
63
+ //
64
+ // node_id and headers will be set on request message before it
65
+ // is returned
66
+ JRMessage.pretty_request = function(args, node_id, headers){
67
+ // create request message
68
+ var rpc_method = args[0];
69
+ var params = [];
70
+ var cb = null;
71
+ for(a = 1; a < args.length; a++){
72
+ if(a == args.length - 1 && typeof args[a] === 'function')
73
+ cb = args[a];
74
+ else
75
+ params.push(args[a]);
76
+ }
77
+ var req = JRMessage.new_request(rpc_method, params);
78
+ if(node_id) req.data['node_id'] = node_id;
79
+ for(var header in headers)
80
+ req.data[header] = headers[header];
81
+
82
+ // register callback if last argument is a function
83
+ if(cb) req.onresponse = cb;
84
+
85
+ return req;
86
+ }
87
+
88
+ // Parse message received in json string
89
+ JRMessage.from_msg = function(dat){
90
+ var msg = new JRMessage();
91
+ msg.json = dat;
92
+ msg.data = $.evalJSON(dat);
93
+
94
+ msg.id = msg.data['id'];
95
+ msg.rpc_method = msg.data['method'];
96
+ if(msg.data['params']){
97
+ msg.params = msg.data['params'];
98
+ for(p=0;p<msg.params.length;++p){
99
+ if(JRObject.is_jrobject(msg.params[p]))
100
+ msg.params[p] = JRObject.from_json(msg.params[p]);
101
+ else if(JRObject.is_jrobject_array(msg.params[p]))
102
+ msg.params[p] = JRObject.from_json_array(msg.params[p]);
103
+ }
104
+ }
105
+ msg.error = msg.data['error'];
106
+ msg.result = msg.data['result'];
107
+ if(msg.result && JRObject.is_jrobject(msg.result))
108
+ msg.result = JRObject.from_json(msg.result);
109
+ else if(JRObject.is_jrobject_array(msg.result))
110
+ msg.result = JRObject.from_json_array(msg.result);
111
+ return msg;
112
+ }
113
+
114
+ ///////////////////////////////////////////////////////
115
+ // JRObject
116
+
117
+ // Encapsulates an object w/ type
118
+ // - adaptor for the ruby 'json' library
119
+ function JRObject (type, value, ignore_properties){
120
+ this.type = type;
121
+ this.value = value;
122
+ this.ignore_properties = (typeof(ignore_properties) != 'undefined') ?
123
+ ignore_properties : ["toJSON"];
124
+ this.toJSON = function(){
125
+ var data = {};
126
+ for(p in this.value)
127
+ if($.inArray(p, this.ignore_properties) == -1)
128
+ data[p] = value[p];
129
+ return {json_class: this.type, data: data };
130
+ };
131
+ };
132
+
133
+ // Return boolean indicating if json contains encapsulated JRObject
134
+ JRObject.is_jrobject = function(json){
135
+ return json && json['json_class'] && json['data'];
136
+ };
137
+
138
+ // Return boolean indicating if json contains array of encapsulated JRObjects
139
+ JRObject.is_jrobject_array = function(json){
140
+ return json && typeof(json) == "object" && json.length > 0 && JRObject.is_jrobject(json[0]);
141
+ };
142
+
143
+ // Convert json to JRObject
144
+ JRObject.from_json = function(json){
145
+ var obj = json['data'];
146
+ obj.json_class = json['json_class'];
147
+ for(var p in obj){
148
+ if(JRObject.is_jrobject(obj[p]))
149
+ obj[p] = JRObject.from_json(obj[p]);
150
+ else if(JRObject.is_jrobject_array(obj[p])){
151
+ obj[p] = JRObject.from_json_array(obj[p]);
152
+ }
153
+ }
154
+ return obj;
155
+ };
156
+
157
+ // Convert json to array of JRObjects
158
+ JRObject.from_json_array = function(json){
159
+ var objs = [];
160
+ for(var i in json)
161
+ if(JRObject.is_jrobject(json[i]))
162
+ objs[i] = JRObject.from_json(json[i]);
163
+ return objs;
164
+ };
165
+
166
+ ///////////////////////////////////////////////////////
167
+ // WSNode
168
+
169
+ // Main json-rpc client websocket interface
170
+ function WSNode (host, port){
171
+ var node = this;
172
+ this.opened = false;
173
+ this.node_id = null;
174
+ this.headers = {};
175
+ this.messages = {};
176
+
177
+ // Open socket connection
178
+ this.open = function(){
179
+ node.socket = new WebSocket("ws://" + host + ":" + port);
180
+
181
+ node.socket.onclose = function (){
182
+ if(node.onclose)
183
+ node.onclose();
184
+ };
185
+
186
+ node.socket.onmessage = function (evnt){
187
+ var msg = JRMessage.from_msg(evnt.data);
188
+
189
+ // match response w/ outstanding request
190
+ if(msg.id){
191
+ var req = node.messages[msg.id];
192
+ delete node.messages[msg.id];
193
+ req.handle_response(msg)
194
+
195
+ // if err msg, run node.onerror
196
+ if(msg.error)
197
+ if(node.onerror)
198
+ node.onerror(msg)
199
+
200
+ }else{
201
+ // relying on clients to handle notifications via message_received
202
+ // TODO add notification (and request?) handler support here
203
+ //node.invoke_method(msg.rpc_method, msg.params)
204
+ if(node.message_received)
205
+ node.message_received(msg);
206
+
207
+ }
208
+ };
209
+
210
+ node.socket.onerror = function(e){
211
+ if(node.onerror)
212
+ node.onerror(e);
213
+ }
214
+
215
+ node.socket.onopen = function (){
216
+ // send queued messages
217
+ for(var m in node.messages)
218
+ node.socket.send(node.messages[m].to_json());
219
+
220
+ node.opened = true;
221
+
222
+ // invoke client callback
223
+ if(node.onopen)
224
+ node.onopen();
225
+ };
226
+ };
227
+
228
+ // Close socket connection
229
+ this.close = function(){
230
+ this.socket.close();
231
+ };
232
+
233
+ // Invoke request on socket, may be invoked before or after socket is opened.
234
+ //
235
+ // Pass in the rpc method, arguments to invoke method with, and optional callback
236
+ // to be invoked upon received response.
237
+ this.invoke = function(){
238
+ var req = JRMessage.pretty_request(arguments, this.node_id, this.headers);
239
+
240
+ // store requests for later retrieval
241
+ this.messages[req.id] = req;
242
+
243
+ if(node.opened)
244
+ this.socket.send(req.to_json());
245
+
246
+ return req;
247
+ };
248
+ };
249
+
250
+ ///////////////////////////////////////////////////////
251
+ // WebNode
252
+
253
+ // Main json-rpc www interface
254
+ function WebNode (uri){
255
+ var node = this;
256
+ this.node_id = null;
257
+ this.headers = {};
258
+
259
+ // Invoke request via http
260
+ //
261
+ // Pass in the rpc method, arguments to invoke method with, and optional callback
262
+ // to be invoked upon received response.
263
+ this.invoke = function(){
264
+ var req = JRMessage.pretty_request(arguments, this.node_id, this.headers);
265
+
266
+ $.ajax({type: 'POST',
267
+ url: uri,
268
+ data: req.to_json(),
269
+ dataType: 'text', // using text so we can parse json ourselves
270
+
271
+ success: function(data){
272
+ var msg = JRMessage.from_msg(data);
273
+ // js web client doesn't support notifications
274
+ //if(node.message_received)
275
+ //node.message_received(msg);
276
+
277
+ req.handle_response(msg)
278
+
279
+ // if err msg, run node.onerror
280
+ if(msg.error)
281
+ if(node.onerror)
282
+ node.onerror(msg);
283
+ },
284
+
285
+ error: function(jqXHR, textStatus, errorThrown){
286
+ var err = { 'error' : {'code' : jqXHR.status,
287
+ 'message' : textStatus,
288
+ 'class' : errorThrown } };
289
+ if(node.onerror)
290
+ node.onerror(err);
291
+
292
+ req.handle_response(err)
293
+ }});
294
+
295
+ return req;
296
+ };
297
+ };
data/site/json.js ADDED
@@ -0,0 +1,199 @@
1
+ /**
2
+ * jQuery JSON plugin 2.4.0
3
+ *
4
+ * @author Brantley Harris, 2009-2011
5
+ * @author Timo Tijhof, 2011-2012
6
+ * @source This plugin is heavily influenced by MochiKit's serializeJSON, which is
7
+ * copyrighted 2005 by Bob Ippolito.
8
+ * @source Brantley Harris wrote this plugin. It is based somewhat on the JSON.org
9
+ * website's http://www.json.org/json2.js, which proclaims:
10
+ * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
11
+ * I uphold.
12
+ * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
13
+ */
14
+ (function ($) {
15
+ 'use strict';
16
+
17
+ var escape = /["\\\x00-\x1f\x7f-\x9f]/g,
18
+ meta = {
19
+ '\b': '\\b',
20
+ '\t': '\\t',
21
+ '\n': '\\n',
22
+ '\f': '\\f',
23
+ '\r': '\\r',
24
+ '"' : '\\"',
25
+ '\\': '\\\\'
26
+ },
27
+ hasOwn = Object.prototype.hasOwnProperty;
28
+
29
+ /**
30
+ * jQuery.toJSON
31
+ * Converts the given argument into a JSON representation.
32
+ *
33
+ * @param o {Mixed} The json-serializable *thing* to be converted
34
+ *
35
+ * If an object has a toJSON prototype, that will be used to get the representation.
36
+ * Non-integer/string keys are skipped in the object, as are keys that point to a
37
+ * function.
38
+ *
39
+ */
40
+ $.toJSON = typeof JSON === 'object' && JSON.stringify ? JSON.stringify : function (o) {
41
+ if (o === null) {
42
+ return 'null';
43
+ }
44
+
45
+ var pairs, k, name, val,
46
+ type = $.type(o);
47
+
48
+ if (type === 'undefined') {
49
+ return undefined;
50
+ }
51
+
52
+ // Also covers instantiated Number and Boolean objects,
53
+ // which are typeof 'object' but thanks to $.type, we
54
+ // catch them here. I don't know whether it is right
55
+ // or wrong that instantiated primitives are not
56
+ // exported to JSON as an {"object":..}.
57
+ // We choose this path because that's what the browsers did.
58
+ if (type === 'number' || type === 'boolean') {
59
+ return String(o);
60
+ }
61
+ if (type === 'string') {
62
+ return $.quoteString(o);
63
+ }
64
+ if (typeof o.toJSON === 'function') {
65
+ return $.toJSON(o.toJSON());
66
+ }
67
+ if (type === 'date') {
68
+ var month = o.getUTCMonth() + 1,
69
+ day = o.getUTCDate(),
70
+ year = o.getUTCFullYear(),
71
+ hours = o.getUTCHours(),
72
+ minutes = o.getUTCMinutes(),
73
+ seconds = o.getUTCSeconds(),
74
+ milli = o.getUTCMilliseconds();
75
+
76
+ if (month < 10) {
77
+ month = '0' + month;
78
+ }
79
+ if (day < 10) {
80
+ day = '0' + day;
81
+ }
82
+ if (hours < 10) {
83
+ hours = '0' + hours;
84
+ }
85
+ if (minutes < 10) {
86
+ minutes = '0' + minutes;
87
+ }
88
+ if (seconds < 10) {
89
+ seconds = '0' + seconds;
90
+ }
91
+ if (milli < 100) {
92
+ milli = '0' + milli;
93
+ }
94
+ if (milli < 10) {
95
+ milli = '0' + milli;
96
+ }
97
+ return '"' + year + '-' + month + '-' + day + 'T' +
98
+ hours + ':' + minutes + ':' + seconds +
99
+ '.' + milli + 'Z"';
100
+ }
101
+
102
+ pairs = [];
103
+
104
+ if ($.isArray(o)) {
105
+ for (k = 0; k < o.length; k++) {
106
+ pairs.push($.toJSON(o[k]) || 'null');
107
+ }
108
+ return '[' + pairs.join(',') + ']';
109
+ }
110
+
111
+ // Any other object (plain object, RegExp, ..)
112
+ // Need to do typeof instead of $.type, because we also
113
+ // want to catch non-plain objects.
114
+ if (typeof o === 'object') {
115
+ for (k in o) {
116
+ // Only include own properties,
117
+ // Filter out inherited prototypes
118
+ if (hasOwn.call(o, k)) {
119
+ // Keys must be numerical or string. Skip others
120
+ type = typeof k;
121
+ if (type === 'number') {
122
+ name = '"' + k + '"';
123
+ } else if (type === 'string') {
124
+ name = $.quoteString(k);
125
+ } else {
126
+ continue;
127
+ }
128
+ type = typeof o[k];
129
+
130
+ // Invalid values like these return undefined
131
+ // from toJSON, however those object members
132
+ // shouldn't be included in the JSON string at all.
133
+ if (type !== 'function' && type !== 'undefined') {
134
+ val = $.toJSON(o[k]);
135
+ pairs.push(name + ':' + val);
136
+ }
137
+ }
138
+ }
139
+ return '{' + pairs.join(',') + '}';
140
+ }
141
+ };
142
+
143
+ /**
144
+ * jQuery.evalJSON
145
+ * Evaluates a given json string.
146
+ *
147
+ * @param str {String}
148
+ */
149
+ $.evalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
150
+ /*jshint evil: true */
151
+ return eval('(' + str + ')');
152
+ };
153
+
154
+ /**
155
+ * jQuery.secureEvalJSON
156
+ * Evals JSON in a way that is *more* secure.
157
+ *
158
+ * @param str {String}
159
+ */
160
+ $.secureEvalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
161
+ var filtered =
162
+ str
163
+ .replace(/\\["\\\/bfnrtu]/g, '@')
164
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
165
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, '');
166
+
167
+ if (/^[\],:{}\s]*$/.test(filtered)) {
168
+ /*jshint evil: true */
169
+ return eval('(' + str + ')');
170
+ }
171
+ throw new SyntaxError('Error parsing JSON, source is not valid.');
172
+ };
173
+
174
+ /**
175
+ * jQuery.quoteString
176
+ * Returns a string-repr of a string, escaping quotes intelligently.
177
+ * Mostly a support function for toJSON.
178
+ * Examples:
179
+ * >>> jQuery.quoteString('apple')
180
+ * "apple"
181
+ *
182
+ * >>> jQuery.quoteString('"Where are we going?", she asked.')
183
+ * "\"Where are we going?\", she asked."
184
+ */
185
+ $.quoteString = function (str) {
186
+ if (str.match(escape)) {
187
+ return '"' + str.replace(escape, function (a) {
188
+ var c = meta[a];
189
+ if (typeof c === 'string') {
190
+ return c;
191
+ }
192
+ c = a.charCodeAt();
193
+ return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
194
+ }) + '"';
195
+ }
196
+ return '"' + str + '"';
197
+ };
198
+
199
+ }(jQuery));