Ruby JSONP returns Errno::EPROTONOSUPPORT - ruby

This one is driving me nuts.
I have two websites who have to talk to each other trough JSONP.
Site A checks in its database if some value is present and returns json data {"result":"Found in library"} if found.
When doing a cURL from the command line, I get my data as expected.
Doing a Ajax call from site B, I can see in the logging of site A that the request is handled correct, but site B complains about "Unexpected end of data at line 1" The inspector shows me no data at all, so I assume that is the problem. Where did the data go?
As I don't want the inner workings of the Ajax call to be reveiled, I want to do it in the Ruby controller with a Net::HTTP call.
Again, I can see in the logging of site A that the call is handled correct and returns the result.
This time, Ruby logging shows:
Errno::EPROTONOSUPPORT (Failed to open TCP connection to xx.domain.com:443 (Protocol not supported - socket(2) for "xx.domain.com" port 443)):
My controller in site B:
def query
respond_to do |format|
format.js {
# Submit Ajax call
uri = URI(base_url + "/data_subjects/query.json")
data = {
key: api_key,
parmA: params[:parmA],
parmB: params[:parmB],
}
uri.query = URI.encode_www_form(data)
logger.info("QUERY: #{uri.inspect}")
res = Net::HTTP.get_response(uri)
render :json, JSON.parse(res)
}
end
end
My controller in site A:
def query
respond_to do |format|
format.json {
.... Do some stuff
if $rc == 1
render json: {:result => I18n.t("result_found")}, status: :ok , :layout => false
else
render json: {result: I18n.t("result_not_found")}, status: 404, :layout => false
end
}
end
As I said, using cURL everything looks okay, but with either jQuery or Ruby it fails.
UPDATE
Okay, found it!
As it turns out, you need to specify ":callback => params[:callback]" to the json response:
render json: {:result => I18n.t("result_found")}, :callback => params[:callback], status: :ok , :layout => false

Problem was triggered by not having IPv6 support in the FreeBSD jail in which the calling process runs. And the server which was called only has IPv6 connectivity.

Related

opal-jquery ajax request sending malformed json string

I'm using opal-jquery to send an ajax request to my backend, but the json payload is malformed when I try to access it on the backend.
When I do puts #params.to_s on my back end, it gets displays as:
{"{\"username\":\"some_user\", \"password\":\"some_password\"}"=>nil}
So to get the login details I have to do:
#params.each do |k,v|
login_details = JSON.parse(k)
end
My front end (opal) submit request looks like this:
def handle_login_submit details
url = "/client/api/auth"
HTTP.post url, payload: details.to_json, dataType: 'json' do |response|
case response.status_code / 100
when 2
puts response.status_code
self.login_state = :return_success
puts response.json
when 4
self.login_state = :return_failed
puts response.status_code
puts response.json
when 5
self.f_message = {type: :error, message: "There was a server error" }
self.login_state = :return_failed
end
end
end
I definitely know that details is correctly formatted so that's not an issue.
I guess in the end, it boils down to is whether it's
opal-jquery sending the post request incorrectly
sinatra improperly parsing the request.
I'm not sure which one it is, but I'm inclined to believe it's the former because it was working fine before I switched to using react.rb for the front end.
After following the advice from this, specifically I'm doing:
before do
request.body.rewind
req = request.body.read
puts req
puts JSON.parse(req)
end
I was able to see that the request arrived at the server correctly and as expected. I don't know why but sinatra was mangling the response in such a away that #params came out as
{"{\"username\":\"some_user\", \"password\":\"some_password\"}"=>nil}
so, in the end I'm doing
before do
request.body.rewind
#request_payload = JSON.parse(request.body.read)
request.body.rewind
end
To read out the request body before sinatra gets anything and just reading out the params I need from #request_payload.

better ruby - possible to not repeat parts of a function?

