Socket error when writing to socket in Ruby - ruby

I have the following code:
# Connect to neighbors and send hello message
current_node.neighbors_addresses.each do |address|
for i in 0..10
begin
sock = TCPSocket.new(address, 3000)
break
rescue Errno::EHOSTUNREACH, Errno::ECONNREFUSED
puts "before sleep"
sleep(2)
puts "after sleep"
end
end
sock.write("Hi from node #{current_node.name}\n")
end
The current_node is a node object with neighbors_addresses as an instance variable that stores the ip addresses of the neighbors of the current node.
The problem is, the line sock.write("Hi from node #{current_node.name}\n")gives the error:
server1.rb:211:in `block in main': undefined method `write' for nil:NilClass (NoMethodError)
from server1.rb:200:in `each'
from server1.rb:200:in `main'
from server1.rb:276:in `<main>'
If write is not a method in the socket class then how do you write to that socket?

Put sock.write just after sock = TCPSocket... ?
– SeanNieuwoudt
if the code of rescue gets executed 10 times, sock will be nil after the for-loop ends. You need to make sure sock is a Socket object before calling any method or accessing any attribute of the object itself. SeanNieuwoudt is right.
– yeyo

Related

catch undefined method exception from a thread and restart it in ruby

I have an activemq topic subscriber in ruby which uses stomp protocol with failover to connect to the broker and if somehow the activemq gets restarted then sometimes i get an exception :
undefined method `command' for nil:NilClass
/usr/lib64/ruby/gems/1.8/gems/stomp-1.1.8/lib/stomp/client.rb:295:in `start_listeners'
/usr/lib64/ruby/gems/1.8/gems/stomp-1.1.8/lib/stomp/client.rb:108:in `join'
/usr/lib64/ruby/gems/1.8/gems/stomp-1.1.8/lib/stomp/client.rb:108:in `join'
./lib/active_mq_topic_reader.rb:31:in `active_mq_topic_reader'
main.rb:164
main.rb:163:in `initialize'
main.rb:163:in `new'
but i get this exception only when i use join() method on the broker thread, otherwise no exception appears and the subscriber get unsubscribed from the topic.
The problem which i am facing is i have a different mechanism of shutting down the process by sending shutdown signal, and till then the process waits, but if we use join() then the process will get stuck on this line and i will not be able to close the method by shutdown signal.So what should i do to catch the exception and restart the listener thread?
active_mq_topic_reader.rb :
require 'rubygems'
require 'ffi-rzmq'
require 'msgpack'
require 'zmq_helper'
require 'stomp'
include ZmqHelper
def active_mq_topic_reader(context, notice_agg_fe_url, signal_pub_url, monitor_url, active_mq_broker, topic)
begin
sender = create_connect_socket(context, ZMQ::PUSH, notice_agg_fe_url)
monitor = create_connect_socket(context, ZMQ::PUSH, monitor_url)
active_mq_broker.subscribe(topic, {}) do |msg|
notice = {}
["entity_id","entity_type","system_name","change_type"].each do |key|
notice[key] = msg.headers[key].to_s
end
monitor.send_string("qreader-#{topic.slice(7..-1)}")
sender.send_string(notice.to_msgpack)
end
active_mq_broker.join() #cannot use this
signal_subscriber.recv_string() #here the code waits for shutdown signal in case of process shutdown
sender.close()
monitor.close()
signal_subscriber.close()
active_mq_broker.unsubscribe(topic)
return
rescue Exception => e
puts "#{topic}: #{e}"
puts e.backtrace
$stdout.flush
end
end
main.rb :
context = ZMQ::Context.new(1)
active_mq_broker_audit = Stomp::Client.new("failover:(stomp://localhost:61613,stomp://localhost:61613)")
new_thread = Thread.new do
active_mq_topic_reader(context,
"inproc://notice_agg_fe",
"inproc://signal_pub",
"tcp://localhost:xxxx",
active_mq_broker_audit,
"/topic/myTopic")
end
new_thread.join()

How could I use Redis calls in trap context in Ruby?

My script gets elements from redis database end doing some work with it. I need to be sure that if script finishes with ^C or other Signal, the element will be returned back in the database.
I'm trying to do it
require "redis"
class Test
attr_reader :quit
def initialize
#redis = Redis.new
end
def trap_signal
trap("INT") {
puts "get ready to exit"
#redis.rpush "TestQueue", #elem # I need to be sure that #emelent always puts back in the database
#quit = true}
end
def run!
trap_signal
#elem = "test string"
#redis.rpush "TestQueue", #elem
while !quit
#redis.blpop "TestQueue", #elem
# Do some work whith #element
sleep 1
# And put it back in the database
#redis.rpush "TestQueue", #elem
end
end
end
Test.new.run!
but get this error
^Cget ready to exit
/usr/lib/ruby/2.1.0/monitor.rb:185:in `lock': can't be called from trap context (ThreadError)
from /usr/lib/ruby/2.1.0/monitor.rb:185:in `mon_enter'
from /usr/lib/ruby/2.1.0/monitor.rb:209:in `mon_synchronize'
from /home/kusayu/.gem/ruby/2.1.0/gems/redis-3.2.0/lib/redis.rb:37:in `synchronize'
from /home/kusayu/.gem/ruby/2.1.0/gems/redis-3.2.0/lib/redis.rb:991:in `rpush'
from test.rb:13:in `block in trap_signal'
from test.rb:24:in `call'
from test.rb:24:in `sleep'
from test.rb:24:in `run!'
from test.rb:32:in `<main>'
Your code already works properly, just remove the #redis.rpush from the signal handler.
You shouldn't run "heavy" operations in the signal handler (and you get an exception because of it anyway). It's much better to use a variable like #quit = true to signal the main loop that it's time to finish, and then let the main loop handle the proper cleanup.
So if you just remove the #redis.rpush from your INT signal handler, then you will ensure that the element is returned back into the database because the main loop will only finish once #quit is true.

How to search message in mailbox with net/imap in Ruby?

I have some script on Ruby 1.9.3:
require "net/imap"
imap = Net::IMAP.new(mail_imap_server)
imap.login(mail_login, mail_password)
imap.select("INBOX")
puts imap.search(["FROM", "homer#simpson.com"])
imap.logout
imap.disconnect
If the desired message is present, then all is well.
If the desired message is missing, an error:
/opt/local/lib/ruby1.9/1.9.1/net/imap.rb:1332:in `block in search_internal': undefined method `[]' for nil:NilClass (NoMethodError)
from /opt/local/lib/ruby1.9/1.9.1/monitor.rb:211:in `mon_synchronize'
from /opt/local/lib/ruby1.9/1.9.1/net/imap.rb:1326:in `search_internal'
from /opt/local/lib/ruby1.9/1.9.1/net/imap.rb:752:in `search'
from ./mail.rb:12:in `mail'
from ./mail.rb:26:in `<main>'
How can I solve this problem?
first check if there are messages in the result, use a rescue just in case
require "net/imap"
imap = Net::IMAP.new(mail_imap_server)
imap.login(mail_login, mail_password)
imap.select("INBOX")
begin
messages = imap.search(["FROM", "homer#simpson.com"])
puts messages if messages.length > 0
rescue
puts "Error while retrieving the message"
end
imap.logout
imap.disconnect

How to handle timeout with net/http in ruby while sending requests to IPs from given IP range and skip IPs with timeout and move to next ones?

I want to handle timeout for IP range taken from console for which I make requests to IPs within taken range and getting timeout error.
I want to make requests to all IPs and get responses from them.
For IP that time out , want to skip it and move to next one. How to handle this so loop dont get exception and script sends request to all IPs that can give response handling timed out ones.
Attaching code here:
require 'net/http'
require 'uri'
require 'ipaddr'
puts "Origin IP:"
originip = gets()
(IPAddr.new("209.85.175.121")..IPAddr.new("209.85.175.150")).each do |address|
req = Net::HTTP.get(URI.parse("http://#{address.to_s}"))
puts req
end
Error:
C:/Ruby187/lib/ruby/1.8/net/http.rb:560:in `initialize': A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. - connect(2) (Errno::ETIMEDOUT)
from C:/Ruby187/lib/ruby/1.8/net/http.rb:560:in `open'
from C:/Ruby187/lib/ruby/1.8/net/http.rb:560:in `connect'
from C:/Ruby187/lib/ruby/1.8/timeout.rb:53:in `timeout'
from C:/Ruby187/lib/ruby/1.8/timeout.rb:101:in `timeout'
from C:/Ruby187/lib/ruby/1.8/net/http.rb:560:in `connect'
from C:/Ruby187/lib/ruby/1.8/net/http.rb:553:in `do_start'
from C:/Ruby187/lib/ruby/1.8/net/http.rb:542:in `start'
from C:/Ruby187/lib/ruby/1.8/net/http.rb:379:in `get_response'
from C:/Ruby187/lib/ruby/1.8/net/http.rb:356:in `get'
from IP Range 2.rb:9
from IP Range 2.rb:8:in `each'
Just like Marc says. You should rescue the exception. Like so:
begin
response = Net::HTTP.get(...)
rescue Errno::ECONNREFUSED => e
# Do what you think needs to be done
end
Also, what you get back from the call to get() is a response, not a request.
Catch the exception using timeout,
require 'timeout'
(IPAddr.new("209.85.175.121")..IPAddr.new("209.85.175.150")).each do |address|
begin
req = Net::HTTP.get(URI.parse("http://#{address.to_s}"))
puts req
rescue Timeout::Error => exc
puts "ERROR: #{exc.message}"
rescue Errno::ETIMEDOUT => exc
puts "ERROR: #{exc.message}"
# uncomment the following two lines, if you are not able to track the exception type.
#rescue Exception => exc
# puts "ERROR: #{exc.message}"
end
end
Edit: When we rescue Timeout::Error, only those exceptions which belongs to Timeout::Error class will be caught. We need to catch the raised exception using their error class, updated the code accordingly.

stream closed (IOError) when closing Ruby TCPSocket client

I've got a Ruby TCPSocket client that works great except when I'm trying to close it. When I call the disconnect method in my code below, I get this error:
./smartlinc.rb:70:in `start_listen': stream closed (IOError)
from ./smartlinc.rb:132:in `initialize'
from ./smartlinc.rb:132:in `new'
from ./smartlinc.rb:132:in `start_listen'
from bot.rb:45:in `initialize'
from bot.rb:223:in `new'
from bot.rb:223
Here's the (simplified) code:
class Smartlinc
def initialize
#socket = TCPSocket.new(HOST, PORT)
end
def disconnect
#socket.close
end
def start_listen
# Listen on a background thread
th = Thread.new do
Thread.current.abort_on_exception = true
# Listen for Ctrl-C and disconnect socket gracefully.
Kernel.trap('INT') do
self.disconnect
exit
end
while true
ready = IO.select([#socket])
readable = ready[0]
readable.each do |soc|
if soc == #socket
buf = #socket.recv_nonblock(1024)
if buf.length == 0
puts "The socket connection is dead. Exiting."
exit
else
puts "Received Message"
end
end
end # end each
end # end while
end # end thread
end # end message callback
end
Is there a way I can prevent or catch this error? I'm no expert in socket programming (obviously!), so all help is appreciated.
Your thread is sitting in IO.select() while the trap code happily slams the door in its face with #socket.close, hence you get some complaining.
Don't set abort_on_exception to true, or then handle the exception properly in your code:
Something along these lines...
Kernel.trap('INT') do
#interrupted = true
disconnect
exit
end
...
ready = nil
begin
ready = IO.select(...)
rescue IOError
if #interrupted
puts "Interrupted, we're outta here..."
exit
end
# Else it was a genuine IOError caused by something else, so propagate it up..
raise
end
...

Resources