em-simple_telnet 0.0.1 → 0.0.6

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.
Files changed (3) hide show
  1. data/README.rdoc +47 -0
  2. data/lib/em-simple_telnet.rb +83 -82
  3. metadata +22 -5
data/README.rdoc ADDED
@@ -0,0 +1,47 @@
1
+ == EventMachine::Protocols::SimpleTelnet
2
+
3
+ Provides telnet client functionality.
4
+
5
+ This class derived from the Net::Telnet class in Ruby's standard library.
6
+ It was developed with simplicity in mind. It tries to hide the complexity
7
+ that normally comes with asynchronous programming using the Fiber class
8
+ found in Ruby 1.9.
9
+
10
+ == Overview
11
+
12
+ The telnet protocol allows a client to login remotely to a user account on a
13
+ server and execute commands via a shell. The equivalent is done by creating
14
+ a EventMachine::Protocols::SimpleTelnet instance with the <tt>:host</tt>
15
+ option set to your host along with a block which defines the task to be done
16
+ on the host. Inside the task you can issue one or more #cmd calls. After the
17
+ block is executed, #close is automatically called.
18
+
19
+ This class can also be used to connect to non-telnet services, such as SMTP
20
+ or HTTP. In this case, you normally want to provide the <tt>:port</tt>
21
+ option to specify the port to connect to, and set the <tt>:telnet_mode</tt>
22
+ option to +false+ to prevent the client from attempting to interpret telnet
23
+ command sequences. Generally, #login will not work with other protocols,
24
+ and you have to handle authentication yourself.
25
+
26
+ == Examples
27
+
28
+ EventMachine.run do
29
+
30
+ opts = {
31
+ host: "localhost",
32
+ username: "user",
33
+ password: "secret",
34
+ }
35
+
36
+ EM::P::SimpleTelnet.new(opts) do |host|
37
+ # already logged in
38
+ puts host.cmd("ls -la")
39
+ end
40
+ end
41
+
42
+ == References
43
+
44
+ There are a large number of RFCs relevant to the Telnet protocol.
45
+ RFCs 854-861 define the base protocol. For a complete listing
46
+ of relevant RFCs, see
47
+ http://www.omnifarious.org/~hopper/technical/telnet-rfc.html
@@ -15,27 +15,18 @@ require "eventmachine"
15
15
  # still working on a deferred action or not.
16
16
  #
17
17
  module EventMachine # :nodoc:
18
- def self.defers_finished?
19
- (not defined? @threadqueue or (tq=@threadqueue).nil? or tq.empty? ) and
20
- (not defined? @resultqueue or (rq=@resultqueue).nil? or rq.empty? ) and
21
- (not defined? @threadpool or (tp=@threadpool).nil? or tp.none? {|t|t[:working]})
22
- end
18
+ # ensure they're always defined
19
+ @threadpool = @threadqueue = @resultqueue = nil
23
20
 
24
- def self.spawn_threadpool
25
- until @threadpool.size == @threadpool_size.to_i
26
- thread = Thread.new do
27
- Thread.current.abort_on_exception = true
28
- while true
29
- Thread.current[:working] = false
30
- op, cback = *@threadqueue.pop
31
- Thread.current[:working] = true
32
- result = op.call
33
- @resultqueue << [result, cback]
34
- EventMachine.signal_loopbreak
35
- end
36
- end
37
- @threadpool << thread
38
- end
21
+ ##
22
+ # Returns +true+ if all deferred actions are done executing and their
23
+ # callbacks have been fired.
24
+ #
25
+ def self.defers_finished?
26
+ return false if @threadqueue and not @threadqueue.empty?
27
+ return false if @resultqueue and not @resultqueue.empty?
28
+ return false if @threadpool and @threadqueue.num_waiting != @threadpool.size
29
+ return true
39
30
  end
40
31
  end
41
32
 
@@ -261,32 +252,39 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
261
252
  opts
262
253
  ]
263
254
 
264
- # start establishing the connection
265
- connection = EventMachine.connect(*params)
255
+ begin
256
+ # start establishing the connection
257
+ connection = EventMachine.connect(*params)
266
258
 
267
- # set callback to be executed when connection establishing
268
- # fails/succeeds
269
- f = Fiber.current
270
- connection.connection_state_callback = lambda do |obj=nil|
271
- @connection_state_callback = nil
272
- f.resume obj
273
- end
259
+ # set callback to be executed when connection establishing
260
+ # fails/succeeds
261
+ f = Fiber.current
262
+ connection.connection_state_callback = ->(o){ f.resume(o) }
274
263
 
