NET::HTTP on rails anyway to increase timeout - ruby-on-rails

I have making a NET:HTTP call to a third party payment API using form_post method from NET:HTTP using rails.
res = Net::HTTP.post_form(uri, payment_details)
logger.info "RESPONSE: #{res}"
descrypted_res = JSON.parse(res.body)
logger.info "RESPONSE: #{descrypted_res}"
sometimes, I got the response back saying HTTPOK, sometimes i dont get anything at all. I am suspecting the payment server did not respond to my request in time and the connection is terminated. My question is, is there any way to increase the post_form timeout duration?
I want to increase it to 60 seconds.
Thanks.

You can adapt the ruby method:
https://docs.ruby-lang.org/en/2.0.0/Net/HTTP.html#method-c-post_form
like this:
def _post_form(url, params)
req = Net::HTTP::Post.new(url)
req.form_data = params
req.basic_auth url.user, url.password if url.user
Net::HTTP.start(
url.hostname,
url.port,
use_ssl: url.scheme == 'https',
read_timeout: 600
) do |http|
http.request(req)
end
end

Related

How do I ensure that I am using the same session for multiple HTTP call?

Let's say that I am calling the following code from inside a loop with a 1-second sleep/delay between each iteration and the URL is an API. How do I make sure that Net::HTTP is using the same API session for all the calls? I know the documentation says Net::HTTP.new will try to reuse the same connection. But how do I verify that? Is there a session ID that I can pull out of Net::HTTP?
request = Net::HTTP::Put.new(url)
url = URI(url)
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request["Accept"] = 'application/json'
request["Content-Type"] = 'application/json'
request["Authorization"] = #auth_key
request["cache-control"] = 'no-cache'
request.body = request_body.to_json if request_body
response = http.request(request)
double check the following against the ruby version you are running on
For one, I don't think there is any session ID from what I can see which would be quite a useful feature. Next, looking at the source code, we see the variable setting in lib/net/http.rb in such methods as:
def do_finish
#started = false
#socket.close if #socket
#socket = nil
end
# Returns true if the HTTP session has been started.
def started?
#started
end
# Finishes the HTTP session and closes the TCP connection.
# Raises IOError if the session has not been started.
def finish
raise IOError, 'HTTP session not yet started' unless started?
do_finish
end
Where do_finish sets the instance variable #socket to nil and #socket is used as a BufferedIO instance to run HTTP requests through
So I would write an override method for the finish method and raise an alert when it calls on do_finish.
Looking through the comments start is the safest bet to use the same session, so you could use a start block and compare the id of the instance variable does not change
Net::HTTP.start(url) do |http|
before = http.instance_variable_get(:#socket)
loop do
instance_var = http.instance_variable_get(:#socket)
break unless before == instance_var
end
end

Ruby: failed to make a successful GET request

I am trying to send a http.get request to different websites. Here is the code I am using:
def makePing
begin
url = URI.parse(#URI)
req = Net::HTTP::Get.new(url.to_s)
res = Net::HTTP.start(url.host, url.port) {|http|
http.read_timeout = #request_timeout_limit
http.request(req)
}
# debugger
rescue Exception => echo
puts "Error is: Failed to open TCP connection to #{#URI}"
end
end
It returns the result of 200 for 'http://www.example.com'
but
for http://www.google.com or http://www.facebook.com
it returns
<Net::HTTPNotFound 404 Not Found readbody=true>
1-I am wondering why it happens like this?
2-How can I get the body of the response?
3- I expect that, the request get expired exactly after #request_timeout_limit, and it stop trying, but it is not working in this way?

Running a HTTP request with rails

It has been a while since I have used Rails. I currently have a curl request as follows
curl -X GET -H 'Authorization: Element TOKEN, User TOKEN' 'https://api.cloud-elements.com/elements/api-v2/hubs/marketing/ping'
All I am looking to do is to be able to run this request from inside of a rails controller, but my lack of understanding when it comes to HTTP requests is preventing me from figuring it out to how best handle this. Thanks in advance.
Use this method for HTTP requests:
def api_request(type , url, body=nil, header =nil )
require "net/http"
uri = URI.parse(url)
case type
when :post
request = Net::HTTP::Post.new(uri)
request.body = body
when :get
request = Net::HTTP::Get.new(uri)
when :put
request = Net::HTTP::Put.new(uri)
request.body = body
when :delete
request = Net::HTTP::Delete.new(uri)
end
request.initialize_http_header(header)
#request.content_type = 'application/json'
response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') {|http| http.request request}
end
Your example will be:
api_request(:get, "https://api.cloud-elements.com/elements/api-v2/hubs/marketing/ping",nil, {"Authorization" => "Element TOKEN, User TOKEN" })
It would be something like the following. Note that the connection will be blocking, so it can tie up your server depending on how quickly the remote host returns the HTTP response and how many of these requests you are making.
require 'net/http'
# Let Ruby form a canonical URI from our URL
ping_uri = URI('https://api.cloud-elements.com/elements/api-v2/hubs/marketing/ping')
# Pass the basic configuration to Net::HTTP
# Note, this is not asynchronous. Ruby will wait until the HTTP connection
# has closed before moving forward
Net::HTTP.start(ping_uri.host, ping_uri.port, :use_ssl => true) do |http|
# Build the request using the URI as a Net::HTTP::Get object
request = Net::HTTP::Get.new(ping_uri)
# Add the Authorization header
request['Authorization'] = "Element #{ELEMENT_TOKEN}, User #{user.token}"
# Actually send the request
response = http.request(request)
# Ruby will automatically close the connection once we exit the block
end
Once the block exits, you can use the response object as necessary. The response object is always a subclass (or subclass of a subclass) of Net::HTTPResponse and you can use response.is_a? Net::HTTPSuccess to check for a 2xx response. The actual body of the response will be in response.body as a String.

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
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.
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
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
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)
If anyone is still facing timeout setting issue and Net::HTTP timeout not working as expected, then you may follow below approach as well:
begin
Timeout::timeout(10) {
####
## YOUR REQUEST CODE WILL BE HERE
####
}
rescue
408
end

Rails open() times out. How to remove time limit?

The following code gives me a Timeout::Error
open("#{local_file}", 'wb') do |file|
file << open(remote_file_url).read
end
How can I increase the timeout? The file to download is big and usually takes more than 2 minutes in the browser.
I notice you are opening a remote file "remote_file_url". I imagine this is where the timeout is coming from.
For starters, I wrote a net/http connection class that includes timeout code you can use or model. https://github.com/bf4/Notes/blob/master/code/connection.rb (I believe the open method is using net/http)
You can also get the response in a block
e.g. something like
local_file, url, timeout = 'foo', 'http://www.example.com/index.html', 60
File.open(local_file, 'wb') do |file|
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
http.open_timeout = timeout
http.read_timeout = timeout
http.use_ssl = (uri.scheme == 'https')
http.request_get(uri.request_uri) do |response|
response.read_body do |segment|
file.write(segment)
end
end
end
see http://www.ruby-doc.org/stdlib-1.8.7/libdoc/net/http/rdoc/Net/HTTPResponse.html and http://www.ruby-doc.org/stdlib-1.8.7/libdoc/net/http/rdoc/Net/HTTP.html

Resources