I'm trying to verify whether a user has control of a domain as part of my rails 3 app by uploading a randomly generated file to the root of their domain (the same way google does it for google apps). Currently I'm using Net::HTTP and it seems to work if the address is valid but if the address is not valid I get this error.
getaddrinfo: nodename nor servname provided, or not known
Is there a way to somehow ignore this error or a better/ different way to wrtite the code for the controller?
def check
require 'net/http'
require 'uri'
result = Net::HTTP.get_response(URI.parse('http://www.example.com/21312324213123.html'))
if Net::HTTPSuccess
#test = "true"
else
#test = "false"
end
end
Any help would be great. Thanks.
All you need to do is rescue from that exception and return that as a separate case.
def check
# ...
rescue => e
# Uh oh, got an exception
#error = e.to_s
#test = "false"
end
Related
So I have a Sinatra API containing this piece of code in a model:
def self.delete(account_id)
# using Sequel, not ActiveRecord:
if Account[account_id][:default] == true
abort("Impossible to delete a default account. Please first set another account to default.")
end
# rest of the code
end
then, in app.rb :
delete '/account/:id' do
if Account.delete(params[:id]) == 1
status 200
else
status 500
end
end
On the client side (vuejs app), I would like the error message to be displayed. Instead, when the error produces, I get a SystemExit with the error message.
How do I send that SystemExit message to the server?
In Ruby in general you want to break out of execution either by using return, exceptions or throw/catch. abort is rarely if ever used as it will immediately halt execution of the entire program - and prevent you from doing stuff like cleaning up or actually sending a meaningful response to the client besides whatever error page the web server will render if you just quit the job half way though.
You can easily implement this by creating your own exception class:
class AccountDeletionError < StandardError
end
def self.delete(account_id)
# using Sequel, not ActiveRecord:
if Account[account_id][:default] == true
raise AccountDeletionError.new, "Impossible to delete a default account. Please first set another account to default."
end
end
delete '/account/:id' do
if Account.delete(params[:id]) == 1
status 200
end
rescue AccountDeletionError => e
status 409 # not 500.
content_type :json
{ error: e.message }.to_json
end
end
Now that you know how to handle errors you should probally address the next possible one - when an account cannot be found.
In my Rails app I am making various API calls like this one:
class SearchFacebook
FALLBACK_STARS = 5.0
def self.call
begin
url = "https://graph.facebook.com/v2.12/#{FACEBOOK_PAGE_ID}"
response = RestClient.get(url, :params => {:access_token => FACEBOOK_ACCESS_TOKEN, :fields => "overall_star_rating"})
json = JSON.parse(response)
stars = json["overall_star_rating"]
rescue RestClient::ExceptionWithResponse => error
stars = FALLBACK_STARS
end
{:stars => stars}
end
end
The problem is that I am running my app in my local environment a lot of the time. When there's no Internet connection available (which happens a lot to me, don't ask why), I get the following error:
SocketError.
Failed to open TCP connection to api.facebook.com:443 (getaddrinfo:
nodename nor servname provided, or not known)
How can I rescue from being offline?
I checked RestClient documentation already but to no avail. Thanks for any help.
Can you use a blanket rescue => e and use byebug to inspect the exception being raised. You could then rescue that error if in a development environment.
There are some other options mentioned here Check if Internet Connection Exists with Ruby?
OK, I solved this by simply rescuing from a SocketError:
rescue SocketError, RestClient::ExceptionWithResponse => error
I found this answer quite helpful.
I tried many URLs on this and they seem to be fine until I came across this particular one:
require 'rubygems'
require 'nokogiri'
require 'open-uri'
doc = Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))
puts doc
This is the result:
/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in `open_http': 404 Not Found (OpenURI::HTTPError)
from /Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:709:in `buffer_open'
from /Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:210:in `block in open_loop'
from /Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:208:in `catch'
from /Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:208:in `open_loop'
from /Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:149:in `open_uri'
from /Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:689:in `open'
from /Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:34:in `open'
from test.rb:5:in `<main>'
I can access this from a web browser, I just don't get it at all.
What is going on, and how can I deal with this kind of error? Can I ignore it and let the rest do their work?
You're getting 404 Not Found (OpenURI::HTTPError), so, if you want to allow your code to continue, rescue for that exception. Something like this should work:
require 'nokogiri'
require 'open-uri'
URLS = %w[
http://www.moxyst.com/fashion/men-clothing/underwear.html
]
URLs.each do |url|
begin
doc = Nokogiri::HTML(open(url))
rescue OpenURI::HTTPError => e
puts "Can't access #{ url }"
puts e.message
puts
next
end
puts doc.to_html
end
You can use more generic exceptions, but then you run into problems getting weird output or might handle an unrelated problem in a way that causes more problems, so you'll need to figure out the granularity you need.
You could even sniff either the HTTPd headers, the status of the response, or look at the exception message if you want even more control and want to do something different for a 401 or a 404.
I can access this from a web browser, I just don't get it at all.
Well, that could be something happening on the server side: Perhaps they don't like the UserAgent string you're sending? The OpenURI documentation shows how to change that header:
Additional header fields can be specified by an optional hash argument.
open("http://www.ruby-lang.org/en/",
"User-Agent" => "Ruby/#{RUBY_VERSION}",
"From" => "foo#bar.invalid",
"Referer" => "http://www.ruby-lang.org/") {|f|
# ...
}
You might need to pass 'User-Agent' as parameter to open method. Some sites require a valid User-Agent otherwise they simply don't respond or show a 404 not found error.
doc = Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html", "User-Agent" => "MyCrawlerName (http://mycrawler-url.com)"))
So what is going on and how can I deal with this kind of error.
No clue what's going on, but you can deal with it by catching the error.
begin
doc = Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))
puts doc
rescue => e
puts "I failed: #{e}"
end
Can I just ignore it and let the rest do their work?
Sure! Maybe? Not sure. We don't know your requirements.
Just asked how to check if an internet connection exists using javascript and got some great answers. What's the easiest way to do this in Ruby? In trying to make generated html markup code as clean as possible, I'd like to conditionally render the script tag for javascript files depending on whether or not an internet condition. Something like (this is HAML):
- if internet_connection?
%script{:src => "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js", :type => "text/javascript"}
- else
%script{:src => "/shared/javascripts/jquery/jquery.js", :type => "text/javascript"}
require 'open-uri'
def internet_connection?
begin
true if open("http://www.google.com/")
rescue
false
end
end
This is closer to what the OP is looking for. It works in Ruby 1.8 and 1.9. It's a bit cleaner too.
I love how everyone simply assume that googles servers are up. Creds to google.
If you want to know if you have internet without relying on google, then you could use DNS to see if you are able to get a connection.
You can use Ruby DNS Resolv to try to translate a url into an ip address. Works for Ruby version 1.8.6+
So:
#The awesome part: resolv is in the standard library
def has_internet?
require "resolv"
dns_resolver = Resolv::DNS.new()
begin
dns_resolver.getaddress("symbolics.com")#the first domain name ever. Will probably not be removed ever.
return true
rescue Resolv::ResolvError => e
return false
end
end
Hope this helps someone out :)
You can use the Ping class.
require 'resolv-replace'
require 'ping'
def internet_connection?
Ping.pingecho "google.com", 1, 80
end
The method returns true or false and doesn't raise exceptions.
Same basics as in Simone Carletti's answer but compatible with Ruby 2:
# gem install "net-ping"
require "net/ping"
def internet_connection?
Net::Ping::External.new("8.8.8.8").ping?
end
require 'open-uri'
page = "http://www.google.com/"
file_name = "output.txt"
output = File.open(file_name, "a")
begin
web_page = open(page, :proxy_http_basic_authentication => ["http://your.company.proxy:80/", "your_user_name", "your_user_password"])
output.puts "#{Time.now}: connection established - OK !" if web_page
rescue Exception
output.puts "#{Time.now}: Connection failed !"
output.close
ensure
output.close
end
I was trying to find a solution to a problem similar to yours and could not find any. Unfortunately the Ping.pingecho method doesn't work for me for some reason i don't know. I came up with a solution. The latest way to do it using httparty. I wanted this in a module and so did it this way and it works just fine
# gem install httparty
require "httparty"
module Main
def Main.check_net
begin
a = HTTParty.get("https://www.google.com")
if a.length() >= 100
puts "online"
end
rescue SocketError
puts "offline"
end
end
end
include Main
Main.check_net
A socket error to Google might not happen so this method will work
def connected?
!!Socket.getaddrinfo("google.com", "http")
rescue SocketError => e
e.message != 'getaddrinfo: nodename nor servname provided, or not known'
end
Since it uses a hostname the first thing it needs to do is DNS lookup, which causes the exception if there is no internet connection.
I want to be able to spec out that when Open-Uri open() calls either timeout or raise an exception such as SocketError I am handling things as expected, however I'm having trouble with this.
Here is my spec (for SocketError):
#obj.should_receive(:open).with("some_url").and_raise(SocketError)
And the part of my object where I'm using open-uri:
begin
resp = open(url)
resp = resp.read
rescue SocketError
something = true
end
However in this situation the spec fails as with a nil.read error.
This is the second time this week I've come across this problem, the previous time I was attempting to simulate a TimeoutError when wrapping open() with a timeout() {}, that time I gave up and just caused an actual timeout to happen by opening up the class. I could obviously cause this to throw a SocketError by trying to call an invalid URL, but I'm sure there is a correct way to mock this out with RSpec.
Update: I obviously wasn't thinking clearly that late at night, the error was actually when I re-tried the URL after the SocketError, the and_raise(SocketError) part worked fine.
The line you provided should work, based on the information you've given: I made a tiny test class and spec (see below) with only the described functionality, and things behaved as expected. It might be helpful if you could provide a little more context - the full "it" block from the spec, for instance, might expose some other problem.
As mentioned, the following spec passes, and I believe it captures the logic you were attempting to verify:
require 'rubygems'
require 'spec'
class Foo
attr_accessor :socket_error
def get(url)
#socket_error = false
begin
resp = open(url)
resp = resp.read
rescue SocketError
#socket_error = true
end
end
end
describe Foo do
before do
#foo = Foo.new
end
it "should handle socket errors" do
#foo.should_receive(:open).with("http://www.google.com").and_raise(SocketError)
#foo.get("http://www.google.com")
#foo.socket_error.should be_true
end
end