275
- # block here and get result from establishing connection
276
- state = Fiber.yield
264
+ # block here and get result from establishing connection
265
+ state = Fiber.yield
277
266
 
278
- # raise if exception (e.g. Telnet::ConnectionFailed)
279
- raise state if state.is_a? Exception
267
+ # raise if exception (e.g. Telnet::ConnectionFailed)
268
+ raise state if state.is_a? Exception
280
269
 
281
- # login
282
- connection.instance_eval { login }
270
+ # login
271
+ connection.instance_eval { login }
283
272
 
284
- begin
285
- yield connection
273
+ begin
274
+ yield connection
275
+ ensure
276
+ # Use #close so a subclass can execute some kind of logout command
277
+ # before the connection is closed.
278
+ connection.close
279
+ end
286
280
  ensure
287
- # Use #close so a subclass can execute some kind of logout command
288
- # before the connection is closed.
289
- connection.close
281
+ # close the connection in any case
282
+ if connection
283
+ connection.close_connection_after_writing
284
+
285
+ # give some time to send the remaining data, which should be nothing
286
+ EventMachine.add_timer(2) { connection.close_connection }
287
+ end
290
288
  end
291
289
 
292
290
  return connection
@@ -574,7 +572,7 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
574
572
  # we only need to do something if there's a connection state callback
575
573
  return unless @connection_state_callback
576
574
 
577
- # we ensure there's no timer running to check the input buffer
575
+ # we ensure there's no timer running for checking the input buffer
578
576
  if @check_input_buffer_timer
579
577
  @check_input_buffer_timer.cancel
580
578
  @check_input_buffer_timer = nil
@@ -588,9 +586,9 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
588
586
  # We postpone checking the input buffer by one second because the regular
589
587
  # expression matches can get quite slow.
590
588
  #
591
- # So as long as data is received (continuously), the input buffer is not
592
- # checked. It's only checked one second after the whole output has been
593
- # received.
589
+ # So as long as data is being received (continuously), the input buffer
590
+ # is not checked. It's only checked one second after the whole output
591
+ # has been received.
594
592
  @check_input_buffer_timer = EventMachine::Timer.new(1) do
595
593
  @check_input_buffer_timer = nil
596
594
  check_input_buffer
@@ -607,33 +605,32 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
607
605
 
608
606
  ##
609
607
  # Checks the input buffer (<tt>@input_buffer</tt>) for the prompt we're
610
- # waiting for. Calls the proc in <tt>@connection_state_callback</tt> if the
608
+ # waiting for. Calls #call_connection_state_callback with the output if the
611
609
  # prompt has been found. Thus, call this method *only* if
612
610
  # <tt>@connection_state_callback</tt> is set!
613
611
  #
614
- # If <tt>@telnet_options[:wait_time]</tt> is set, this amount of seconds is
615
- # waited (call to <tt>@connection_state_callback</tt> is scheduled) after
616
- # seeing what looks like the prompt before firing the
617
- # <tt>@connection_state_callback</tt> is fired, so more data can come until
618
- # the real prompt is reached. This is useful for commands which will cause
619
- # multiple prompts to be sent.
612
+ # If <tt>@telnet_options[:wait_time]</tt> is set, it will wait this amount
613
+ # of seconds after seeing what looks like the prompt before calling
614
+ # #call_connection_state_callback. This way, more data can be received
615
+ # until the real prompt is received. This is useful for commands that send
616
+ # multiple prompts.
620
617
  #
621
618
  def check_input_buffer
622
- if md = @input_buffer.match(@telnet_options[:prompt])
623
- blk = lambda do
624
- @last_prompt = md.to_s # remember last prompt
625
- output = md.pre_match + @last_prompt
626
- @input_buffer = md.post_match
627
- @connection_state_callback.call(output)
628
- end
619
+ return unless md = @input_buffer.match(@telnet_options[:prompt])
629
620
 
630
- if s = @telnet_options[:wait_time]
631
- # fire @connection_state_callback after s seconds
632
- @wait_time_timer = EventMachine::Timer.new(s, &blk)
633
- else
634
- # fire @connection_state_callback now
635
- blk.call
636
- end
621
+ blk = lambda do
622
+ @last_prompt = md.to_s # remember last prompt
623
+ output = md.pre_match + @last_prompt
624
+ @input_buffer = md.post_match
625
+ call_connection_state_callback(output)
626
+ end
627
+
628
+ if s = @telnet_options[:wait_time] and s > 0
629
+ # fire @connection_state_callback after s seconds
630
+ @wait_time_timer = EventMachine::Timer.new(s, &blk)
631
+ else
632
+ # fire @connection_state_callback now
633
+ blk.call
637
634
  end
