pry-remote-em 0.6.3 → 0.7.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.
@@ -0,0 +1,36 @@
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
+ log.info("[pry-remote-em broker-client] broker connection unbound starting a new one")
26
+ # Give the existing broker a little time to release the port. Even if the
27
+ # restart here fails the next time a server tries to register, a new client
28
+ # will be created; when that fails Broker#restart will be called again.
29
+ EM::Timer.new(rand(0.9)) do
30
+ PryRemoteEm::Broker.restart
31
+ end
32
+ end
33
+
34
+ end # module::Broker
35
+ end # module::Client
36
+ end # module::PryRemoteEm
@@ -0,0 +1,73 @@
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(opt = {})
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
+ @nego_timer = EM::Timer.new(PryRemoteEm::NEGOTIMER) do
43
+ fail("[pry-remote-em] server didn't finish negotiation within #{PryRemoteEm::NEGOTIMER} seconds; terminating")
44
+ end
45
+ end
46
+
47
+ def receive_banner(name, version, scheme)
48
+ log.info("[pry-remote-em] remote is #{name} #{version} #{scheme}")
49
+ client_ver = Gem::Version.new(PryRemoteEm::VERSION)
50
+ server_req = Gem::Requirement.new("~>#{version}")
51
+ server_ver = Gem::Version.new(version)
52
+ client_req = Gem::Requirement.new("~>#{PryRemoteEm::VERSION}")
53
+ unless server_req.satisfied_by?(client_ver) || client_req.satisfied_by?(server_ver)
54
+ fail("[pry-remote-em] incompatible version #{PryRemoteEm::VERSION}")
55
+ return false
56
+ end
57
+ if scheme.nil? || scheme != (reqscheme = opts[:tls] ? 'pryems' : 'pryem')
58
+ if scheme == 'pryems' && defined?(::OpenSSL)
59
+ opts[:tls] = true
60
+ else
61
+ fail("[pry-remote-em] server doesn't support required scheme #{reqscheme.dump}")
62
+ return false
63
+ end
64
+ end
65
+ @negotiated = true
66
+ @nego_timer.cancel
67
+ true
68
+ end
69
+
70
+
71
+ end # module::Generic
72
+ end # module::Client
73
+ end # module::PryRemoteEm
@@ -0,0 +1,32 @@
1
+ require "pry-remote-em/client/generic"
2
+ module PryRemoteEm
3
+ module Client
4
+ module Proxy
5
+
6
+ def initialize(client, opts = {})
7
+ @opts = opts
8
+ @client = client
9
+ end
10
+
11
+ def connection_completed
12
+ if get_peername
13
+ port, ip = Socket.unpack_sockaddr_in(get_peername)
14
+ log.info("[pry-remote-em] proxy connected to pryem://#{ip}:#{port}/")
15
+ else
16
+ log.info("[pry-remote-em] proxy connected")
17
+ end
18
+ @client.proxy_incoming_to(self)
19
+ proxy_incoming_to(@client)
20
+ end
21
+
22
+ def log
23
+ return @opts[:logger] if @opts[:logger]
24
+ @log ||= Logger.new(STDERR)
25
+ end
26
+
27
+ def unbind
28
+ @client && @client.close_connection(true)
29
+ end
30
+ end # module::Proxy
31
+ end # module::Client
32
+ end # module::PryRemoteEm
@@ -0,0 +1,167 @@
1
+ require 'json'
2
+ require "zlib"
3
+
4
+ module PryRemoteEm
5
+ module Proto
6
+ PREAMBLE = 'PRYEM'
7
+ SEPERATOR = ' '
8
+ PREAMBLE_LEN = PREAMBLE.length
9
+ SEPERATOR_LEN = SEPERATOR.length
10
+
11
+ def send_json(d)
12
+ send_data(JSON.dump(d.is_a?(String) ? {:d => d} : d))
13
+ end
14
+
15
+ def send_data(data)
16
+ crc = Zlib::crc32(data).to_s
17
+ msg = PREAMBLE + (data.length + crc.length + SEPERATOR_LEN).to_s + SEPERATOR + crc + SEPERATOR + data
18
+ super(msg)
19
+ end
20
+
21
+ # Each frame is a string consisting of 4 parts
22
+ # 1. preamble (PRYEM)
23
+ # 2. length in characters of crc, a seperator, and body
24
+ # 3. CRC
25
+ # 4. JSON encoded body
26
+ # It is possible and likely that receive_data will be given more than one frame at a time, or
27
+ # an incomplete frame.
28
+ # @example "PRYEM42 3900082256 {\"g\":\"PryRemoteEm 0.7.0 pryem\"}PRYEM22 1794245389 {\"a\":false}"
29
+ def receive_data(d)
30
+ return unless d && d.length > 0
31
+ @buffer ||= "" # inlieu of a post_init
32
+ @buffer << d
33
+ while @buffer && !@buffer.empty?
34
+ return unless @buffer.length >= PREAMBLE_LEN &&
35
+ (len_ends = @buffer.index(SEPERATOR)) &&
36
+ (crc_ends = @buffer.index(SEPERATOR, len_ends))
37
+ if (preamble = @buffer[0...PREAMBLE_LEN]) != PREAMBLE
38
+ raise "message is not in proper format; expected #{PREAMBLE.inspect} not #{preamble.inspect}"
39
+ end
40
+ length = @buffer[PREAMBLE_LEN ... len_ends].to_i
41
+ return if len_ends + length > @buffer.length
42
+ crc_start = len_ends + SEPERATOR_LEN
43
+ crc, data = @buffer[crc_start ... crc_start + length].split(SEPERATOR, 2)
44
+ crc = crc.to_i
45
+ @buffer = @buffer[crc_start + length .. -1]
46
+ if (dcrc = Zlib::crc32(data)) == crc
47
+ receive_json(JSON.load(data))
48
+ else
49
+ warn("data crc #{dcrc} doesn't match crc #{crc.inspect}; discarding #{data.inspect}")
50
+ end
51
+ end
52
+ @buffer
53
+ end
54
+
55
+ def receive_json(j)
56
+ if j['p']
57
+ receive_prompt(j['p'])
58
+ elsif j['d']
59
+ receive_raw(j['d'])
60
+ elsif j['m']
61
+ receive_msg(j['m'])
62
+ elsif j['mb']
63
+ receive_msg_bcast(j['mb'])
64
+ elsif j['s']
65
+ receive_shell_cmd(j['s'])
66
+ elsif j.include?('sc')
67
+ receive_shell_result(j['sc'])
68
+ elsif j['g']
69
+ receive_banner(*j['g'].split(" ", 3))
70
+ elsif j['c']
71
+ receive_completion(j['c'])
72
+ elsif j.include?('a')
73
+ receive_auth(*Array(j['a']))
74
+ elsif j['sd']
75
+ receive_shell_data(j['sd'])
76
+ elsif j['ssc']
77
+ receive_shell_sig(:term)
78
+ elsif j['hb']
79
+ receive_heartbeat(j['hb'])
80
+ elsif j['rs']
81
+ receive_register_server(*Array(j['rs']))
82
+ elsif j['urs']
83
+ receive_unregister_server(j['urs'])
84
+ elsif j.include?('sl')
85
+ j['sl'] ? receive_server_list(j['sl']) : receive_server_list
86
+ elsif j['tls']
87
+ receive_start_tls
88
+ elsif j['pc']
89
+ receive_proxy_connection(j['pc'])
90
+ else
91
+ receive_unknown(j)
92
+ end
93
+ j
94
+ end
95
+
96
+
97
+ def receive_prompt(p); end
98
+ def receive_banner(name, version, scheme); end
99
+ def receive_auth(a, b = nil); end
100
+ def receive_msg(m); end
101
+ def receive_msg_bcast(mb); end
102
+ def receive_shell_cmd(c); end
103
+ def receive_shell_result(c); end
104
+ def receive_completion(c); end
105
+ def receive_raw(r); end
106
+ def receive_shell_sig(sym); end
107
+ def receive_shell_data(d); end
108
+ def receive_unknown(j); end
109
+
110
+ def receive_start_tls; end
111
+
112
+ def receive_register_server(url, name); end
113
+ def receive_unregister_server(url); end
114
+ def receive_server_list(list = nil); end
115
+
116
+ def receive_proxy_connection(url); end
117
+
118
+ def send_banner(g)
119
+ send_json({:g => g})
120
+ end
121
+ def send_auth(a)
122
+ send_json({:a => a})
123
+ end
124
+ def send_prompt(p)
125
+ send_json({:p => p})
126
+ end
127
+ def send_msg_bcast(m)
128
+ send_json({:mb => m})
129
+ end
130
+ def send_msg(m)
131
+ send_json({:m => m})
132
+ end
133
+ def send_shell_cmd(c)
134
+ send_json({:s => c})
135
+ end
136
+ def send_shell_result(r)
137
+ send_json({:sc => r})
138
+ end
139
+ def send_completion(word)
140
+ send_json({:c => word})
141
+ end
142
+ def send_raw(l)
143
+ send_json(l)
144
+ end
145
+
146
+ def send_start_tls
147
+ send_json({:tls => true})
148
+ end
149
+
150
+ def send_register_server(url, name)
151
+ send_json({:rs => [url, name]})
152
+ end
153
+ def send_unregister_server(url)
154
+ send_json({:urs => url})
155
+ end
156
+ def send_heatbeat(url)
157
+ send_json({:hb => url})
158
+ end
159
+ def send_server_list(list = nil)
160
+ send_json({:sl => list})
161
+ end
162
+
163
+ def send_proxy_connection(url)
164
+ send_json({:pc => url})
165
+ end
166
+ end # module::Proto
167
+ end # module::PryRemoteEm
@@ -1,7 +1,9 @@
1
1
  require 'pry'
