pry-remote-em 1.1.0-java

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.
@@ -0,0 +1,39 @@
1
+ require 'pry-remote-em/client/generic'
2
+
3
+ module PryRemoteEm
4
+ module Client
5
+ module Broker
6
+ include Client::Generic
7
+ include EM::Deferrable
8
+
9
+ def log
10
+ return opts[:logger] if opts[:logger]
11
+ @log ||= Logger.new(STDERR)
12
+ end
13
+
14
+ def receive_banner(name, version, scheme)
15
+ if super(name, version, scheme)
16
+ @opts[:tls] ? start_tls : succeed(self)
17
+ end
18
+ end
19
+
20
+ def ssl_handshake_completed
21
+ succeed(self)
22
+ end
23
+
24
+ def unbind
25
+ return if EventMachine.stopping?
26
+
27
+ # Give the existing broker a little time to release the port. Even if the
28
+ # restart here fails the next time a server tries to register, a new client
29
+ # will be created; when that fails Broker#restart will be called again.
30
+ timeout = ENV['PRYEMBROKERTIMEOUT'].nil? || ENV['PRYEMBROKERTIMEOUT'].empty? ? RECONNECT_TO_BROKER_TIMEOUT : ENV['PRYEMBROKERTIMEOUT']
31
+ log.info("[pry-remote-em broker-client] broker connection unbound; starting a new one in a #{timeout} seconds")
32
+ EM::Timer.new(timeout) do
33
+ PryRemoteEm::Broker.restart
34
+ end
35
+ end
36
+
37
+ end # module::Broker
38
+ end # module::Client
39
+ end # module::PryRemoteEm
@@ -0,0 +1,74 @@
1
+ require 'pry-remote-em/proto'
2
+
3
+ module PryRemoteEm
4
+ module Client
5
+ module Generic
6
+ include EM::Deferrable
7
+ include Proto
8
+
9
+ def initialize(opts = {})
10
+ @opts = opts
11
+ end
12
+
13
+ def opts
14
+ @opts ||= {}
15
+ end
16
+
17
+ def log
18
+ @log ||= Class.new do
19
+ def print(str); $stderr.puts(str) end
20
+ alias :info :print
21
+ alias :warn :print
22
+ alias :error :print
23
+ alias :debug :print
24
+ end.new
25
+ end
26
+
27
+ def start_tls
28
+ return if @tls_started
29
+ @tls_started = true
30
+ log.info('[pry-remote-em] negotiating TLS')
31
+ super(opts[:tls].is_a?(Hash) ? opts[:tls] : {})
32
+ end
33
+
34
+ def connection_completed
35
+ if get_peername
36
+ port, ip = Socket.unpack_sockaddr_in(get_peername)
37
+ log.info("[pry-remote-em] client connected to pryem://#{ip}:#{port}/")
38
+ else
39
+ # TODO use the args used to create this connection
40
+ log.info('[pry-remote-em] client connected')
41
+ end
42
+ timeout = ENV['PRYEMNEGOTIMEOUT'].nil? || ENV['PRYEMNEGOTIMEOUT'].empty? ? NEGOTIATION_TIMEOUT : ENV['PRYEMNEGOTIMEOUT']
43
+ @nego_timer = EM::Timer.new(timeout) do
44
+ fail("[pry-remote-em] server didn't finish negotiation within #{timeout} seconds; terminating")
45
+ end
46
+ end
47
+
48
+ def receive_banner(name, version, scheme)
49
+ log.info("[pry-remote-em] remote is #{name} #{version} #{scheme}")
50
+ client_ver = Gem::Version.new(PryRemoteEm::VERSION)
51
+ server_req = Gem::Requirement.new("~>#{version}")
52
+ server_ver = Gem::Version.new(version)
53
+ client_req = Gem::Requirement.new("~>#{PryRemoteEm::VERSION}")
54
+ unless server_req.satisfied_by?(client_ver) || client_req.satisfied_by?(server_ver)
55
+ fail("[pry-remote-em] incompatible version #{PryRemoteEm::VERSION}")
56
+ return false
57
+ end
58
+ if scheme.nil? || scheme != (reqscheme = opts[:tls] ? 'pryems' : 'pryem')
59
+ if scheme == 'pryems' && defined?(::OpenSSL)
60
+ opts[:tls] = true
61
+ else
62
+ fail("[pry-remote-em] server doesn't support required scheme #{reqscheme.dump}")
63
+ return false
64
+ end
65
+ end
66
+ @negotiated = true
67
+ @nego_timer.cancel
68
+ true
69
+ end
70
+
71
+
72
+ end # module::Generic
73
+ end # module::Client
74
+ end # module::PryRemoteEm
@@ -0,0 +1,193 @@
1
+ require 'highline'
2
+
3
+ module PryRemoteEm
4
+ module Client
5
+ module InteractiveMenu
6
+ def choose_server(list)
7
+ highline = HighLine.new
8
+ choice = nil
9
+ url = nil
10
+ nm_col_len = list.map { |id, server| server['name'].size }.max
11
+ sc_col = list.map { |id, server| second_column_for_server(server) }
12
+ sc_col_name = opts[:show_details] == '@' ? 'details' : opts[:show_details] || 'url'
13
+ sc_col_len = [sc_col.flatten.map(&:size).max || 1, sc_col_name.size].max
14
+ show_th_col = opts[:show_metrics] || list.any? { |id, server| server.has_key?('metrics') && server['metrics']['errors'] }
15
+ if show_th_col
16
+ th_col = list.map { |id, server| third_column_for_server(server) }
17
+ th_col_name = opts[:show_metrics] == '@' ? 'metrics' : opts[:show_metrics] || 'errors'
18
+ th_col_len = [th_col.flatten.map(&:size).max || 1, th_col_name.size].max
19
+ end
20
+ formatter = "| %-3s | %-#{nm_col_len}s | %-#{sc_col_len}s |#{" %-#{th_col_len}s |" if show_th_col}"
21
+ header = sprintf(formatter, '', 'name', sc_col_name, *th_col_name)
22
+ border = ('-' * header.length)
23
+ table = [border, header, border]
24
+ list = list.to_a
25
+ list = filter_server_list(list)
26
+ list = sort_server_list(list)
27
+ list.each.with_index do |(id, server), index|
28
+ index_column = [(index + 1).to_s]
29
+ first_column = [server['name']]
30
+ second_column = second_column_for_server(server)
31
+ third_column = third_column_for_server(server) if show_th_col
32
+
33
+ lines = show_th_col ? [second_column.size, third_column.size].max : second_column.size
34
+
35
+ lines.times do |index|
36
+ table << sprintf(formatter, index_column[index] || '', first_column[index] || '', second_column[index] || '', *(show_th_col ? third_column[index] || '' : nil))
37
+ end
38
+ end
39
+ table << border
40
+ table = table.join("\n")
41
+ Kernel.puts table
42
+
43
+ proxy = if (choice = opts.delete(:proxy))
44
+ true
45
+ elsif (choice = opts.delete(:connect))
46
+ false
47
+ elsif opts.delete(:proxy_by_default)
48
+ true
49
+ else
50
+ false
51
+ end
52
+
53
+ while url.nil?
54
+ if proxy
55
+ question = "(q) to quit; (r) to refresh; (c) to connect without proxy\nproxy to: "
56
+ else
57
+ question = "(q) to quit; (r) to refresh; (p) to proxy\nconnect to: "
58
+ end
59
+
60
+ choice = highline.ask(question)
61
+
62
+ return close_connection if ['q', 'quit', 'exit'].include?(choice.downcase)
63
+ if ['r', 'reload', 'refresh'].include?(choice.downcase)
64
+ send_server_reload_list
65
+ return nil
66
+ end
67
+ if ['c', 'connect'].include?(choice.downcase)
68
+ proxy = false
69
+ choice = nil
70
+ next
71
+ end
72
+ if ['p', 'proxy'].include?(choice.downcase)
73
+ proxy = true
74
+ choice = nil
75
+ next
76
+ end
77
+
78
+ choice = choice[/^\d+$/] ?
79
+ list[choice.to_i - 1] :
80
+ list.detect { |(id, server)| choice == id || choice == server['name'] || server['urls'].include?(choice) }
81
+
82
+ if choice
83
+ id, server = *choice
84
+ urls = filtered_urls_list_for_server(server)
85
+ url = if urls.size > 1
86
+ choose_url(urls)
87
+ elsif urls.size == 1
88
+ urls.first
89
+ else
90
+ log.error("\033[31mno #{'non-localhost ' if opts[:ignore_localhost]}urls for this server\033[0m")
91
+ nil
92
+ end
93
+ else
94
+ log.error("\033[31mserver not found\033[0m")
95
+ end
96
+ end
97
+
98
+ return url, proxy
99
+ end
100
+
101
+ def choose_url(urls)
102
+ highline = HighLine.new
103
+ url = nil
104
+ length = urls.map(&:size).max
105
+ border = '-' * (length + 8)
106
+ Kernel.puts border
107
+ urls.each.with_index do |url, index|
108
+ Kernel.puts sprintf("| %d | %-#{length}s |", index + 1, url)
109
+ end
110
+ Kernel.puts border
111
+
112
+ choice = highline.ask('select url: ')
113
+
114
+ url = if choice && choice[/^\d+$/]
115
+ urls[choice.to_i - 1]
116
+ elsif urls.include?(choice)
117
+ choice
118
+ end
119
+
120
+ log.error("\033[31mno url selected\033[0m") unless url
121
+
122
+ return url
123
+ end
124
+
125
+ def sort_server_list(list)
126
+ case opts[:sort]
127
+ when :name
128
+ list.sort { |(_, a), (_, b)| a['name'] <=> b['name'] }
129
+ when :ssl
130
+ list.sort &sort_by_uri(:scheme)
131
+ when :port
132
+ list.sort &sort_by_uri(:port)
133
+ else # :host or default
134
+ list.sort &sort_by_uri(:host)
135
+ end
136
+ end
137
+
138
+ def sort_by_uri(part)
139
+ -> a, b { URI.parse(a[1]['urls'].first).send(part) <=> URI.parse(b[1]['urls'].first).send(part) }
140
+ end
141
+
142
+ def filter_server_list(list)
143
+ if opts[:filter_host]
144
+ list = list.select { |(id, server)| server['urls'].any? { |url| URI.parse(url).host =~ opts[:filter_host] } }
145
+ end
146
+ if opts[:filter_name]
147
+ list = list.select { |(id, server)| server['name'] =~ opts[:filter_name] }
148
+ end
149
+ if opts.has_key?(:filter_ssl)
150
+ target_scheme = opts[:filter_ssl] ? 'pryems' : 'pryem'
151
+ list = list.select { |(id, server)| server['urls'].any? { |url| URI.parse(url).scheme == target_scheme } }
152
+ end
153
+ if list.empty?
154
+ log.info("\033[33m[pry-remote-em] no registered servers match the given filter\033[0m")
155
+ Process.exit
156
+ end
157
+ list
158
+ end
159
+
160
+ def second_column_for_server(server)
161
+ data = case opts[:show_details]
162
+ when nil then filtered_urls_list_for_server(server)
163
+ when '@' then server['details']
164
+ else server['details'][opts[:show_details]]
165
+ end
166
+
167
+ prepare_data_for_table(data)
168
+ end
169
+
170
+ def filtered_urls_list_for_server(server)
171
+ opts[:ignore_localhost] ? server['urls'].reject { |url| %w[localhost 127.0.0.1 ::1].include?(URI.parse(url).host) } : server['urls']
172
+ end
173
+
174
+ def third_column_for_server(server)
175
+ data = case opts[:show_metrics]
176
+ when nil then server['metrics']['errors']
177
+ when '@' then server['metrics']
178
+ else server['metrics'][opts[:show_metrics]]
179
+ end
180
+
181
+ prepare_data_for_table(data)
182
+ end
183
+
184
+ def prepare_data_for_table(data)
185
+ case data
186
+ when Array then data.map(&:to_s)
187
+ when Hash then data.map { |key, value| "#{key}: #{value}" }
188
+ else [data.to_s]
189
+ end
190
+ end
191
+ end # module::InteractiveMenu
192
+ end # module::Client
193
+ end # module PryRemoteEm
@@ -0,0 +1,42 @@
1
+ require 'termios' unless RUBY_PLATFORM =~ /java/
2
+
3
+ module PryRemoteEm
4
+ module Client
5
+ module Keyboard
6
+
7
+ def initialize(c)
8
+ @con = c
9
+ # TODO check actual current values to determine if it's enabled or not
10
+ @buff_enabled = true
11
+
12
+ bufferio(false)
13
+
14
+ @old_trap = Signal.trap(:INT) do
15
+ @con.send_shell_sig(:int)
16
+ end
17
+ end
18
+
19
+ def receive_data(d)
20
+ print d.chr
21
+ @con.send_shell_data(d)
22
+ end
23
+
24
+ def unbind
25
+ bufferio(true)
26
+
27
+ Signal.trap(:INT, @old_trap)
28
+ end
29
+
30
+ # Makes stdin buffered or unbuffered.
31
+ # In unbuffered mode read and select will not wait for "\n"; also will not echo characters.
32
+ # This probably does not work on Windows.
33
+ def bufferio(enable)
34
+ return if !defined?(Termios) || enable && @buff_enabled || !enable && !@buff_enabled
35
+ attr = Termios.getattr($stdin)
36
+ enable ? (attr.c_lflag |= Termios::ICANON | Termios::ECHO) : (attr.c_lflag &= ~(Termios::ICANON|Termios::ECHO))
37
+ Termios.setattr($stdin, Termios::TCSANOW, attr)
38
+ @buff_enabled = enable
39
+ end
40
+ end # module::Keyboard
41
+ end # module::Client
42
+ end # module PryRemoteEm
@@ -0,0 +1,33 @@
1
+ require 'pry-remote-em/client/generic'
2
+
3
+ module PryRemoteEm
4
+ module Client
5
+ module Proxy
6
+
7
+ def initialize(client, opts = {})
8
+ @opts = opts
9
+ @client = client
10
+ end
11
+
12
+ def connection_completed
13
+ if get_peername
14
+ port, ip = Socket.unpack_sockaddr_in(get_peername)
15
+ log.info("[pry-remote-em] proxy connected to pryem://#{ip}:#{port}/")
16
+ else
17
+ log.info('[pry-remote-em] proxy connected')
18
+ end
19
+ @client.proxy_incoming_to(self)
20
+ proxy_incoming_to(@client)
21
+ end
22
+
23
+ def log
24
+ return @opts[:logger] if @opts[:logger]
25
+ @log ||= Logger.new(STDERR)
26
+ end
27
+
28
+ def unbind
29
+ @client && @client.close_connection(true)
30
+ end
31
+ end # module::Proxy
32
+ end # module::Client
33
+ end # module::PryRemoteEm
@@ -0,0 +1,39 @@
1
+ module PryRemoteEm
2
+ # Simple metrics system
3
+ # See Sandbox section in Readme for guide
4
+ module Metrics
5
+ def list
6
+ @list ||= Hash.new { |hash, key| hash[key] = 0 }
7
+ end
8
+
9
+ def add(name, value = 1)
10
+ list[name] += value
11
+ end
12
+
13
+ def reduce(name, value = 1)
14
+ add(name, -value)
15
+ end
16
+
17
+ def maximum(name, value)
18
+ list[name] = value if list[name] < value
19
+ end
20
+
21
+ def minimum(name, value)
22
+ list[name] = value if list[name] > value
23
+ end
24
+
25
+ def set(name, value)
26
+ list[name] = value
27
+ end
28
+
29
+ def get(name)
30
+ list[name]
31
+ end
32
+
33
+ def any?
34
+ list.any?
35
+ end
36
+
37
+ extend self
38
+ end
39
+ end
@@ -0,0 +1,148 @@
1
+ # Prefer MessagePack "out of the box" protocol over old JSON+Zlib+CRC
2
+ # variant because of strange `expected "PRYEM" not "}PRYE"` errors
3
+ # on long output over network (not localhost).
4
+ require 'msgpack'
5
+
6
+ module PryRemoteEm
7
+ module Proto
8
+ def receive_data(data)
9
+ @unpacker ||= MessagePack::Unpacker.new
10
+ @unpacker.feed_each(data) { |object| receive_object(object) }
11
+ end
12
+
13
+ def send_object(object)
14
+ send_data(object.to_msgpack)
15
+ end
16
+
17
+ def receive_object(j)
18
+ if !j.is_a?(Hash)
19
+ receive_unknown(j)
20
+ elsif j['p']
21
+ receive_prompt(j['p'])
22
+ elsif j['d']
23
+ receive_raw(j['d'])
24
+ elsif j['m']
25
+ receive_msg(j['m'])
26
+ elsif j['mb']
27
+ receive_msg_bcast(j['mb'])
28
+ elsif j['s']
29
+ receive_shell_cmd(j['s'])
30
+ elsif j.include?('sc')
31
+ receive_shell_result(j['sc'])
32
+ elsif j['g']
33
+ receive_banner(*j['g'].split(' ', 3))
34
+ elsif j['c']
35
+ receive_completion(j['c'])
36
+ elsif j['cb']
37
+ receive_clear_buffer
38
+ elsif j.include?('a')
39
+ receive_auth(*Array(j['a']))
40
+ elsif j['sd']
41
+ receive_shell_data(j['sd'])
42
+ elsif j['ssc']
43
+ receive_shell_sig(j['ssc'].to_sym)
44
+ elsif j['hb']
45
+ receive_heartbeat(j['hb'])
46
+ elsif j['rs']
47
+ receive_register_server(*Array(j['rs']))
48
+ elsif j['urs']
49
+ receive_unregister_server(j['urs'])
50
+ elsif j['sl']
51
+ receive_server_list(j['sl'])
52
+ elsif j['srl']
53
+ receive_server_reload_list
54
+ elsif j['tls']
55
+ receive_start_tls
56
+ elsif j['pc']
57
+ receive_proxy_connection(j['pc'])
58
+ else
59
+ receive_unknown(j)
60
+ end
61
+ end
62
+
63
+
64
+ def receive_prompt(p); end
65
+ def receive_banner(name, version, scheme); end
66
+ def receive_auth(a, b = nil); end
67
+ def receive_msg(m); end
68
+ def receive_msg_bcast(mb); end
69
+ def receive_shell_cmd(c); end
70
+ def receive_shell_result(c); end
71
+ def receive_shell_sig(sym); end
72
+ def receive_shell_data(d); end
73
+ def receive_completion(c); end
74
+ def receive_clear_buffer; end
75
+ def receive_raw(r); end
76
+ def receive_unknown(j); end
77
+
78
+ def receive_start_tls; end
79
+
80
+ def receive_register_server(id, urls, name, details, metrics); end
81
+ def receive_unregister_server(id); end
82
+ def receive_server_list(list); end
83
+ def receive_server_reload_list; end
84
+
85
+ def receive_proxy_connection(url); end
86
+
87
+ def send_banner(g)
88
+ send_object({g: g})
89
+ end
90
+ def send_auth(a)
91
+ send_object({a: a})
92
+ end
93
+ def send_prompt(p)
94
+ send_object({p: p})
95
+ end
96
+ def send_msg_bcast(m)
97
+ send_object({mb: m})
98
+ end
99
+ def send_msg(m)
100
+ send_object({m: m})
101
+ end
102
+ def send_shell_cmd(c)
103
+ send_object({s: c})
104
+ end
105
+ def send_shell_result(r)
106
+ send_object({sc: r})
107
+ end
108
+ def send_shell_sig(sym)
109
+ send_object({ssc: sym})
110
+ end
111
+ def send_shell_data(d)
112
+ send_object({sd: d})
113
+ end
114
+ def send_completion(word)
115
+ send_object({c: word})
116
+ end
117
+ def send_clear_buffer
118
+ send_object({cb: true})
119
+ end
120
+ def send_raw(d)
121
+ send_object(d.is_a?(String) ? {d: d} : d)
122
+ end
123
+
124
+ def send_start_tls
125
+ send_object({tls: true})
126
+ end
127
+
128
+ def send_register_server(id, urls, name, details, metrics)
129
+ send_object({rs: [id, urls, name, details, metrics]})
130
+ end
131
+ def send_unregister_server(id)
132
+ send_object({urs: id})
133
+ end
134
+ def send_heatbeat(url)
135
+ send_object({hb: url})
136
+ end
137
+ def send_server_list(list = nil)
138
+ send_object({sl: list})
139
+ end
140
+ def send_server_reload_list
141
+ send_object({srl: true})
142
+ end
143
+
144
+ def send_proxy_connection(url)
145
+ send_object({pc: url})
146
+ end
147
+ end # module::Proto
148
+ end # module::PryRemoteEm