xrbp 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +7 -0
- data/examples/autoconnect_timeout.rb +20 -0
- data/examples/cmd.rb +13 -0
- data/examples/ledger.rb +15 -0
- data/examples/ledger_multi_subscribe.rb +20 -0
- data/examples/ledger_subscribe.rb +18 -0
- data/examples/multi.rb +10 -0
- data/examples/paginate.rb +13 -0
- data/examples/prioritized.rb +13 -0
- data/examples/round_robin.rb +11 -0
- data/examples/websocket.rb +20 -0
- data/lib/xrbp/model/account.rb +27 -0
- data/lib/xrbp/model/base.rb +31 -0
- data/lib/xrbp/model/ledger.rb +24 -0
- data/lib/xrbp/model.rb +3 -0
- data/lib/xrbp/network/nodes.rb +8 -0
- data/lib/xrbp/network.rb +6 -0
- data/lib/xrbp/version.rb +3 -0
- data/lib/xrbp/websocket/client.rb +127 -0
- data/lib/xrbp/websocket/cmds/account_info.rb +20 -0
- data/lib/xrbp/websocket/cmds/account_lines.rb +33 -0
- data/lib/xrbp/websocket/cmds/account_objects.rb +33 -0
- data/lib/xrbp/websocket/cmds/account_offers.rb +33 -0
- data/lib/xrbp/websocket/cmds/account_tx.rb +20 -0
- data/lib/xrbp/websocket/cmds/book_offers.rb +38 -0
- data/lib/xrbp/websocket/cmds/ledger.rb +25 -0
- data/lib/xrbp/websocket/cmds/ledger_entry.rb +16 -0
- data/lib/xrbp/websocket/cmds/paginated.rb +43 -0
- data/lib/xrbp/websocket/cmds/server_info.rb +11 -0
- data/lib/xrbp/websocket/cmds/subscribe.rb +18 -0
- data/lib/xrbp/websocket/cmds.rb +12 -0
- data/lib/xrbp/websocket/command.rb +19 -0
- data/lib/xrbp/websocket/connection.rb +169 -0
- data/lib/xrbp/websocket/has_plugin.rb +30 -0
- data/lib/xrbp/websocket/message.rb +41 -0
- data/lib/xrbp/websocket/multi/fallback.rb +18 -0
- data/lib/xrbp/websocket/multi/multi_connection.rb +55 -0
- data/lib/xrbp/websocket/multi/parallel.rb +28 -0
- data/lib/xrbp/websocket/multi/prioritized.rb +15 -0
- data/lib/xrbp/websocket/multi/round_robin.rb +19 -0
- data/lib/xrbp/websocket/plugins/autoconnect.rb +42 -0
- data/lib/xrbp/websocket/plugins/command_dispatcher.rb +39 -0
- data/lib/xrbp/websocket/plugins/command_paginator.rb +45 -0
- data/lib/xrbp/websocket/plugins/connection_timeout.rb +54 -0
- data/lib/xrbp/websocket/plugins/message_dispatcher.rb +141 -0
- data/lib/xrbp/websocket/plugins/result_parser.rb +31 -0
- data/lib/xrbp/websocket/plugins.rb +18 -0
- data/lib/xrbp/websocket/socket.rb +109 -0
- data/lib/xrbp/websocket.rb +15 -0
- data/lib/xrbp.rb +5 -0
- metadata +123 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'active_support/core_ext/hash/except'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module WebSocket
|
5
|
+
module Cmds
|
6
|
+
module Paginated
|
7
|
+
attr_reader :prev_cmd
|
8
|
+
|
9
|
+
def root_cmd
|
10
|
+
return self unless prev_cmd
|
11
|
+
prev_cmd.root_cmd
|
12
|
+
end
|
13
|
+
|
14
|
+
def each_ancestor(&bl)
|
15
|
+
bl.call self
|
16
|
+
prev_cmd.each_ancestor &bl if prev_cmd
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_paginate(args)
|
20
|
+
@paginate = args[:paginate]
|
21
|
+
@prev_cmd = args[:prev_cmd]
|
22
|
+
end
|
23
|
+
|
24
|
+
def paginate_args
|
25
|
+
return :prev_cmd #, :paginate # XXX need to forward paginate
|
26
|
+
end
|
27
|
+
|
28
|
+
def args_without_paginate
|
29
|
+
args.except(*paginate_args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def paginate?
|
33
|
+
!!@paginate
|
34
|
+
end
|
35
|
+
|
36
|
+
def next_page(marker)
|
37
|
+
self.class.from_h(to_h.merge({:marker => marker,
|
38
|
+
:prev_cmd => self}))
|
39
|
+
end
|
40
|
+
end # module Paginated
|
41
|
+
end # module Cmds
|
42
|
+
end # module WebSocket
|
43
|
+
end # module Wipple
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Cmds
|
4
|
+
class Subscribe < Command
|
5
|
+
attr_accessor :args
|
6
|
+
|
7
|
+
def initialize(args={})
|
8
|
+
@args = args
|
9
|
+
super(to_h)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_h
|
13
|
+
args.merge(:command => :subscribe)
|
14
|
+
end
|
15
|
+
end # class Subscribe
|
16
|
+
end # module Cmds
|
17
|
+
end # module WebSocket
|
18
|
+
end # module Wipple
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require_relative './cmds/paginated'
|
2
|
+
require_relative './cmds/server_info'
|
3
|
+
require_relative './cmds/ledger'
|
4
|
+
require_relative './cmds/account_lines'
|
5
|
+
require_relative './cmds/account_info'
|
6
|
+
require_relative './cmds/account_info'
|
7
|
+
require_relative './cmds/account_objects'
|
8
|
+
require_relative './cmds/account_offers'
|
9
|
+
require_relative './cmds/account_tx'
|
10
|
+
require_relative './cmds/book_offers'
|
11
|
+
require_relative './cmds/subscribe'
|
12
|
+
require_relative './cmds/ledger_entry'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module WebSocket
|
5
|
+
class Command < Message
|
6
|
+
attr_accessor :id
|
7
|
+
|
8
|
+
def initialize(data)
|
9
|
+
@@id ||= 0
|
10
|
+
@id = (@@id += 1)
|
11
|
+
|
12
|
+
json = Hash[data]
|
13
|
+
json['id'] = id
|
14
|
+
|
15
|
+
super(json.to_json)
|
16
|
+
end
|
17
|
+
end # class Command
|
18
|
+
end # module WebSocket
|
19
|
+
end # module XRBP
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require_relative './has_plugin'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module WebSocket
|
5
|
+
class Connection
|
6
|
+
include EventEmitter
|
7
|
+
include HasPlugin
|
8
|
+
|
9
|
+
attr_reader :url
|
10
|
+
attr_accessor :parent
|
11
|
+
|
12
|
+
def initialize(url)
|
13
|
+
@url = url
|
14
|
+
@force_quit = false
|
15
|
+
end
|
16
|
+
|
17
|
+
###
|
18
|
+
|
19
|
+
def connect
|
20
|
+
client.connect
|
21
|
+
end
|
22
|
+
|
23
|
+
def next_connection(prev)
|
24
|
+
return nil unless !!parent
|
25
|
+
parent.next_connection(prev)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialized?
|
29
|
+
!!@client
|
30
|
+
end
|
31
|
+
|
32
|
+
def open?
|
33
|
+
initialized? && client.open?
|
34
|
+
end
|
35
|
+
|
36
|
+
def closed?
|
37
|
+
!open?
|
38
|
+
end
|
39
|
+
|
40
|
+
def close!
|
41
|
+
client.close if open?
|
42
|
+
end
|
43
|
+
|
44
|
+
def send_data(data)
|
45
|
+
client.send_data(data)
|
46
|
+
end
|
47
|
+
|
48
|
+
###
|
49
|
+
|
50
|
+
def force_quit?
|
51
|
+
@force_quit
|
52
|
+
end
|
53
|
+
|
54
|
+
def force_quit!
|
55
|
+
@force_quit = true
|
56
|
+
wake_all
|
57
|
+
end
|
58
|
+
|
59
|
+
def thread_registry
|
60
|
+
@thread_registry ||= Concurrent::Array.new
|
61
|
+
end
|
62
|
+
|
63
|
+
def rsleep(t)
|
64
|
+
thread_registry << Thread.current
|
65
|
+
sleep(t)
|
66
|
+
thread_registry.delete(Thread.current)
|
67
|
+
end
|
68
|
+
|
69
|
+
def wake_all
|
70
|
+
thread_registry.each { |th| th.wakeup }
|
71
|
+
end
|
72
|
+
|
73
|
+
###
|
74
|
+
|
75
|
+
def wait_for_open
|
76
|
+
return unless initialized?
|
77
|
+
|
78
|
+
state_mutex.synchronize {
|
79
|
+
open_cv.wait(state_mutex, 0.1)
|
80
|
+
} until force_quit? || open?
|
81
|
+
end
|
82
|
+
|
83
|
+
def wait_for_close
|
84
|
+
return unless initialized?
|
85
|
+
|
86
|
+
state_mutex.synchronize {
|
87
|
+
close_cv.wait(state_mutex, 0.1)
|
88
|
+
} while !force_quit? && open?
|
89
|
+
end
|
90
|
+
|
91
|
+
def state_mutex
|
92
|
+
@state_mutex ||= Mutex.new
|
93
|
+
end
|
94
|
+
|
95
|
+
def open_cv
|
96
|
+
@open_cv ||= ConditionVariable.new
|
97
|
+
end
|
98
|
+
|
99
|
+
def close_cv
|
100
|
+
@close_cv ||= ConditionVariable.new
|
101
|
+
end
|
102
|
+
|
103
|
+
###
|
104
|
+
|
105
|
+
def client
|
106
|
+
@client ||= begin
|
107
|
+
client = Client.new(@url)
|
108
|
+
conn = self
|
109
|
+
|
110
|
+
client.on :connecting do
|
111
|
+
conn.emit :connecting
|
112
|
+
conn.parent.emit :connecting if conn.parent
|
113
|
+
end
|
114
|
+
|
115
|
+
client.on :open do
|
116
|
+
conn.emit :open
|
117
|
+
conn.parent.emit :open if conn.parent
|
118
|
+
|
119
|
+
conn.state_mutex.synchronize {
|
120
|
+
conn.open_cv.signal
|
121
|
+
}
|
122
|
+
|
123
|
+
conn.plugins.each { |plg|
|
124
|
+
plg.opened if plg.respond_to?(:opened)
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
client.on :close do
|
129
|
+
conn.emit :close
|
130
|
+
conn.parent.emit :close if conn.parent
|
131
|
+
|
132
|
+
conn.state_mutex.synchronize {
|
133
|
+
conn.close_cv.signal
|
134
|
+
}
|
135
|
+
|
136
|
+
conn.plugins.each { |plg|
|
137
|
+
plg.closed if plg.respond_to?(:closed)
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
client.on :completed do |err|
|
142
|
+
conn.emit :completed
|
143
|
+
conn.parent.emit :completed if conn.parent
|
144
|
+
end
|
145
|
+
|
146
|
+
client.on :error do |err|
|
147
|
+
conn.emit :error, err
|
148
|
+
conn.parent.emit :error, err if conn.parent
|
149
|
+
|
150
|
+
conn.plugins.each { |plg|
|
151
|
+
plg.error err if plg.respond_to?(:error)
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
client.on :message do |msg|
|
156
|
+
conn.emit :message, msg
|
157
|
+
conn.parent.emit :message, msg if conn.parent
|
158
|
+
|
159
|
+
conn.plugins.each { |plg|
|
160
|
+
plg.message msg if plg.respond_to?(:message)
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
client
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end # class Connection
|
168
|
+
end # module WebSocket
|
169
|
+
end # module XRBP
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module HasPlugin
|
4
|
+
def plugins
|
5
|
+
@plugins ||= []
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_plugin(*plgs)
|
9
|
+
plgs.each { |plg|
|
10
|
+
plg = WebSocket.plugins[plg] if plg.is_a?(Symbol)
|
11
|
+
raise ArgumentError unless !!plg
|
12
|
+
plg = plg.new(self)
|
13
|
+
plugins << plg
|
14
|
+
plg.added if plg.respond_to?(:added)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def plugin?(plg)
|
19
|
+
plugins.collect { |plg| plg.class }.include?(plg)
|
20
|
+
end
|
21
|
+
|
22
|
+
def define_instance_method(name, &block)
|
23
|
+
(class << self; self; end).class_eval do
|
24
|
+
define_method name, &block
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end # module HasPlugin
|
29
|
+
end # module WebSocket
|
30
|
+
end # module XRBP
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
class Message
|
4
|
+
attr_reader :result, :time
|
5
|
+
attr_accessor :connection
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
@data = data
|
9
|
+
@result = nil
|
10
|
+
@cv = ConditionVariable.new
|
11
|
+
@signalled = false
|
12
|
+
@time = Time.now
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
@data
|
17
|
+
end
|
18
|
+
|
19
|
+
def signal
|
20
|
+
@signalled = true
|
21
|
+
@cv.signal
|
22
|
+
end
|
23
|
+
|
24
|
+
def wait
|
25
|
+
connection.state_mutex.synchronize {
|
26
|
+
# only wait if we haven't received response
|
27
|
+
@cv.wait(connection.state_mutex) unless connection.closed? || @signalled
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_writer :bl
|
32
|
+
|
33
|
+
def bl
|
34
|
+
@bl ||= proc { |res|
|
35
|
+
@result = res
|
36
|
+
signal
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end # class Message
|
40
|
+
end # module WebSocket
|
41
|
+
end # module XRBP
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
class Fallback < MultiConnection
|
4
|
+
def initialize(*urls)
|
5
|
+
super(*urls)
|
6
|
+
end
|
7
|
+
|
8
|
+
def next_connection(prev=nil)
|
9
|
+
unless prev.nil?
|
10
|
+
return nil if connections.last == prev
|
11
|
+
return connections[(connections.index(prev) + 1)..-1].find { |c| !c.closed? }
|
12
|
+
end
|
13
|
+
|
14
|
+
connections.find { |c| !c.closed? }
|
15
|
+
end
|
16
|
+
end # class Fallback
|
17
|
+
end # module WebSocket
|
18
|
+
end # module XRBP
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative '../has_plugin'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module WebSocket
|
5
|
+
class MultiConnection
|
6
|
+
include EventEmitter
|
7
|
+
include HasPlugin
|
8
|
+
|
9
|
+
attr_reader :connections
|
10
|
+
|
11
|
+
def initialize(*urls)
|
12
|
+
@connections = []
|
13
|
+
|
14
|
+
urls.each { |url|
|
15
|
+
@connections << Connection.new(url)
|
16
|
+
}
|
17
|
+
|
18
|
+
connections.each { |c| c.parent = self }
|
19
|
+
|
20
|
+
yield self if block_given?
|
21
|
+
end
|
22
|
+
|
23
|
+
def force_quit!
|
24
|
+
connections.each { |c| c.force_quit! }
|
25
|
+
end
|
26
|
+
|
27
|
+
def close!
|
28
|
+
connections.each { |c| c.close! }
|
29
|
+
end
|
30
|
+
|
31
|
+
alias :_add_plugin :add_plugin
|
32
|
+
|
33
|
+
def add_plugin(*plg)
|
34
|
+
connections.each { |c|
|
35
|
+
c.add_plugin *plg
|
36
|
+
}
|
37
|
+
|
38
|
+
_add_plugin(*plg)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Always return first connection by default,
|
42
|
+
# override in subclasses
|
43
|
+
def next_connection(prev=nil)
|
44
|
+
return nil unless prev.nil?
|
45
|
+
@connections.first
|
46
|
+
end
|
47
|
+
|
48
|
+
def connect
|
49
|
+
@connections.each { |c|
|
50
|
+
c.connect
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end # class MultiConnection
|
54
|
+
end # module WebSocket
|
55
|
+
end # module XRBP
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
class Parallel < MultiConnection
|
4
|
+
class All
|
5
|
+
attr_accessor :connections
|
6
|
+
|
7
|
+
def initialize(connections)
|
8
|
+
@connections = connections
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(m, *args, &bl)
|
12
|
+
connections.collect { |c|
|
13
|
+
c.send(m, *args, &bl) if c.open?
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end # class All
|
17
|
+
|
18
|
+
def initialize(*urls)
|
19
|
+
super(*urls)
|
20
|
+
end
|
21
|
+
|
22
|
+
def next_connection(prev=nil)
|
23
|
+
return nil unless prev.nil?
|
24
|
+
All.new connections
|
25
|
+
end
|
26
|
+
end # class RoundRobin
|
27
|
+
end # module WebSocket
|
28
|
+
end # module XRBP
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
class Prioritized < MultiConnection
|
4
|
+
def initialize(*urls)
|
5
|
+
super(*urls)
|
6
|
+
end
|
7
|
+
|
8
|
+
def next_connection(prev=nil)
|
9
|
+
return nil if prev == connections.last
|
10
|
+
return super if prev.nil?
|
11
|
+
connections[connections.index(prev)+1]
|
12
|
+
end
|
13
|
+
end # class Prioritized
|
14
|
+
end # module WebSocket
|
15
|
+
end # module XRBP
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
class RoundRobin < MultiConnection
|
4
|
+
def initialize(*urls)
|
5
|
+
super(*urls)
|
6
|
+
@current = 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def next_connection(prev=nil)
|
10
|
+
return nil unless prev.nil?
|
11
|
+
|
12
|
+
c = connections[@current]
|
13
|
+
@current += 1
|
14
|
+
@current = 0 if @current >= connections.size
|
15
|
+
c
|
16
|
+
end
|
17
|
+
end # class RoundRobin
|
18
|
+
end # module WebSocket
|
19
|
+
end # module XRBP
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Plugins
|
4
|
+
# Automatically reconnects on close events
|
5
|
+
class AutoConnect
|
6
|
+
attr_reader :connection
|
7
|
+
|
8
|
+
def initialize(connection)
|
9
|
+
@connection = connection
|
10
|
+
end
|
11
|
+
|
12
|
+
def added
|
13
|
+
return if connection.kind_of?(MultiConnection)
|
14
|
+
|
15
|
+
conn = connection
|
16
|
+
|
17
|
+
connection.on :completed do
|
18
|
+
connected = false
|
19
|
+
until conn.force_quit? || connected
|
20
|
+
begin
|
21
|
+
conn.connect
|
22
|
+
connected = true
|
23
|
+
rescue
|
24
|
+
conn.rsleep(3)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
until connection.force_quit? || connection.open?
|
30
|
+
begin
|
31
|
+
connection.connect
|
32
|
+
rescue
|
33
|
+
connection.rsleep(3)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end # class AutoConnect
|
38
|
+
|
39
|
+
WebSocket.register_plugin :autoconnect, AutoConnect
|
40
|
+
end # module Plugins
|
41
|
+
end # module WebSocket
|
42
|
+
end # module XRBP
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Plugins
|
4
|
+
# Dispatch commands (based on message dispatcher)
|
5
|
+
class CommandDispatcher < MessageDispatcher
|
6
|
+
def added
|
7
|
+
super
|
8
|
+
|
9
|
+
plugin = self
|
10
|
+
|
11
|
+
connection.define_instance_method(:cmd) do |cmd, &bl|
|
12
|
+
return next_connection.cmd cmd, &bl if self.kind_of?(MultiConnection)
|
13
|
+
|
14
|
+
cmd = Command.new(cmd) unless cmd.kind_of?(Command)
|
15
|
+
msg(cmd, &bl)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def match_message(msg)
|
20
|
+
begin
|
21
|
+
return nil if msg.data == ""
|
22
|
+
parsed = JSON.parse(msg.data)
|
23
|
+
|
24
|
+
rescue => e
|
25
|
+
return nil
|
26
|
+
end
|
27
|
+
|
28
|
+
id = parsed['id']
|
29
|
+
msg = messages.find { |msg| msg.kind_of?(Command) && msg.id == id }
|
30
|
+
|
31
|
+
return nil unless msg
|
32
|
+
[msg, parsed]
|
33
|
+
end
|
34
|
+
end # class CommandDispatcher
|
35
|
+
|
36
|
+
WebSocket.register_plugin :command_dispatcher, CommandDispatcher
|
37
|
+
end # module Plugins
|
38
|
+
end # module WebSocket
|
39
|
+
end # module XRBP
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Plugins
|
4
|
+
# Handles multi-page messages
|
5
|
+
class CommandPaginator
|
6
|
+
attr_reader :connection
|
7
|
+
|
8
|
+
def initialize(connection)
|
9
|
+
@connection = connection
|
10
|
+
end
|
11
|
+
|
12
|
+
def added
|
13
|
+
raise "Must also include CommandDispatcher plugin" unless connection.plugin?(CommandDispatcher)
|
14
|
+
end
|
15
|
+
|
16
|
+
def unlock!(cmd, res)
|
17
|
+
return true unless cmd.respond_to?(:paginate?) && cmd.paginate?
|
18
|
+
|
19
|
+
marker = res["result"]["marker"]
|
20
|
+
page = res["result"][cmd.page_title]
|
21
|
+
|
22
|
+
if marker && next_cmd = cmd.next_page(marker)
|
23
|
+
connection.cmd next_cmd do
|
24
|
+
page
|
25
|
+
end
|
26
|
+
|
27
|
+
else
|
28
|
+
# XXX can't recursively use stack to unwind
|
29
|
+
# callbacks as there may be too many pages.
|
30
|
+
# Do it serially.
|
31
|
+
res = Array.new(page)
|
32
|
+
cmd.each_ancestor { |page_cmd|
|
33
|
+
page_res = page_cmd.bl.call res
|
34
|
+
res = page_res + res if page_cmd.prev_cmd
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end # class CommandPaginator
|
41
|
+
|
42
|
+
WebSocket.register_plugin :command_paginator, CommandPaginator
|
43
|
+
end # module Plugins
|
44
|
+
end # module WebSocket
|
45
|
+
end # module XRBP
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Plugins
|
4
|
+
# Automatic disconnection if no server data in certain time
|
5
|
+
class ConnectionTimeout
|
6
|
+
attr_reader :connection
|
7
|
+
attr_accessor :connection_timeout
|
8
|
+
|
9
|
+
DEFAULT_TIMEOUT = 10
|
10
|
+
|
11
|
+
def initialize(connection)
|
12
|
+
@connection = connection
|
13
|
+
@connection_timeout = DEFAULT_TIMEOUT
|
14
|
+
end
|
15
|
+
|
16
|
+
def added
|
17
|
+
plugin = self
|
18
|
+
connection.define_instance_method(:connection_timeout=) do |t|
|
19
|
+
plugin.connection_timeout = t
|
20
|
+
|
21
|
+
self.plugins.find { |plg|
|
22
|
+
plg.is_a?(ConnectionTimeout)
|
23
|
+
}.connection_timeout = t if self.kind_of?(MultiConnection)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def message(msg)
|
28
|
+
@last_msg = Time.now
|
29
|
+
end
|
30
|
+
|
31
|
+
def opened
|
32
|
+
@thread = Thread.new {
|
33
|
+
@last_msg = Time.now
|
34
|
+
until connection.force_quit? ||
|
35
|
+
connection.closed? ||
|
36
|
+
Thread.current != @thread
|
37
|
+
if Time.now - @last_msg > @connection_timeout
|
38
|
+
connection.close!
|
39
|
+
end
|
40
|
+
|
41
|
+
connection.rsleep(0.1)
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def closed
|
47
|
+
@thread.join unless @thread == Thread.current
|
48
|
+
end
|
49
|
+
end # class ConnectionTimeout
|
50
|
+
|
51
|
+
WebSocket.register_plugin :connection_timeout, ConnectionTimeout
|
52
|
+
end # module Plugins
|
53
|
+
end # module WebSocket
|
54
|
+
end # module XRBP
|