2
2
  require 'logger'
3
3
  require 'pry-remote-em'
4
+ require 'pry-remote-em/broker'
4
5
  require 'pry-remote-em/server/shell_cmd'
6
+
5
7
  # How it works with Pry
6
8
  #
7
9
  # When PryRemoteEm.run is called it registers with EventMachine for a given ip
@@ -26,14 +28,41 @@ require 'pry-remote-em/server/shell_cmd'
26
28
  # Pry just interacts with PryRemoteEm as if it were any other blocking Readline
27
29
  # object. The important bit is making sure that it is started in a new Fiber that
28
30
  # can be paused and resumed as needed. PryRemoteEm#readline pauses it and
29
- # PryRemoteEm#receive_json resumes it.
31
+ # PryRemoteEm#receive_raw resumes it.
30
32
  #
31
33
  # Reference:
32
34
  # http://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/
33
-
34
35
  module PryRemoteEm
36
+ class << self
37
+ # Local PryRemoteEm EM signatures and server name indexed by url. Each
38
+ # signature can be used with high level EM methods like EM.stop_server or
39
+ # EM.get_sockname. If a server has been stopped EM.get_sockname will return
40
+ # nil for that server's signature.
41
+ def servers
42
+ @servers ||= {}
43
+ end
44
+
45
+ # Safely stop one or more PryRemoteEm servers and remove them from the list
46
+ # of servers.
47
+ # @param [String] id url or name
48
+ def stop_server(id)
49
+ if servers[id]
50
+ EM.stop_server(servers[id][0]) if EM.get_sockname(servers[id][0])
51
+ Broker.unregister(id)
52
+ servers.delete(id)
53
+ return Array(id)
54
+ end
55
+ servers.select{ |i, (sig, name)| name == id }.map do |i, (sig, name)|
56
+ EM.stop_server(sig) if EM.get_sockname(sig)
57
+ Broker.unregister(i)
58
+ servers.delete(i)
59
+ i
60
+ end
61
+ end # stop_server(id)
62
+ end
63
+
35
64
  module Server
