XML-RPC over SSL with Ruby: end of file reached (EOFError) - ruby

I have some very simple Ruby code that is attempting to do XML-RPC over SSL:
require 'xmlrpc/client'
require 'pp'
server = XMLRPC::Client.new2("https://%s:%d/" % [ 'api.ultradns.net', 8755 ])
pp server.call2('UDNS_OpenConnection', 'sponsor', 'username', 'password')
The problem is that it always results in the following EOFError exception:
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/net/protocol.rb:135:in `sysread': end of file reached (EOFError)
So it appears that after doing the POST, I don't get anything back. Interestingly, this is the behavior I would expect if I tried to make an HTTP connection on the HTTPS port (or visa versa), and I actually do get the same exact exception if I change the protocol. Everything I've looked at indicates that using "https://" in the URL is enough to enable SSL, but I'm starting wonder if I've missed something.
Note that Even though the credentials I'm using in the RPC are made up, I'm expecting to at least get back an XML error page (similar to if you access https://api.ultradns.net:8755/ with a web browser). I've tried running this code on OSX and Linux with the exact same result, so I have to conclude that I'm just doing something wrong here. Does anyone have any examples of doing XML-RPC over SSL with Ruby?

http://www.ultradns.net/api/NUS_API_XML.pdf explicitly states that the protocol is not compatible with standard XML-RPC clients. You need to add a toplevel transaction and session tag on top of method call.
<transaction>
<methodCall>
...
</methodCall>
</transaction>
So I guess the ruby xml-rpc parser is just not being able to parse the response. Just a theory. Have your tried other xml-rpc clients?

Related

Get request with curl works but Net::HTTP and OpenURI fail

My Rails application regularly polls partners' ICS files and sometimes it fails for no reason whatsoever. When I do:
curl https://www.airbnb.es/calendar/ical/234892374.ics?s=23412342323
(params #'s faked here)
I get output matching the content of the ICS file. Just opening it in the browser works fine as well.
When I use:
Net::HTTP.get(URI(a.ics_link))
I get a "503 Service Temporarily Unavailable" response. I also tried the same with OpenURI with similar results.
Why is it that the server is treating requests from curl or a browser differently?
Is there some way to get Ruby to get around this?
It's an https issue... not sure why, but switch your url in Ruby to https and it should work.

How to work around obsolete networking protocol in ruby 1.8.6

I have a lot of software running on an embedded system with ruby 1.8.6. One script queries the national weather service and a few days ago it stopped working: I started getting <H1>Access Denied</H1>. This is a public interface and there's no API key.
I get the same error when I run this script using ruby 1.8.6 on my development machine. When I run it with ruby 1.9, the error doesn't happen.
Evidently the NWS modified their HTTP protocol in some way last week that is incompatible with Net:HTTP in ruby 1.8.6
The URL is "http://graphical.weather.gov/xml/sample_products/browser_interface/ndfdXMLclient.php?lat=37&lon=-105&product=time-series&begin=&end=&sky=sky&maxt=maxt&mint=mint&Submit=Submit"
I get a good response when I use curl.
The code is:
begin
xml = Net::HTTP.get_response(URI.parse(url)).body
rescue
I have RestClient 1.6 and tried substituting that for Net:HTTP but it just uses Net:HTTP internally and I get a 403 Forbidden.
Does anyone have any idea what could have changed in the protocol and a way to work around it?
I think I may be able to get updated firmware from the system manufacturer but I hesitate to go that route because it will force rewriting of many rather fragile hacks.
According to your traffic capture analysis, the issue is that 1.8.6 doesn't add HTTP User-Agent field by default.
One way to add it is:
http = Net::HTTP.new("your.site.com", 80)
req = Net::HTTP::Get.new("/path/to/the/page.html", {'User-Agent' => 'your_agent'})
response = http.request(req)
puts response.body

Net::ReadTimeout with local URI

I'm trying to understand why is the following method is blocking my app.
url = 'http://192.168.1.33/assets/my_small_pic.jpg'
image_file = open(url).read
It's working perfectly when I try it in the console. But when I do it from an API method, it blocks my app and after a long while I have the following error:
Net::ReadTimeout (Net::ReadTimeout)
What does my app not like my way of reading the file?
I assume you're using 'open-uri' and the api is a part of the same RoR app where you're sending the request to. In this case your app is just being blocked by the first request while you send a second request to it so the second request gives a timeout. You should see this issue in development only. In production things will be a bit different since static assets to be served by the Nginx or Apache. Additionally Rails 4 is by default is thread safe in production which means it can serve multiple requests at a time. So if you're on Rails4 then in production calls to other apis will work as well. For Rails3 you would have to explicitly specify config.threadsafe!
In general I would recommend you to access resources or make any other API calls from the same app directly. It's more efficient. In your example above you can read the file like this:
File.read(File.join(Rails.root, 'public/assets/my_small_pic.jpg'))
If you still want to send the request then to make it work in development you have to start a new thread similar to this:
Thread.new do
open('http://192.168.1.33/assets/my_small_pic.jpg').read
end

Problems attempting to upload image to Twitter via POST in Sinatra

I'm using Sinatra 1.2.6 in Ruby 1.8.7 and I have something like a Twitter client that I'm writing. I am using the Twitter gem version 1.7.2 written by John Nunemaker. For database ORM I'm using Sequel 3.29.0.
Overall, things are working great. I've got a good Oauth sequence working and any user who goes through the Oauth process can post Tweets to my application.
I cannot however for the life of me get media upload working using update_with_media. I'm trying to upload a multi-part octet-stream image file, keep it in memory and then give it to Twitter.
post '/file_upload' do
user_name = params[:user]
if params[:action] == "FILE-UPLOAD"
unless params[:name].match(/\.jpg|png|jpeg/).nil?
#Assume these 3 lines work, and properly authorize to Twitter
current_user = User[:user_name => user_name, :current_account => "1"]
client = current_user.authorize_to_twitter #Handles the Oauth keys/process
client.update("Text status updates work properly")
#Something incorrect is happening in the next two lines.
#I'm either handling the file upload wrong, or posting wrong to Twitter
datafile = params[:file]
client.update_with_media("File upload from Skype: ", datafile)
return "File uploaded ok"
end
end
end
Yet, when I try this, I'm getting:
Twitter::Unauthorized - POST https://upload.twitter.com/1/statuses/update_with_media.json: 401: Could not authenticate with OAuth.
Its saying the line causing this error is the client.update_with_media line.
I am trying to use Rack::RawUpload, but I don't know if I'm using it incorrectly. If I don't need to use it I won't, but I'm just currently stuck. The only thing outside of this code snippet that's using it is this at the top of my code:
require 'rack/raw_upload'
use Rack::RawUpload
Any help on this would be massively appreciated. I've tried messing around with Tempfile.new() as well, but that didn't seem to help much, and I was either getting 401 or 403 errors. I'm fairly new to Ruby, so being as explicit as possible about changes needed would be really helpful.
I should note that I'd like to avoid putting the file on the filesystem if possible. I'm really just passing along the upload here, and I never need access in my scenario to the file on-disk afterward. Keeping the files in-memory is much preferred.
You need to check how your library HTTP headers are setup and logically connected to the POST method you have written here. The thing is that for upload_with_media, twitter api in this gem version requires you to use http://upload.twitter.com upload endpoint instead of the default api endpoint.
The gem may be forcing the api site so while the OAuth based status update works fine, it crashes when you try it with an image. You will need to check the gem documentation to figure out how to force the upload twitter site into the HTTP headers for this method.
Alternatively, consider updating to the latest twitter gem. This is what I got from http://rdoc.info/gems/twitter
The Twitter::API#update_with_media method no longer uses the custom upload.twitter.com endpoint, so media_endpoint configuration has been removed. Likewise, the Twitter::API#search method no longer uses the custom search.twitter.com endpoint, so search_endpoint configuration has also been removed.

Recieving a 404 HTTPError on a working page in Ruby Script

This is my first time asking a question, please be gentle!
I have a Rails application that handles content for a whole bunch of domains (over 100 so far). Each domain either points to where my app is hosted (Heroku, if you're interested), or the original place it was hosted. Every time a domain is ready, it needs to point to the heroku servers, so that my app can serve content for it.
To check to see if a domain has successfully been changed over from its original location to my application, I'm writing a script that looks for a special hidden tag I included in them. If it finds the tag, then the domain is pointing to my app. If not, it hasn't been changed, which I record.
The problem is that, at least for one domain so far, I'm getting a 404 OpenURI::HTTPError exception for my script. Which is strange, because I can visit the site just fine and I can even get it via curl. Does anyone know why a working site would get an error like this? Here's the important snippet:
require 'rubygems'
require 'open-uri'
require 'hpricot'
...
url = "http://www.#{domainname}.com"
doc = Hpricot(open(url)) #<---- Problem right here.
...
Thanks for all of your help!
Welcome to SO!
Here would be my debugging method:
See if you can replicate in irb with open-uri alone, no Hpricot:
$ irb -rubygems -ropen-uri
>> open('http://www.somedomain.com')
Look in your Heroku log to see if it even touches the server.
Look in your original server's log for the same.
Throw open something like Wireshark to see the HTTP transaction, and see if a 404 is indeed coming back.
Start with that, and come back with your results.

Resources