xrbp 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +103 -5
- data/examples/accounts.rb +13 -0
- data/examples/autoconnect_timeout.rb +5 -1
- data/examples/autorety.rb +7 -0
- data/examples/crawl_nodes.rb +32 -0
- data/examples/dsl/account.rb +16 -0
- data/examples/dsl/ledger.rb +7 -0
- data/examples/dsl/ledger_subscribe.rb +18 -0
- data/examples/dsl/validators.rb +8 -0
- data/examples/gateways.rb +8 -0
- data/examples/latest_account.rb +4 -0
- data/examples/ledger_multi_subscribe.rb +1 -1
- data/examples/ledger_subscribe.rb +2 -2
- data/examples/market.rb +13 -0
- data/examples/username.rb +12 -0
- data/examples/validator.rb +8 -0
- data/lib/xrbp.rb +5 -1
- data/lib/xrbp/common.rb +10 -0
- data/lib/xrbp/core_ext.rb +24 -0
- data/lib/xrbp/dsl.rb +25 -0
- data/lib/xrbp/dsl/accounts.rb +13 -0
- data/lib/xrbp/dsl/ledgers.rb +23 -0
- data/lib/xrbp/dsl/validators.rb +10 -0
- data/lib/xrbp/dsl/webclient.rb +8 -0
- data/lib/xrbp/dsl/websocket.rb +28 -0
- data/lib/xrbp/model.rb +4 -0
- data/lib/xrbp/model/account.rb +142 -1
- data/lib/xrbp/model/base.rb +1 -0
- data/lib/xrbp/model/gateway.rb +24 -0
- data/lib/xrbp/model/ledger.rb +30 -1
- data/lib/xrbp/model/market.rb +52 -0
- data/lib/xrbp/model/node.rb +131 -0
- data/lib/xrbp/model/parsers/account.rb +44 -0
- data/lib/xrbp/model/parsers/gateway.rb +40 -0
- data/lib/xrbp/model/parsers/market.rb +28 -0
- data/lib/xrbp/model/parsers/node.rb +19 -0
- data/lib/xrbp/model/parsers/quote.rb +47 -0
- data/lib/xrbp/model/parsers/validator.rb +25 -0
- data/lib/xrbp/model/validator.rb +24 -0
- data/lib/xrbp/plugins.rb +6 -0
- data/lib/xrbp/plugins/base.rb +10 -0
- data/lib/xrbp/plugins/has_plugin.rb +45 -0
- data/lib/xrbp/plugins/has_result_parsers.rb +27 -0
- data/lib/xrbp/plugins/plugin_registry.rb +20 -0
- data/lib/xrbp/plugins/result_parser.rb +19 -0
- data/lib/xrbp/terminatable.rb +19 -0
- data/lib/xrbp/thread_registry.rb +22 -0
- data/lib/xrbp/version.rb +1 -1
- data/lib/xrbp/webclient.rb +2 -0
- data/lib/xrbp/webclient/connection.rb +100 -0
- data/lib/xrbp/webclient/plugins.rb +8 -0
- data/lib/xrbp/webclient/plugins/autoretry.rb +54 -0
- data/lib/xrbp/webclient/plugins/result_parser.rb +23 -0
- data/lib/xrbp/websocket/client.rb +85 -24
- data/lib/xrbp/websocket/cmds/account_info.rb +4 -0
- data/lib/xrbp/websocket/cmds/account_lines.rb +5 -0
- data/lib/xrbp/websocket/cmds/account_objects.rb +4 -0
- data/lib/xrbp/websocket/cmds/account_offers.rb +5 -0
- data/lib/xrbp/websocket/cmds/account_tx.rb +4 -0
- data/lib/xrbp/websocket/cmds/book_offers.rb +4 -0
- data/lib/xrbp/websocket/cmds/ledger.rb +3 -0
- data/lib/xrbp/websocket/cmds/ledger_entry.rb +4 -0
- data/lib/xrbp/websocket/cmds/paginated.rb +4 -1
- data/lib/xrbp/websocket/cmds/server_info.rb +4 -0
- data/lib/xrbp/websocket/cmds/subscribe.rb +4 -0
- data/lib/xrbp/websocket/command.rb +11 -0
- data/lib/xrbp/websocket/connection.rb +75 -22
- data/lib/xrbp/websocket/message.rb +5 -2
- data/lib/xrbp/websocket/multi/fallback.rb +2 -4
- data/lib/xrbp/websocket/multi/multi_connection.rb +37 -2
- data/lib/xrbp/websocket/multi/parallel.rb +2 -4
- data/lib/xrbp/websocket/multi/prioritized.rb +2 -4
- data/lib/xrbp/websocket/multi/round_robin.rb +4 -0
- data/lib/xrbp/websocket/plugins.rb +1 -7
- data/lib/xrbp/websocket/plugins/autoconnect.rb +25 -5
- data/lib/xrbp/websocket/plugins/command_dispatcher.rb +5 -0
- data/lib/xrbp/websocket/plugins/command_paginator.rb +9 -8
- data/lib/xrbp/websocket/plugins/connection_timeout.rb +27 -16
- data/lib/xrbp/websocket/plugins/message_dispatcher.rb +60 -30
- data/lib/xrbp/websocket/plugins/result_parser.rb +19 -19
- data/lib/xrbp/websocket/socket.rb +23 -6
- metadata +118 -8
- data/lib/xrbp/network.rb +0 -6
- data/lib/xrbp/network/nodes.rb +0 -8
- data/lib/xrbp/websocket/has_plugin.rb +0 -30
@@ -1,8 +1,10 @@
|
|
1
1
|
module XRBP
|
2
2
|
module WebSocket
|
3
|
+
# Raw data which to write to websocket and mechanisms
|
4
|
+
# which to track and manage response state.
|
3
5
|
class Message
|
4
|
-
attr_reader :result
|
5
|
-
attr_accessor :connection
|
6
|
+
attr_reader :result
|
7
|
+
attr_accessor :time, :connection
|
6
8
|
|
7
9
|
def initialize(data)
|
8
10
|
@data = data
|
@@ -19,6 +21,7 @@ module XRBP
|
|
19
21
|
def signal
|
20
22
|
@signalled = true
|
21
23
|
@cv.signal
|
24
|
+
self
|
22
25
|
end
|
23
26
|
|
24
27
|
def wait
|
@@ -1,10 +1,8 @@
|
|
1
1
|
module XRBP
|
2
2
|
module WebSocket
|
3
|
+
# MultiConnection strategy where connections are tried sequentially
|
4
|
+
# until one is found that is open & succeeds
|
3
5
|
class Fallback < MultiConnection
|
4
|
-
def initialize(*urls)
|
5
|
-
super(*urls)
|
6
|
-
end
|
7
|
-
|
8
6
|
def next_connection(prev=nil)
|
9
7
|
unless prev.nil?
|
10
8
|
return nil if connections.last == prev
|
@@ -1,13 +1,31 @@
|
|
1
|
-
require_relative '../has_plugin'
|
2
|
-
|
3
1
|
module XRBP
|
4
2
|
module WebSocket
|
3
|
+
# Base class facilitating transparent multiple
|
4
|
+
# connection dispatching. This provides mechanism which
|
5
|
+
# to instantiate multiple WebSocket::Connection instances
|
6
|
+
# proxying requests to them depending on the *next_connection*
|
7
|
+
# selected.
|
8
|
+
#
|
9
|
+
# This class provides all the common logic to manage
|
10
|
+
# multiple connections. Subclasses should override and
|
11
|
+
# implement *next_connection* specifying the strategy
|
12
|
+
# used to select the connection to use for any given
|
13
|
+
# request.
|
5
14
|
class MultiConnection
|
6
15
|
include EventEmitter
|
7
16
|
include HasPlugin
|
8
17
|
|
18
|
+
def plugin_namespace
|
19
|
+
WebSocket
|
20
|
+
end
|
21
|
+
|
9
22
|
attr_reader :connections
|
10
23
|
|
24
|
+
# MultiConnection initializer taking list of urls which
|
25
|
+
# to connect to
|
26
|
+
#
|
27
|
+
# @param urls [Array<String>] list of urls which to establish
|
28
|
+
# connections to
|
11
29
|
def initialize(*urls)
|
12
30
|
@connections = []
|
13
31
|
|
@@ -20,14 +38,31 @@ module XRBP
|
|
20
38
|
yield self if block_given?
|
21
39
|
end
|
22
40
|
|
41
|
+
# Force terminate all connections
|
23
42
|
def force_quit!
|
24
43
|
connections.each { |c| c.force_quit! }
|
25
44
|
end
|
26
45
|
|
46
|
+
# Close all connections
|
27
47
|
def close!
|
28
48
|
connections.each { |c| c.close! }
|
29
49
|
end
|
30
50
|
|
51
|
+
# Block until all connections are openend
|
52
|
+
def wait_for_open
|
53
|
+
connections.each { |c| c.wait_for_open }
|
54
|
+
end
|
55
|
+
|
56
|
+
# Block until all connections are closed
|
57
|
+
def wait_for_close
|
58
|
+
connections.each { |c| c.wait_for_close }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Block until all connections are completed
|
62
|
+
def wait_for_completed
|
63
|
+
connections.each { |c| c.wait_for_completed }
|
64
|
+
end
|
65
|
+
|
31
66
|
alias :_add_plugin :add_plugin
|
32
67
|
|
33
68
|
def add_plugin(*plg)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module XRBP
|
2
2
|
module WebSocket
|
3
|
+
# MultiConnection strategy where requests are sent to
|
4
|
+
# all connections in parallel.
|
3
5
|
class Parallel < MultiConnection
|
4
6
|
class All
|
5
7
|
attr_accessor :connections
|
@@ -15,10 +17,6 @@ module XRBP
|
|
15
17
|
end
|
16
18
|
end # class All
|
17
19
|
|
18
|
-
def initialize(*urls)
|
19
|
-
super(*urls)
|
20
|
-
end
|
21
|
-
|
22
20
|
def next_connection(prev=nil)
|
23
21
|
return nil unless prev.nil?
|
24
22
|
All.new connections
|
@@ -1,10 +1,8 @@
|
|
1
1
|
module XRBP
|
2
2
|
module WebSocket
|
3
|
+
# MultiConnection strategy where connections are tried
|
4
|
+
# sequentially until one succeeds
|
3
5
|
class Prioritized < MultiConnection
|
4
|
-
def initialize(*urls)
|
5
|
-
super(*urls)
|
6
|
-
end
|
7
|
-
|
8
6
|
def next_connection(prev=nil)
|
9
7
|
return nil if prev == connections.last
|
10
8
|
return super if prev.nil?
|
@@ -1,5 +1,9 @@
|
|
1
1
|
module XRBP
|
2
2
|
module WebSocket
|
3
|
+
# MultiConnection strategy where connections selected in
|
4
|
+
# a circular-round robin manner, where the next connection
|
5
|
+
# is always used for the next request even if the current
|
6
|
+
# one succeeds.
|
3
7
|
class RoundRobin < MultiConnection
|
4
8
|
def initialize(*urls)
|
5
9
|
super(*urls)
|
@@ -1,22 +1,42 @@
|
|
1
1
|
module XRBP
|
2
2
|
module WebSocket
|
3
3
|
module Plugins
|
4
|
-
# Automatically
|
5
|
-
|
6
|
-
|
4
|
+
# Automatically connects on instantiation and reconnects
|
5
|
+
# on close events.
|
6
|
+
#
|
7
|
+
# @example autoconnecting
|
8
|
+
# connection = WebSocket::Connection.new "wss://s1.ripple.com:443"
|
9
|
+
# connection.add_plugin :autoconnect
|
10
|
+
# connection.open? # => true
|
11
|
+
class AutoConnect < PluginBase
|
12
|
+
attr_accessor :reconnect_delay
|
7
13
|
|
8
14
|
def initialize(connection)
|
9
|
-
|
15
|
+
super(connection)
|
16
|
+
@reconnect_delay = nil
|
10
17
|
end
|
11
18
|
|
12
19
|
def added
|
20
|
+
plugin = self
|
21
|
+
|
22
|
+
connection.define_instance_method(:reconnect_delay=) do |d|
|
23
|
+
plugin.reconnect_delay = d
|
24
|
+
|
25
|
+
connections.each{ |c|
|
26
|
+
c.plugin(AutoConnect)
|
27
|
+
.reconnect_delay = d
|
28
|
+
} if self.kind_of?(MultiConnection)
|
29
|
+
end
|
30
|
+
|
13
31
|
return if connection.kind_of?(MultiConnection)
|
14
32
|
|
15
33
|
conn = connection
|
16
|
-
|
17
34
|
connection.on :completed do
|
18
35
|
connected = false
|
19
36
|
until conn.force_quit? || connected
|
37
|
+
conn.rsleep(plugin.reconnect_delay) if plugin.reconnect_delay
|
38
|
+
next if conn.force_quit?
|
39
|
+
|
20
40
|
begin
|
21
41
|
conn.connect
|
22
42
|
connected = true
|
@@ -2,6 +2,11 @@ module XRBP
|
|
2
2
|
module WebSocket
|
3
3
|
module Plugins
|
4
4
|
# Dispatch commands (based on message dispatcher)
|
5
|
+
#
|
6
|
+
# @example dispatching server info command
|
7
|
+
# connection = WebSocket::Connection.new "wss://s1.ripple.com:443"
|
8
|
+
# connection.add_plugin :command_dispatcher
|
9
|
+
# puts connection.cmd(WebSocket::Cmds::ServerInfo.new)
|
5
10
|
class CommandDispatcher < MessageDispatcher
|
6
11
|
def added
|
7
12
|
super
|
@@ -1,20 +1,21 @@
|
|
1
1
|
module XRBP
|
2
2
|
module WebSocket
|
3
3
|
module Plugins
|
4
|
-
# Handles multi-page
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
# Handles multi-page responses, automatically issuing subsequent requests
|
5
|
+
# when more data is available and concatinating results.
|
6
|
+
#
|
7
|
+
# This is most useful with account transaction and object lists where a
|
8
|
+
# single account may be associated with more data than can returned in a
|
9
|
+
# single result. In this case response will include pagination marker
|
10
|
+
# which we leverage here to retrieve all data.
|
11
|
+
class CommandPaginator < PluginBase
|
12
12
|
def added
|
13
13
|
raise "Must also include CommandDispatcher plugin" unless connection.plugin?(CommandDispatcher)
|
14
14
|
end
|
15
15
|
|
16
16
|
def unlock!(cmd, res)
|
17
17
|
return true unless cmd.respond_to?(:paginate?) && cmd.paginate?
|
18
|
+
return true unless res["result"] # unlock if we cannot get result
|
18
19
|
|
19
20
|
marker = res["result"]["marker"]
|
20
21
|
page = res["result"][cmd.page_title]
|
@@ -2,25 +2,39 @@ module XRBP
|
|
2
2
|
module WebSocket
|
3
3
|
module Plugins
|
4
4
|
# Automatic disconnection if no server data in certain time
|
5
|
-
|
6
|
-
|
5
|
+
#
|
6
|
+
# @example timed out connection
|
7
|
+
# connection = WebSocket::Connection.new "wss://s1.ripple.com:443"
|
8
|
+
# connection.add_plugin :connection_timeout
|
9
|
+
# connection.connection_timeout = 3
|
10
|
+
# connection.connect
|
11
|
+
# sleep(3)
|
12
|
+
# connection.closed? # => true
|
13
|
+
class ConnectionTimeout < PluginBase
|
14
|
+
include Terminatable
|
15
|
+
|
7
16
|
attr_accessor :connection_timeout
|
8
17
|
|
9
18
|
DEFAULT_TIMEOUT = 10
|
10
19
|
|
11
20
|
def initialize(connection)
|
12
|
-
|
21
|
+
super(connection)
|
13
22
|
@connection_timeout = DEFAULT_TIMEOUT
|
14
23
|
end
|
15
24
|
|
25
|
+
def timeout?
|
26
|
+
Time.now - @last_msg > @connection_timeout
|
27
|
+
end
|
28
|
+
|
16
29
|
def added
|
17
30
|
plugin = self
|
18
31
|
connection.define_instance_method(:connection_timeout=) do |t|
|
19
32
|
plugin.connection_timeout = t
|
20
33
|
|
21
|
-
|
22
|
-
|
23
|
-
|
34
|
+
connections.each{ |c|
|
35
|
+
c.plugin(ConnectionTimeout)
|
36
|
+
.connection_timeout = t
|
37
|
+
} if self.kind_of?(MultiConnection)
|
24
38
|
end
|
25
39
|
end
|
26
40
|
|
@@ -29,22 +43,19 @@ module XRBP
|
|
29
43
|
end
|
30
44
|
|
31
45
|
def opened
|
32
|
-
|
46
|
+
connection.add_work do
|
33
47
|
@last_msg = Time.now
|
34
|
-
until
|
35
|
-
connection.
|
36
|
-
|
37
|
-
|
38
|
-
connection.close!
|
39
|
-
end
|
40
|
-
|
48
|
+
until terminate? ||
|
49
|
+
connection.force_quit? ||
|
50
|
+
connection.closed?
|
51
|
+
connection.async_close! if timeout?
|
41
52
|
connection.rsleep(0.1)
|
42
53
|
end
|
43
|
-
|
54
|
+
end
|
44
55
|
end
|
45
56
|
|
46
57
|
def closed
|
47
|
-
|
58
|
+
terminate!
|
48
59
|
end
|
49
60
|
end # class ConnectionTimeout
|
50
61
|
|
@@ -1,15 +1,30 @@
|
|
1
1
|
module XRBP
|
2
2
|
module WebSocket
|
3
3
|
module Plugins
|
4
|
-
# Dispatch messages & wait for responses (w/ optional timeout)
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
# Dispatch messages & wait for responses (w/ optional timeout).
|
5
|
+
# This module allows the client to track messages sent to the server,
|
6
|
+
# waiting for responses up to a maximum time. An overridable callback
|
7
|
+
# method is provided to match responses to messages. Most often the
|
8
|
+
# end-user will not use this plugin directly but rather through
|
9
|
+
# CommandDispatcher which inherits it / extends it to issue and
|
10
|
+
# track structured commands.
|
11
|
+
#
|
12
|
+
# @see CommandDispatcher
|
13
|
+
class MessageDispatcher < PluginBase
|
14
|
+
include Terminatable
|
15
|
+
include HasResultParsers
|
8
16
|
|
9
17
|
DEFAULT_TIMEOUT = 10
|
10
18
|
|
19
|
+
def parsing_plugins
|
20
|
+
connection.plugins
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :messages
|
24
|
+
attr_accessor :message_timeout
|
25
|
+
|
11
26
|
def initialize(connection)
|
12
|
-
|
27
|
+
super(connection)
|
13
28
|
@message_timeout = DEFAULT_TIMEOUT
|
14
29
|
@messages = []
|
15
30
|
end
|
@@ -20,9 +35,10 @@ module XRBP
|
|
20
35
|
connection.define_instance_method(:message_timeout=) do |t|
|
21
36
|
plugin.message_timeout = t
|
22
37
|
|
23
|
-
|
24
|
-
|
25
|
-
|
38
|
+
connections.each{ |c|
|
39
|
+
c.plugin(MessageDispatcher)
|
40
|
+
.message_timeout = t
|
41
|
+
} if self.kind_of?(MultiConnection)
|
26
42
|
end
|
27
43
|
|
28
44
|
connection.define_instance_method(:msg) do |msg, &bl|
|
@@ -30,11 +46,19 @@ module XRBP
|
|
30
46
|
|
31
47
|
msg = Message.new(msg) unless msg.kind_of?(Message)
|
32
48
|
msg.connection = self
|
49
|
+
msg.time = Time.now
|
33
50
|
msg.bl = bl if bl
|
34
51
|
|
35
52
|
unless self.open?
|
36
|
-
|
37
|
-
|
53
|
+
if plugin.try_next(msg)
|
54
|
+
return nil if bl
|
55
|
+
msg.wait
|
56
|
+
return msg.result
|
57
|
+
|
58
|
+
else
|
59
|
+
msg.bl.call nil if bl
|
60
|
+
return nil
|
61
|
+
end
|
38
62
|
end
|
39
63
|
|
40
64
|
plugin.messages << msg
|
@@ -47,9 +71,7 @@ module XRBP
|
|
47
71
|
end
|
48
72
|
|
49
73
|
connection.on :close do
|
50
|
-
plugin.
|
51
|
-
plugin.cancel_message(msg)
|
52
|
-
}
|
74
|
+
plugin.cancel_all_messages
|
53
75
|
end unless connection.kind_of?(MultiConnection)
|
54
76
|
end
|
55
77
|
|
@@ -68,14 +90,6 @@ module XRBP
|
|
68
90
|
}
|
69
91
|
end
|
70
92
|
|
71
|
-
# Allows plugins to convert results
|
72
|
-
def parse_result(res)
|
73
|
-
connection.plugins.each { |plg|
|
74
|
-
res = plg.parse_result(res) if plg != self && plg.respond_to?(:parse_result)
|
75
|
-
}
|
76
|
-
res
|
77
|
-
end
|
78
|
-
|
79
93
|
def message(res)
|
80
94
|
req, res = match_message(res)
|
81
95
|
return unless req
|
@@ -84,10 +98,9 @@ module XRBP
|
|
84
98
|
return unless unlock!(req, res)
|
85
99
|
|
86
100
|
begin
|
87
|
-
res = parse_result(res)
|
101
|
+
res = parse_result(res, req)
|
88
102
|
rescue Exception => e
|
89
|
-
if
|
90
|
-
conn.msg(req, &req.bl)
|
103
|
+
if try_next(req)
|
91
104
|
return
|
92
105
|
|
93
106
|
else
|
@@ -98,6 +111,14 @@ module XRBP
|
|
98
111
|
req.bl.call(res)
|
99
112
|
end
|
100
113
|
|
114
|
+
def try_next(msg)
|
115
|
+
conn = connection.next_connection(msg.connection)
|
116
|
+
return false unless !!conn
|
117
|
+
messages.delete(msg)
|
118
|
+
conn.msg(msg, &msg.bl)
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
101
122
|
def cancel_message(msg)
|
102
123
|
connection.state_mutex.synchronize {
|
103
124
|
messages.delete(msg)
|
@@ -105,33 +126,42 @@ module XRBP
|
|
105
126
|
}
|
106
127
|
end
|
107
128
|
|
129
|
+
def cancel_all_messages
|
130
|
+
messages.each { |msg|
|
131
|
+
cancel_message(msg)
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
public
|
136
|
+
|
108
137
|
def opened
|
109
|
-
|
138
|
+
connection.add_work do
|
110
139
|
# XXX remove force_quit? condition check from this loop,
|
111
140
|
# so we're sure messages always timeout, even on force quit.
|
112
141
|
# Always ensure close! is called after websocket is no longer
|
113
142
|
# being used!
|
114
|
-
until
|
143
|
+
until terminate? || connection.closed?
|
115
144
|
now = Time.now
|
116
145
|
tmsgs = Array.new(messages)
|
117
146
|
tmsgs.each { |msg|
|
118
147
|
if now - msg.time > @message_timeout
|
119
148
|
connection.emit :timeout, msg
|
120
|
-
|
149
|
+
|
150
|
+
cancel_message(msg) unless try_next(msg)
|
121
151
|
|
122
152
|
# XXX manually close the connection as
|
123
153
|
# a broken pipe will not stop websocket polling
|
124
|
-
connection.
|
154
|
+
connection.async_close!
|
125
155
|
end
|
126
156
|
}
|
127
157
|
|
128
158
|
connection.rsleep(0.1)
|
129
159
|
end
|
130
|
-
|
160
|
+
end
|
131
161
|
end
|
132
162
|
|
133
163
|
def closed
|
134
|
-
|
164
|
+
terminate!
|
135
165
|
end
|
136
166
|
end # class MessageDispatcher
|
137
167
|
|