I have a little sinatra api that I'm trying to beautify. Most of my routes are simple db operations but a few involve calling an external service before doing db operations. In all cases most of the code is the same except for how I respond to the service response. Is there any slick functional programming approach?
Here's an example of one of these routes:
get '/update_x' do
validateParams(params,:x)
xid = params[:x]
xName = getNameFromId(xid)
if xName
# Make request to proxy service
rid = generateRandomHexNumber(16) # generate requestId
params['m'] = 'set'
params['rid'] = rid
json = "{}"
begin
response = #resource["/"+"?rid=#{rid}&id=#{xid}&json=#{json}"].get
status = response.code
body = response.body
parsed_json = JSON(body)
if parsed_json['response'] and parsed_json['response']['success'] and parsed_json['response']['success']=='false'
msg = {:success => "false", :response => "unknown error"}
if parsed_json['response']['response']
msg = {:success => "false", :response => parsed_json['response']['response']}
end
content_type :json
msg.to_json
else
#### Here is stuff specific to this api call
updateDBHelper(xid,buildUpdateOptions(params))
params['ss_status'] = status
content_type :json
params.to_json
#### End specific to api call
end
rescue Exception=>e
params['ss_status'] = status
params['exception'] = e
content_type :json
params.to_json
end
else
msg = {:success => "false", :response => "Not found"}
content_type :json
msg.to_json
end
end
In general if you have a general pattern with some arbitrary code that changes every time then the simplest thing would be accepting a block with those customizations.
def make_api_request(some, params)
# do what you need to do
yield(variables, that, your_custom_code, needs)
# do some more, maybe cleanup
end
get '/some_route' do
make_api_request do |variables, that, your_custom_code, needs|
# do custom stuff here
end
end

Faraday (Ruby) Timeout Errors