36
- include JsonProto
65
+ include Proto
37
66
 
38
67
  class << self
39
68
  # Start a pry-remote-em server
@@ -46,11 +75,12 @@ module PryRemoteEm
46
75
  # @option opts [Proc, Object] :auth require user authentication - see README
47
76
  # @option opts [Boolean] :allow_shell_cmds
48
77
  def run(obj, host = DEFHOST, port = DEFPORT, opts = {:tls => false})
49
- tries = :auto == port ? 100.tap{ port = DEFPORT } : 1
78
+ tries = [port, opts[:port_fail]].include?(:auto) ? 100 : 1
79
+ port = DEFPORT if :auto == port
50
80
  # TODO raise a useful exception not RuntimeError
51
81
  raise "root permission required for port below 1024 (#{port})" if port < 1024 && Process.euid != 0
52
82
  begin
53
- EM.start_server(host, port, PryRemoteEm::Server, obj, opts) do |pre|
83
+ server = EM.start_server(host, port, PryRemoteEm::Server, obj, opts) do |pre|
54
84
  Fiber.new {
55
85
  begin
56
86
  yield pre if block_given?
@@ -62,16 +92,29 @@ module PryRemoteEm
62
92
  end
63
93
  rescue => e
64
94
  # EM 1.0.0.beta4's message tells us the port is in use; 0.12.10 just says, 'no acceptor'
65
- if (e.message.include?('port is in use') || e.message.include?('no acceptor')) && tries >= 1
95
+ if (e.message.include?('port is in use') || e.message.include?('no acceptor')) && tries > 1
66
96
  tries -= 1
67
97
  port += 1
68
98
  retry
69
99
  end
70
- raise e
100
+ raise "can't bind to #{host}:#{port} - #{e}"
101
+ end
102
+ url = "#{opts[:tls] ? 'pryems' : 'pryem'}://#{host}:#{port}/"
103
+ begin
104
+ name = obj.send(:eval, 'self')
105
+ rescue
106
+ name = "#{obj}"
107
+ end
108
+ name = Pry.view_clip(name)
109
+ PryRemoteEm.servers[url] = [server, name]
110
+ (opts[:logger] || ::Logger.new(STDERR)).info("[pry-remote-em] listening for connections on #{url}")
111
+ Broker.run(opts[:broker_host] || ENV['PRYEMBROKER'] || DEF_BROKERHOST, opts[:broker_port] || ENV['PRYEMBROKERPORT'] || DEF_BROKERPORT, opts)
112
+ EM::Timer.new(rand(0.9)) { Broker.register(url, name) }
113
+ rereg = EM::PeriodicTimer.new(15) do
114
+ EM.get_sockname(server) ? Broker.register(url, name) : nil #rereg.cancel
71
115
  end
72
- scheme = opts[:tls] ? 'pryems' : 'pryem'
73
- (opts[:logger] || ::Logger.new(STDERR)).info("[pry-remote-em] listening for connections on #{scheme}://#{host}:#{port}/")
74
- end # run(obj, host = DEFHOST, port = DEFPORT)
116
+ url
117
+ end
75
118
 
76
119
  # The list of pry-remote-em connections for a given object, or the list of all pry-remote-em
77
120
  # connections for this process.
@@ -127,14 +170,21 @@ module PryRemoteEm
127
170
  end
128
171
  end
129
172
 
173
+ def url
174
+ port, host = Socket.unpack_sockaddr_in(get_sockname)
175
+ "#{@opts[:tls] ? 'pryems' : 'pryem'}://#{host}:#{port}/"
176
+ end
177
+
130
178
  def post_init
131
179
  @lines = []
132
180
  Pry.config.pager, @old_pager = false, Pry.config.pager
133
181
  @auth_required = @auth
134
182
  port, ip = Socket.unpack_sockaddr_in(get_peername)
135
183
  @log.info("[pry-remote-em] received client connection from #{ip}:#{port}")
136
- send_data({:g => "PryRemoteEm #{VERSION} #{@opts[:tls] ? 'pryems' : 'pryem'}"})
137
- @opts[:tls] ? start_tls : (@auth_required && send_data({:a => false}))
184
+ # TODO include first level prompt in banner
185
+ send_banner("PryRemoteEm #{VERSION} #{@opts[:tls] ? 'pryems' : 'pryem'}")
186
+ @log.info("#{url} PryRemoteEm #{VERSION} #{@opts[:tls] ? 'pryems' : 'pryem'}")
187
+ @opts[:tls] ? start_tls : (@auth_required && send_auth(false))
138
188
  PryRemoteEm::Server.register(@obj, self)
139
189
  end
140
190
 
@@ -145,7 +195,7 @@ module PryRemoteEm
145
195
 
146
196
  def ssl_handshake_completed
147
197
  @log.info("[pry-remote-em] TLS connection established (#{peer_ip}:#{peer_port})")
148
- send_data({:a => false}) if @auth_required
198
+ send_auth(false) if @auth_required
149
199
  end
150
200
 
151
201
  def peer_ip
@@ -162,74 +212,94 @@ module PryRemoteEm
162
212
  @peer_port
163
213
  end
164
214
 
165
- def receive_json(j)
166
- return send_data({:a => false}) if @auth_required && !j['a']
215
+ def receive_raw(d)
216
+ return if require_auth
217
+ return send_last_prompt if d.nil? || d.empty?
218
+ @lines.push(*d.split("\n"))
219
+ if @waiting
220
+ f, @waiting = @waiting, nil
221
+ f.resume(@lines.shift)
222
+ end
223
+ end
224
+
225
+ # tab completion request
226
+ def receive_completion(c)
227
+ return if require_auth
228
+ send_completion(@compl_proc.call(c))
229
+ end
167
230
 
168
- if j['d'] # just normal data
169
- return send_last_prompt if j['d'].empty?
170
- @lines.push(*j['d'].split("\n"))
171
- if @waiting
172
- f, @waiting = @waiting, nil
173
- f.resume(@lines.shift)
174
- end
175
- elsif j['c'] # tab completion request
176
- send_data({:c => @compl_proc.call(j['c'])})
177
-
178
- elsif j['a'] # authentication response
179
- return send_data({:a => true}) if !@auth || !@auth_required
180
- return send_data({:a => 'auth data must be a two element array'}) unless j['a'].is_a?(Array) && j['a'].length == 2
181
- auth_attempt(j['a'][0], peer_ip)
182
- unless (@auth_required = !@auth.call(*j['a']))
183
- @user = j['a'][0]
184
- auth_ok(j['a'][0], peer_ip)
185
- authenticated!
186
- else
187
- auth_fail(j['a'][0], peer_ip)
188
- if @auth_tries <= 0
189
- msg = "max authentication attempts reached"
190
- send_data({:a => msg})
191
- @log.debug("[pry-remote-em] #{msg} (#{peer_ip}:#{peer_port})")
192
- return close_connection_after_writing
193
- end
194
- @auth_tries -= 1
231
+ def receive_auth(user, pass)
232
+ return send_auth(true) if !@auth || !@auth_required
233
+ return send_auth('auth data must include a user and pass') if user.nil? || pass.nil?
234
+ auth_attempt(user, peer_ip)
235
+ unless (@auth_required = !@auth.call(user, pass))
236
+ @user = user
237
+ auth_ok(user, peer_ip)
238
+ authenticated!
239
+ else
240
+ auth_fail(user, peer_ip)
241
+ if @auth_tries <= 0
242
+ msg = "max authentication attempts reached"
243
+ send_auth(msg)
244
+ @log.debug("[pry-remote-em] #{msg} (#{peer_ip}:#{peer_port})")
245
+ return close_connection_after_writing
195
246
  end
196
- return send_data({:a => !@auth_required})
247
+ @auth_tries -= 1
248
+ end
249
+ return send_auth(!@auth_required)
250
+ end
197
251
 
198
- elsif j['m'] # message all peer connections
199
- peers.each { |peer| peer.send_message(j['m'], @user) }
200
- send_last_prompt
252
+ def receive_msg(m)
253
+ return if require_auth
254
+ peers.each { |peer| peer.send_message(m, @user) }
255
+ send_last_prompt
256
+ end
201
257
 
202
- elsif j['b'] # broadcast message
203
- peers(:all).each { |peer| peer.send_bmessage(j['b'], @user) }
204
- send_last_prompt
258
+ def receive_msg_bcast(mb)
259
+ return if require_auth
260
+ peers(:all).each { |peer| peer.send_bmessage(mb, @user) }
261
+ send_last_prompt
262
+ end
205
263
 
206
- elsif j['s'] # shell command
207
- # TODO confirm shell command's are allowed
208
- unless @allow_shell_cmds
209
- puts "\033[1mshell commands are not allowed by this server\033[0m"
210
- @log.error("refused to execute shell command '#{j['s']}' for #{@user} (#{peer_ip}:#{peer_port})")
211
- send_data({:sc => -1})
212
- send_last_prompt
213
- else
214
- @log.warn("executing shell command '#{j['s']}' for #{@user} (#{peer_ip}:#{peer_port})")
215
- @shell_cmd = EM.popen3(j['s'], ShellCmd, self)
216
- end
264
+ def receive_shell_cmd(cmd)
265
+ return if require_auth
266
+ unless @allow_shell_cmds
267
+ puts "\033[1mshell commands are not allowed by this server\033[0m"
268
+ @log.error("refused to execute shell command '#{cmd}' for #{@user} (#{peer_ip}:#{peer_port})")
269
+ send_shell_result(-1)
270
+ send_last_prompt
271
+ else
272
+ @log.warn("executing shell command '#{cmd}' for #{@user} (#{peer_ip}:#{peer_port})")
273
+ @shell_cmd = EM.popen3(cmd, ShellCmd, self)
274
+ end
275
+ end
217
276
 
218
- elsif j['sd'] # shell data
219
- @shell_cmd.send_data(j['sd'])
277
+ def receive_shell_data(d)
278
+ return if require_auth
279
+ @shell_cmd.send_data(d)
280
+ end
220
281
 
221
- elsif j['ssc'] # shell ctrl-c
222
- @shell_cmd.close_connection
282
+ def receive_shell_sig(type)
283
+ return if require_auth
284
+ type == :term && @shell_cmd.close_connection
285
+ end
223
286
 
224
- else
225
- warn "received unexpected data: #{j.inspect}"
226
- end # j['d']
227
- end # receive_json(j)
287
+ def receive_unknown(j)
288
+ return if require_auth
289
+ warn "received unexpected data: #{j.inspect}"
290
+ send_error("received unexpected data: #{j.inspect}")
291
+ send_last_prompt
292
+ end
228
293
 
294
+ def require_auth
295
+ return false if !@auth_required
296
+ send_auth(false)
297
+ true
298
+ end
229
299
 
230
300
  def authenticated!
231
301
  while (aa = @after_auth.shift)
232
- send_data(aa)
302
+ aa.call
233
303
  end
234
304
  end
235
305
 
@@ -246,19 +316,24 @@ module PryRemoteEm
246
316
  end
247
317
 
248
318
  def send_last_prompt
249
- @auth_required ? @after_auth.push({:p => @last_prompt}) : send_data({:p => @last_prompt})
319
+ @auth_required ? (after_auth { send_prompt(@last_prompt) }) : send_prompt(@last_prompt)
320
+ end
321
+
322
+ def after_auth(&blk)
323
+ # TODO perhaps replace with #auth_ok
324
+ @after_auth.push(blk)
250
325
  end
251
326
 
252
327
  # Sends a chat message to the client.
253
328
  def send_message(msg, from = nil)
254
329
  msg = "#{msg} (@#{from})" unless from.nil?
255
- @auth_required ? @after_auth.push({:m => msg}) : send_data({:m => msg})
330
+ @auth_required ? (after_auth {send_msg(msg)}) : send_msg(msg)
256
331
  end
257
332
  #
258
333
  # Sends a chat message to the client.
259
334
  def send_bmessage(msg, from = nil)
260
335
  msg = "#{msg} (@#{from})" unless from.nil?
261
- @auth_required ? @after_auth.push({:mb => msg}) : send_data({:mb => msg})
336
+ @auth_required ? (after_auth {send_msg_bcast(msg)}) : send_msg_bcast(msg)
262
337
  end
263
338
 
264
339
  # Callbacks for events on the server
@@ -299,19 +374,22 @@ module PryRemoteEm
299
374
  end
300
375
  end # auth_fail(*args, &blk)
301
376
 
377
+ def send_error(msg)
378
+ puts "\033[31m#{msg}\033[0m"
379
+ end
302
380
 
303
381
  # Methods that make Server compatible with Pry
304
382
 
305
383
  def readline(prompt)
306
384
  @last_prompt = prompt
307
- @auth_required ? @after_auth.push({:p => prompt}) : send_data({:p => prompt})
385
+ @auth_required ? (after_auth { send_prompt(prompt) }) : send_prompt(prompt)
308
386
  return @lines.shift unless @lines.empty?
309
387
  @waiting = Fiber.current
310
388
  return Fiber.yield
311
389
  end
312
390
 
313
391
  def print(val)
314
- @auth_required ? @after_auth.push({:d => val}) : send_data({:d => val})
392
+ @auth_required ? (after_auth { send_raw(val) }) : send_raw(val)
315
393
  end
316
394
  alias :write :print
317
395