pry-remote-em 1.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -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