I'm attempting to put a small payload generated in route A (Sinatra app) to Route B using Faraday. So the code basically looks like:
post "/routeA" do
foo.save
foo_id = foo.id
conn = Faraday.new(:url => "http://localhost:3001/routeB" ) do |builder|
builder.request :url_encoded
builder.response :logger
builder.adapter :net_http
end
resp = conn.put do |req|
req.url '/routeB'
req.headers['Content-Type'] = 'application/json'
req.body = {:id => foo_id }.to_json
req.options = {
#:timeout => 5, # see below, these aren't the problem
#:open_timeout => 2
}
end
# never gets here b/c Timeout error always thrown
STDERR.puts resp.body
end
put "/routeB" do
# for test purposes just log output
STDERR.puts request.body.read.to_s.inspect
status 202
body '{"Ok"}'
end
Problem is that it always throws a timeout error (I've run without the timeout options, and with the ones shown above -> same results). However, the logs show the request is going through:
I, [2012-03-24T16:56:13.241329 #17673] INFO -- : put http://localhost:3001/routeB
D, [2012-03-24T16:56:13.241427 #17673] DEBUG -- request: Content-Type: "application/json"
#<Faraday::Error::TimeoutError>
DEBUG - POST (60.7987ms) /routeA - 500 Internal Server Error
"{\"id\":7}"
DEBUG - PUT (0.0117ms) /routeB - 202 Accepted
Not sure how to get past the timeout error? Any insight would be appreciated. Thanks.
The problem is that the application cannot respond to another request until it's finished with the current one. That is, when you make a PUT request on the /routeB, the application got that, and it's waiting for the current request (/routeA) to finish. But the request won't finish because it's waiting to get the response from the /routeB. I think this is what causes the timeout error.

Why is Net::HTTP timing out when I try to access a Prawn Generated PDF?

I am using Prawn to generate a PDF from my controller, and when accessed directly at the url, it works flawlessly, I.E. localhost:3000/responses/1.pdf
However, when I try to generate this file on the fly for inclusion in a Mailer, everything freezes up and it times out.
I have tried various methods for generating / attaching the file and none have changed the outcome.
I also tried modifying the timeout for Net::HTTP to no avail, it just takes LONGER to time out.
If I run this command on the Rails Console, I receive a PDF data stream.
Net::HTTP.get('127.0.0.1',"/responses/1.pdf", 3000)
But if I include this code in my controller, it times out.
I have tried two different methods, and both fail repeatedly.
Method 1
Controller:
http = Net::HTTP.new('localhost', 3000)
http.read_timeout = 6000
file = http.get(response_path(#response, :format => 'pdf')) #timeout here
ResponseMailer.confirmComplete(#response,file).deliver #deliver the mail!
Method 1 Mailer:
def confirmComplete(response,file)
email_address = response.supervisor_id
attachments["test.pdf"] = {:mime_type => "application/pdf", :content=> file}
mail to: email_address, subject: 'Thank you for your feedback!'
end
The above code times out.
Method 2 Controller:
ResponseMailer.confirmComplete(#response).deliver #deliver the mail!
Method 2 Mailer:
def confirmComplete(response)
email_address = response.supervisor_id
attachment "application/pdf" do |a|
a.body = Net::HTTP.get('127.0.0.1',"/responses/1.pdf", 3000) #timeout here
a.filename = "test.pdf"
end
mail to: email_address, subject: 'Thank you for your feedback!'
end
If I switch the a.body and a.filename, it errors out first with
undefined method `filename=' for #<Mail::Part:0x007ff620e05678>
Every example I find has a different syntax or suggestion but none fix the problem that Net::HTTP times out. Rails 3.1, Ruby 1.9.2
The problem is that, in development, you're only running one server process, which is busy generating the email. That process is sending another request (to itself) to generate a PDF and waiting for a response. The request for the PDF is basically standing in line at the server so that it can get it's PDF, but the server is busy generating the email and waiting to get the PDF before it can finish. And thus, you're waiting forever.
What you need to do is start up a second server process...
script/rails server -p 3001
and then get your PDF with something like ...
args = ['127.0.0.1','/responses/1.pdf']
args << 3001 unless Rails.env == 'production'
file = Net::HTTP.get(*args)
As an aside, depending on what server you're running on your production machine, you might run into issues with pointing at 127.0.0.1. You might need to make that dynamic and point to the full domain when in production, but that should be easy.
I agree with https://stackoverflow.com/users/811172/jon-garvin's analysis that you're only running one server process, but I would mention another solution. Refactor your PDF generation so you don't depend on your controller.
If you're using Prawnto, I'm guessing you have a view like
# app/views/response.pdf.prawn
pdf.text "Hello world"
Move this to your Response model: (or somewhere else more appropriate, like a presenter)
# app/models/response.rb
require 'tmpdir'
class Response < ActiveRecord::Base
def pdf_path
return #pdf_path if #pdf_generated == true
#pdf_path = File.join(Dir.tmpdir, rand(1e11).to_s)
Prawn::Document.generate(#pdf_path) do |pdf|
pdf.text "Hello world"
end
#pdf_generated = true
#pdf_path
end
def pdf_cleanup
if #pdf_generated and File.exist?(#pdf_path.to_s)
File.unlink #pdf_path
end
end
end
Then in your ResponsesController you can do:
# app/controllers/responses_controller.rb
def show
#response = Response.find params[:id]
respond_to do |format|
# this sends the PDF to the browser (doesn't email it)
format.pdf { send_file #response.pdf_path, :type => 'application/pdf', :disposition => 'attachment', :filename => 'test.pdf' }
end
end
And in your mailer you can do:
# this sends an email with the PDF attached
def confirm_complete(response)
email_address = response.supervisor_id
attachments['test.pdf'] = {:mime_type => "application/pdf", :content => File.read(response.pdf_path, :binmode => true) }
mail to: email_address, subject: 'Thank you for your feedback!'
end
Since you created it in the tmpdir, it will be automatically deleted when your server restarts. You can also call the cleanup function.
One final note: you might want to use a different model name like SupervisorReport or something - Response might get you in namespacing trouble later)

Rails 3 - Testing controller's GET method - trying to use JSON and getting 406 errors

I have a simple controller that specifies:
respond_to :json
When I try to build a functional test that calls it like this:
test "GET" do
get 'index', :format => :json
end
Everything works just fine. If, however, I try to pass a query parameter in like this:
test "GET" do
get 'index', {:my_param = '1234'}, :format => :json
end
I get a 406 error returned from the controller. If I dump the response via response.inspect, I can see that the #status = 406 and the #header has a content-type of text/html. If I dump the response via response.inspect for the simple case that doesn't pass a query parameter, I see that the #header has a content-type of application/json as I request in the code.
Does anyone have any ideas what I'm doing wrong here? I'm suspecting that I'm screwing up ruby syntax with hashes or something but I'm bumping my head against the wall and not getting anywhere.
I'm on a Mac running Ruby 1.9 and Rails 3.0.5.
Thanks in advance!
This is the function you are calling:
get(action, parameters = nil, session = nil, flash = nil)
The :format part is still a parameter, certainly not a session or flash. Try:
get 'index', {:my_param => '1234', :format => :json}
Oh, important note, use '=>', not '=' ... that's a hash, not an assignment.

Resources