xrbp 0.0.1
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 +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,141 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Plugins
|
4
|
+
# Dispatch messages & wait for responses (w/ optional timeout)
|
5
|
+
class MessageDispatcher
|
6
|
+
attr_reader :connection, :messages
|
7
|
+
attr_accessor :message_timeout
|
8
|
+
|
9
|
+
DEFAULT_TIMEOUT = 10
|
10
|
+
|
11
|
+
def initialize(connection)
|
12
|
+
@connection = connection
|
13
|
+
@message_timeout = DEFAULT_TIMEOUT
|
14
|
+
@messages = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def added
|
18
|
+
plugin = self
|
19
|
+
|
20
|
+
connection.define_instance_method(:message_timeout=) do |t|
|
21
|
+
plugin.message_timeout = t
|
22
|
+
|
23
|
+
self.plugins.find { |plg|
|
24
|
+
plg.kind_of?(MessageDipsatcher)
|
25
|
+
}.message_timeout = t if self.kind_of?(MultiConnection)
|
26
|
+
end
|
27
|
+
|
28
|
+
connection.define_instance_method(:msg) do |msg, &bl|
|
29
|
+
return next_connection.msg msg, &bl if self.kind_of?(MultiConnection)
|
30
|
+
|
31
|
+
msg = Message.new(msg) unless msg.kind_of?(Message)
|
32
|
+
msg.connection = self
|
33
|
+
msg.bl = bl if bl
|
34
|
+
|
35
|
+
unless self.open?
|
36
|
+
msg.bl.call nil if msg.bl
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
|
40
|
+
plugin.messages << msg
|
41
|
+
|
42
|
+
send_data msg.to_s
|
43
|
+
|
44
|
+
return nil if bl
|
45
|
+
msg.wait
|
46
|
+
msg.result
|
47
|
+
end
|
48
|
+
|
49
|
+
connection.on :close do
|
50
|
+
plugin.messages.each { |msg|
|
51
|
+
plugin.cancel_message(msg)
|
52
|
+
}
|
53
|
+
end unless connection.kind_of?(MultiConnection)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Should be overridden in subclass return
|
57
|
+
# request message & formatted response
|
58
|
+
# given raw response
|
59
|
+
def match_message(msg)
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return bool if message,response is read to be unlocked / returned to client.
|
64
|
+
# Allows other plugins to block message unlocking
|
65
|
+
def unlock!(req, res)
|
66
|
+
!connection.plugins.any? { |plg|
|
67
|
+
plg != self && plg.respond_to?(:unlock!) && !plg.unlock!(req, res)
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
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
|
+
def message(res)
|
80
|
+
req, res = match_message(res)
|
81
|
+
return unless req
|
82
|
+
messages.delete(req)
|
83
|
+
|
84
|
+
return unless unlock!(req, res)
|
85
|
+
|
86
|
+
begin
|
87
|
+
res = parse_result(res)
|
88
|
+
rescue Exception => e
|
89
|
+
if conn = connection.next_connection(req.connection)
|
90
|
+
conn.msg(req, &req.bl)
|
91
|
+
return
|
92
|
+
|
93
|
+
else
|
94
|
+
res = nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
req.bl.call(res)
|
99
|
+
end
|
100
|
+
|
101
|
+
def cancel_message(msg)
|
102
|
+
connection.state_mutex.synchronize {
|
103
|
+
messages.delete(msg)
|
104
|
+
msg.signal
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def opened
|
109
|
+
@timeout_thread = Thread.new {
|
110
|
+
# XXX remove force_quit? condition check from this loop,
|
111
|
+
# so we're sure messages always timeout, even on force quit.
|
112
|
+
# Always ensure close! is called after websocket is no longer
|
113
|
+
# being used!
|
114
|
+
until connection.closed? || Thread.current != @timeout_thread
|
115
|
+
now = Time.now
|
116
|
+
tmsgs = Array.new(messages)
|
117
|
+
tmsgs.each { |msg|
|
118
|
+
if now - msg.time > @message_timeout
|
119
|
+
connection.emit :timeout, msg
|
120
|
+
cancel_message(msg)
|
121
|
+
|
122
|
+
# XXX manually close the connection as
|
123
|
+
# a broken pipe will not stop websocket polling
|
124
|
+
connection.close!
|
125
|
+
end
|
126
|
+
}
|
127
|
+
|
128
|
+
connection.rsleep(0.1)
|
129
|
+
end
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
def closed
|
134
|
+
@timeout_thread.join unless @timeout_thread == Thread.current
|
135
|
+
end
|
136
|
+
end # class MessageDispatcher
|
137
|
+
|
138
|
+
WebSocket.register_plugin :message_dispatcher, MessageDispatcher
|
139
|
+
end # module Plugins
|
140
|
+
end # module WebSocket
|
141
|
+
end # module XRBP
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
module Plugins
|
4
|
+
class ResultParser
|
5
|
+
attr_accessor :parser, :connection
|
6
|
+
|
7
|
+
def initialize(connection)
|
8
|
+
@connection = connection
|
9
|
+
end
|
10
|
+
|
11
|
+
def added
|
12
|
+
plugin = self
|
13
|
+
connection.define_instance_method(:parse_results) do |&bl|
|
14
|
+
plugin.parser = bl
|
15
|
+
|
16
|
+
self.connections.each { |conn|
|
17
|
+
conn.parse_results &bl
|
18
|
+
} if self.kind_of?(MultiConnection)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse_result(res)
|
23
|
+
return res unless parser
|
24
|
+
parser.call(res)
|
25
|
+
end
|
26
|
+
end # class ResultParser
|
27
|
+
|
28
|
+
WebSocket.register_plugin :result_parser, ResultParser
|
29
|
+
end # module Plugins
|
30
|
+
end # module WebSocket
|
31
|
+
end # module XRBP
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module XRBP
|
2
|
+
module WebSocket
|
3
|
+
def self.plugins
|
4
|
+
@plugins ||= {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.register_plugin(label, cls)
|
8
|
+
plugins[label] = cls
|
9
|
+
end
|
10
|
+
end # module WebSocket
|
11
|
+
end # module XRPB
|
12
|
+
|
13
|
+
require_relative './plugins/autoconnect'
|
14
|
+
require_relative './plugins/connection_timeout'
|
15
|
+
require_relative './plugins/message_dispatcher'
|
16
|
+
require_relative './plugins/command_dispatcher'
|
17
|
+
require_relative './plugins/command_paginator'
|
18
|
+
require_relative './plugins/result_parser'
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'socket'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module XRBP
|
6
|
+
module WebSocket
|
7
|
+
class Socket
|
8
|
+
DEFAULT_PORTS = {:ws => 80,
|
9
|
+
:http => 80,
|
10
|
+
:wss => 443,
|
11
|
+
:https => 443}
|
12
|
+
|
13
|
+
|
14
|
+
attr_reader :connection, :socket, :pipe_broken
|
15
|
+
|
16
|
+
def options
|
17
|
+
connection.options
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(connection)
|
21
|
+
@connection = connection
|
22
|
+
@pipe_broken = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def connect
|
26
|
+
uri = URI.parse connection.url
|
27
|
+
host = uri.host
|
28
|
+
port = uri.port || DEFAULT_PORTS[uri.scheme.intern]
|
29
|
+
|
30
|
+
@socket = TCPSocket.new(host, port)
|
31
|
+
socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
32
|
+
|
33
|
+
init_ssl_socket if ['https', 'wss'].include? uri.scheme
|
34
|
+
end
|
35
|
+
|
36
|
+
def init_ssl_socket
|
37
|
+
ssl_context = options[:ssl_context] || begin
|
38
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
39
|
+
ctx.ssl_version = options[:ssl_version] || 'SSLv23'
|
40
|
+
|
41
|
+
# use VERIFY_PEER for verification:
|
42
|
+
ctx.verify_mode = options[:verify_mode] ||
|
43
|
+
OpenSSL::SSL::VERIFY_NONE
|
44
|
+
|
45
|
+
cert_store = OpenSSL::X509::Store.new
|
46
|
+
cert_store.set_default_paths
|
47
|
+
ctx.cert_store = cert_store
|
48
|
+
|
49
|
+
ctx
|
50
|
+
end
|
51
|
+
|
52
|
+
@socket = ::OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
|
53
|
+
socket.connect
|
54
|
+
end
|
55
|
+
|
56
|
+
###
|
57
|
+
|
58
|
+
def close
|
59
|
+
socket.close if socket
|
60
|
+
end
|
61
|
+
|
62
|
+
def write(data)
|
63
|
+
socket.write data
|
64
|
+
end
|
65
|
+
|
66
|
+
def write_nonblock(data)
|
67
|
+
begin
|
68
|
+
socket.write_nonblock(data)
|
69
|
+
|
70
|
+
rescue IO::WaitReadable
|
71
|
+
IO.select([socket]) # OpenSSL needs to read internally
|
72
|
+
retry
|
73
|
+
|
74
|
+
rescue IO::WaitWritable, Errno::EINTR
|
75
|
+
IO.select(nil, [socket])
|
76
|
+
retry
|
77
|
+
|
78
|
+
rescue Errno::EPIPE => e
|
79
|
+
@pipe_broken = true
|
80
|
+
raise
|
81
|
+
|
82
|
+
rescue OpenSSL::SSL::SSLError => e
|
83
|
+
@pipe_broken = true
|
84
|
+
raise
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def read_next(dest)
|
89
|
+
begin
|
90
|
+
read_sockets, _, _ = IO.select([socket], nil, nil, 10)
|
91
|
+
|
92
|
+
if read_sockets && read_sockets[0]
|
93
|
+
dest << socket.read_nonblock(1024)
|
94
|
+
|
95
|
+
if socket.respond_to?(:pending) # SSLSocket
|
96
|
+
dest << socket.read(socket.pending) while socket.pending > 0
|
97
|
+
end
|
98
|
+
end
|
99
|
+
rescue IO::WaitReadable
|
100
|
+
# No op
|
101
|
+
|
102
|
+
rescue IO::WaitWritable
|
103
|
+
IO.select(nil, [socket])
|
104
|
+
retry
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end # class Socket
|
108
|
+
end # module WebSocket
|
109
|
+
end # module XRBP
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'xrbp/websocket/socket'
|
2
|
+
require 'xrbp/websocket/client'
|
3
|
+
require 'xrbp/websocket/connection'
|
4
|
+
|
5
|
+
require 'xrbp/websocket/multi/multi_connection'
|
6
|
+
require 'xrbp/websocket/multi/round_robin'
|
7
|
+
require 'xrbp/websocket/multi/prioritized'
|
8
|
+
require 'xrbp/websocket/multi/fallback'
|
9
|
+
require 'xrbp/websocket/multi/parallel'
|
10
|
+
|
11
|
+
require 'xrbp/websocket/message'
|
12
|
+
require 'xrbp/websocket/command'
|
13
|
+
require 'xrbp/websocket/plugins'
|
14
|
+
|
15
|
+
require 'xrbp/websocket/cmds'
|
data/lib/xrbp.rb
ADDED
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xrbp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dev Null Productions
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-03-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: websocket
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: event_emitter
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.2'
|
41
|
+
description: Ruby XRP Tools
|
42
|
+
email:
|
43
|
+
- devnullproductions@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- LICENSE.txt
|
49
|
+
- README.md
|
50
|
+
- examples/autoconnect_timeout.rb
|
51
|
+
- examples/cmd.rb
|
52
|
+
- examples/ledger.rb
|
53
|
+
- examples/ledger_multi_subscribe.rb
|
54
|
+
- examples/ledger_subscribe.rb
|
55
|
+
- examples/multi.rb
|
56
|
+
- examples/paginate.rb
|
57
|
+
- examples/prioritized.rb
|
58
|
+
- examples/round_robin.rb
|
59
|
+
- examples/websocket.rb
|
60
|
+
- lib/xrbp.rb
|
61
|
+
- lib/xrbp/model.rb
|
62
|
+
- lib/xrbp/model/account.rb
|
63
|
+
- lib/xrbp/model/base.rb
|
64
|
+
- lib/xrbp/model/ledger.rb
|
65
|
+
- lib/xrbp/network.rb
|
66
|
+
- lib/xrbp/network/nodes.rb
|
67
|
+
- lib/xrbp/version.rb
|
68
|
+
- lib/xrbp/websocket.rb
|
69
|
+
- lib/xrbp/websocket/client.rb
|
70
|
+
- lib/xrbp/websocket/cmds.rb
|
71
|
+
- lib/xrbp/websocket/cmds/account_info.rb
|
72
|
+
- lib/xrbp/websocket/cmds/account_lines.rb
|
73
|
+
- lib/xrbp/websocket/cmds/account_objects.rb
|
74
|
+
- lib/xrbp/websocket/cmds/account_offers.rb
|
75
|
+
- lib/xrbp/websocket/cmds/account_tx.rb
|
76
|
+
- lib/xrbp/websocket/cmds/book_offers.rb
|
77
|
+
- lib/xrbp/websocket/cmds/ledger.rb
|
78
|
+
- lib/xrbp/websocket/cmds/ledger_entry.rb
|
79
|
+
- lib/xrbp/websocket/cmds/paginated.rb
|
80
|
+
- lib/xrbp/websocket/cmds/server_info.rb
|
81
|
+
- lib/xrbp/websocket/cmds/subscribe.rb
|
82
|
+
- lib/xrbp/websocket/command.rb
|
83
|
+
- lib/xrbp/websocket/connection.rb
|
84
|
+
- lib/xrbp/websocket/has_plugin.rb
|
85
|
+
- lib/xrbp/websocket/message.rb
|
86
|
+
- lib/xrbp/websocket/multi/fallback.rb
|
87
|
+
- lib/xrbp/websocket/multi/multi_connection.rb
|
88
|
+
- lib/xrbp/websocket/multi/parallel.rb
|
89
|
+
- lib/xrbp/websocket/multi/prioritized.rb
|
90
|
+
- lib/xrbp/websocket/multi/round_robin.rb
|
91
|
+
- lib/xrbp/websocket/plugins.rb
|
92
|
+
- lib/xrbp/websocket/plugins/autoconnect.rb
|
93
|
+
- lib/xrbp/websocket/plugins/command_dispatcher.rb
|
94
|
+
- lib/xrbp/websocket/plugins/command_paginator.rb
|
95
|
+
- lib/xrbp/websocket/plugins/connection_timeout.rb
|
96
|
+
- lib/xrbp/websocket/plugins/message_dispatcher.rb
|
97
|
+
- lib/xrbp/websocket/plugins/result_parser.rb
|
98
|
+
- lib/xrbp/websocket/socket.rb
|
99
|
+
homepage: https://devnull.network
|
100
|
+
licenses:
|
101
|
+
- MIT
|
102
|
+
metadata: {}
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 2.7.6
|
120
|
+
signing_key:
|
121
|
+
specification_version: 4
|
122
|
+
summary: Helper module to read and write data from the XRP Ledger and related resources!
|
123
|
+
test_files: []
|