638
635
  end
639
636
 
@@ -675,10 +672,7 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
675
672
  f = Fiber.current
676
673
 
677
674
  # will be called by #receive_data to resume at "Fiber.yield" below
678
- @connection_state_callback = lambda do |output|
679
- @connection_state_callback = nil
680
- f.resume(output)
681
- end
675
+ @connection_state_callback = ->(output){ f.resume(output) }
682
676
 
683
677
  result = Fiber.yield
684
678
 
@@ -769,8 +763,7 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
769
763
  log_command(opts[:hide] ? "<hidden command>" : command)
770
764
 
771
765
  # send the command
772
- sendcmd = opts[:raw_command] ? :print : :puts
773
- self.__send__(sendcmd, command)
766
+ opts[:raw_command] ? print(command) : puts(command)
774
767
 
775
768
  # wait for the output
776
769
  waitfor(opts[:prompt], opts)
@@ -855,10 +848,10 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
855
848
  # Decreases <tt>@@_telnet_connection_count</tt> by one and calls #close_logs.
856
849
  #
857
850
  # After that and if <tt>@connection_state_callback</tt> is set, it takes a
858
- # look on <tt>@connection_state</tt>. If it was <tt>:connecting</tt>, calls
859
- # <tt>@connection_state_callback</tt> with a new instance of
860
- # ConnectionFailed. If it was <tt>:waiting_for_prompt</tt>, calls the
861
- # callback with a new instance of TimeoutError.
851
+ # look at <tt>@connection_state</tt>. If it was <tt>:connecting</tt>, calls
852
+ # #call_connection_state_callback with a new instance of ConnectionFailed.
853
+ # If it was <tt>:waiting_for_prompt</tt>, calls the same method with a new
854
+ # instance of TimeoutError.
862
855
  #
863
856
  # Finally, the <tt>@connection_state</tt> is set to +closed+.
864
857
  #
@@ -888,23 +881,31 @@ class EventMachine::Protocols::SimpleTelnet < EventMachine::Connection
888
881
  @connection_state = :closed
889
882
  end
890
883
 
884
+ ##
885
+ # Calls the @connection_state_callback with _obj_ and sets it to +nil+.
886
+ #
887
+ # Call this method *only* if <tt>@connection_state_callback</tt> is set!
888
+ #
889
+ def call_connection_state_callback(obj=nil)
890
+ callback, @connection_state_callback = @connection_state_callback, nil
891
+ callback.call(obj)
892
+ end
893
+
891
894
  ##
892
895
  # Called by EventMachine after the connection is successfully established.
893
896
  #
894
897
  def connection_completed
895
898
  @connection_state = :connected
896
- @connection_state_callback.call if @connection_state_callback
899
+ call_connection_state_callback if @connection_state_callback
897
900
  end
898
901
 
899
902
  ##
900
- # Tells EventMachine to close the connection after sending what's in the
901
- # output buffer. Redefine this method to execute some logout command like
902
- # +exit+ or +logout+ before the connection is closed. Don't forget: The
903
- # command will probably not return a prompt, so use #puts, which doesn't
904
- # wait for a prompt.
903
+ # Redefine this method to execute some logout command like +exit+ or
904
+ # +logout+ before the connection is closed. Don't forget: The command will
905
+ # probably not return a prompt, so use #puts, which doesn't wait for a
906
+ # prompt.
905
907
  #
906
908
  def close
907
- close_connection_after_writing
908
909
  end
909
910
 
910
911
  ##
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-simple_telnet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,16 +10,34 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
  date: 2011-11-21 00:00:00.000000000 Z
13
- dependencies: []
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0.beta.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.0.beta.2
14
30
  description: This library provides a very simple way to connect to telnet servers
15
31
  using EventMachine in a seemingly synchronous manner.
16
32
  email:
17
33
  - paddor@gmail.com
18
34
  executables: []
19
35
  extensions: []
20
- extra_rdoc_files: []
36
+ extra_rdoc_files:
37
+ - README.rdoc
21
38
  files:
22
39
  - lib/em-simple_telnet.rb
40
+ - README.rdoc
23
41
  homepage: http://github.com/paddor/em-simple_telnet
24
42
  licenses: []
25
43
  post_install_message:
@@ -40,9 +58,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
40
58
  version: '0'
41
59
  requirements: []
42
60
  rubyforge_project:
43
- rubygems_version: 1.8.11
61
+ rubygems_version: 1.8.23
44
62
  signing_key:
45
63
  specification_version: 3
46
64
  summary: Simple telnet client on EventMachine
47
65
  test_files: []
48
- has_rdoc: