Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to specify a read timeout for a Net::HTTP::Post.new request in Ruby 2

I have a post happening to a rails application from a ruby script. The script creates a variable request as

request = Net::HTTP::Post.new(url.path)

which is then used as follows

request.content_type = "application/json"    
request.body =  JSON.generate( params )

response = Net::HTTP.start(url.host, url.port) {|http| http.request(request)}

There is quite a lot of processing happening on the server side, and I'm getting a Net::ReadTimeout error

I tried to specify a timeout period

request.read_timeout = 500

as per this stackoverflow answer but I got a

undefined method `read_timeout=' for #<Net::HTTP::Post POST> (NoMethodError)

error. I assume that I'm missing something simple somewhere. All clues gratefully received

Technical info:

  • Ruby 2.0.0p247
  • Rails 4.0.0
  • Windows 7 32 bit ruby
like image 298
Jane Avatar asked Oct 23 '13 16:10

Jane


People also ask

What is the default HTTP request receive timeout?

The default value is 100,000 milliseconds (100 seconds).

How does request timeout work?

A server connection timeout means that a server is taking too long to reply to a data request made from another device. Timeouts are not a reply message: they show up when there isn't a reply and a server request is not fulfilled in a predetermined length of time.

What is Net :: HTTP?

Net::HTTP provides a rich library which can be used to build HTTP user-agents. For more details about HTTP see [RFC2616](www.ietf.org/rfc/rfc2616.txt) Net::HTTP is designed to work closely with URI. URI::Generic#host, URI::Generic#port and URI::HTTP#request_uri are designed to work with Net::HTTP.


4 Answers

Solved via this stackoverflow answer

I've changed my

response = Net::HTTP.start(url.host, url.port) {|http| http.request(request)}

line to be

response = Net::HTTP.start(url.host, url.port, :read_timeout => 500) {|http| http.request(request)}

and this seems to have got around this problem.

like image 128
Jane Avatar answered Nov 02 '22 07:11

Jane


The read_timeout is available with a plain Net::HTTP object:

url = URI.parse('http://google.com')

http = Net::HTTP.new(url.host, url.port)
http.read_timeout = 5 # seconds

http.request_post(url.path, JSON.generate(params)) do |response|
  # do something with response
  p response
end
like image 37
Patrick Oscity Avatar answered Nov 02 '22 07:11

Patrick Oscity


One thing to keep in mind is that if read_timeout is set to a small value such that a timeout does occur...Net::HTTP will "helpfully" retry the request. For a slow HTTP server, a timeout error may not be raised to the code calling Net::HTTP until 2x the read_timeout value.

This certainly was not the behavior I expected.

More info on this topic and how possible solutions differ for Ruby < 2.5 and >= 2.5 may be found here:

https://stackoverflow.com/a/59186209/5299483

like image 1
Lemon Cat Avatar answered Nov 02 '22 07:11

Lemon Cat


I catch both OpenTimeout and ReadTimeout and it's work. test in Ruby:2.6.5

def ping(host, port)
    begin
        url = URI.parse("http://#{host}:#{port}/ping")
        req = Net::HTTP::Get.new(url.to_s)

        # setting both OpenTimeout and ReadTimeout
        res = Net::HTTP.start(url.host, url.port, :open_timeout => 3, :read_timeout => 3) {|http|
          http.request(req)
        }

        if JSON.parse(res.body)["ok"]
          # return true
          STDERR.puts "#{host}:#{port} is reachable"
        else
          STDERR.puts "#{host}:#{port} is NOT reachable"
        end
    rescue Net::ReadTimeout => exception
        STDERR.puts "#{host}:#{port} is NOT reachable (ReadTimeout)"
    rescue Net::OpenTimeout => exception
        STDERR.puts "#{host}:#{port} is NOT reachable (OpenTimeout)"
    end

end

ping("#{ENV['FIRST_HOST']}", 2345)
ping("#{ENV['SECOND_HOST']}", 2345)
like image 1
Webb Lu Avatar answered Nov 02 '22 05:11

Webb Lu