Sinatra app that redirects POST/GET requests with parameters - ruby

I'm migrating servers but unfortunately the old server IP is hardcoded inside my iPhone app. Obviously I'm going to submit an update that sets the API endpoint to my new server, but in the meantime I need to setup an app on the old server that redirects all the requests to the new server. I've heard Sinatra would be perfect for this.
require 'sinatra'
get "/foo/bar" do
redirect "http://new-server.com/foo/bar", 303
end
post "/foo/bar" do
redirect "http://new-server.com/foo/bar", 303
end
The problem is that these do not forward the GET or POST parameters along with the request. I read on the Sinatra doc that you can do that by putting them in the URL directly (works for GET requests), or by setting session variables.
Is manually parsing and formatting the GET params to put them back into the redirect URL the only way to go for GET redirects? How are you supposed to forward POST parameters?

For GET requests, use request.fullpath or request.query_string. For POST request, use status code 307, so the subsequent request will be a POST with the same parameters.
helpers do
def new_url
"http://new-server.com" + request.fullpath
end
end
get "/foo/bar" do
redirect new_url
end
post "/foo/bar" do
redirect new_url, 307
end

I would overload the Hash class in lib/overload_hash.rb, like so:
class Hash
def to_url_params
elements = []
keys.size.times do |i|
elements << "#{keys[i]}=#{values[i]}"
end
elements.join('&')
end
end
EDIT (Better solution using net / http)
Place a require "lib/overload_hash", require "net/http" and require "uri" under require 'sinatra'. The following example can be translated into GET easily.
post '/foo/bar' do
uri = URI.parse("http://example.com/search")
response = Net::HTTP.post_form(uri, params.to_ur_params)
response
end

Related

How do I add custom logging for which route is satisfying a request in Sinatra?

I'm working on a Sinatra app that has a bunch of routes of all sorts. I'd like to add some custom logging that logs the params of the get or post call that ends up generating the response for the request. I realize I could subclass the get/post definition to wrap the block with a logging call. But I suspect there is a more appropriate approach.
You can use Sinatra's before hook in your controller, and print out some information contained on the request
before do
if request.request_method == :get || request.request_method == :post
puts request.path_info, params.inspect # check out the request variable for more info you might like to ouput
end
end

how can i store/retrieve files in owncloud from a webapp written in opal/ruby?

I have a webapp written mostly in ruby compiled with opal. I now would like to store/retrieve file in my owncloud, maybe using WebDAV. I am looking for an example how to do this using HTTP module.
I tried
HTTP.get("https://owncloud/foo.abc") do |req|
req.username= "user"
...
end.then do |response|
puts response
end
But that does not work. no method then for module HTTP.
So it seem that if I pass a block to HTTP.get it no longer returns a promise.
When I do not pass a block I don' know
how to configure the request.
Best if I could find an full example how to use HTTP from opal.
The small example in opal blog die not hell out.
I think username/password should be passed in the options hash (see the opal-jquery README).
HTTP.get("https://owncloud/foo.abc", username: 'user').then do |response|
puts response
end
A note about the Promise-style:
The block is used as the default form of callback. To switch to promise-style you should not pass any block, instead try assigning the result of HTTP.get to a variable to modify the request options:
req = HTTP.get("https://owncloud/foo.abc")
puts req.inspect # <= do something with the request
req.then do |response|
puts response
end

Maintaining session and cookies over a 302 redirect

I am trying to make fetch a PDF file that gets generated on-demand behind an auth wall. Based on my testing, the flow is as follows:
I make a GET request with several parameters (including auth credentials) to the appropriate page. That page validates my credentials and then processes my request. When the request is finished processing (nearly instantly), I am sent a 302 response that redirects me to the location of the generated PDF. This PDF can then only be accessed by that session.
Using a browser, there's really nothing strange that happens. I attempted to do the same via curl and wget without any optional parameters, but those both failed. I was able to get curl working by adding -L -b /tmp/cookie.txt as options, though (to follow redirects and store cookies).
According to the ruby-doc, using Net::HTTP.start should get me close to what I want. After playing around with it, I was indeed fairly close. I believe the only issue, however, was that my Set-Cookie values were different between requests, even though they were using the same http object in the same start block.
I tried keeping it as simple as possible and then expanding once I got the results I was looking for:
url = URI.parse("http://dev.example.com:8888/path/to/page.jsp?option1=test1&option2=test2&username=user1&password=password1")
Net::HTTP.start(url.host, url.port) do |http|
# Request the first URL
first_req = Net::HTTP::Get.new url
first_res = http.request first_req
# Grab the 302 redirect location (it will always be relative like "../servlet/sendfile/result/543675843657843965743895642865273847328.pdf")
redirect_loc = URI.parse(first_res['Location']
# Request the PDF
second_req = Net::HTTP::Get.new redirect_loc
second_res = http.request first_req
end
I also attempted to use http.get instead of creating a new request each time, but still no luck.
The problem is with cookie: it should be passed within the second request. Smth like:
second_req = Net::HTTP::Get.new(uri.path, {'Cookie' => first_req['Set-Cookie']})

Ruby Sinatra: Can I update a view template multiple times within one client request

I want to do this:
get '/test' do
#dog = 'WOOF'
erb :test
sleep(1)
#dog = 'BOWWOW'
erb :test
sleep(1)
#dog = 'ARF'
erb :test
end
Is it possible to do something like this where the client sees each update or no, I've tried but can't get it to work.
In short: no.
I think you're confusing the way HTTP works. First, HTTP is stateless. This means that multiple requests know nothing about each other (this is mitigated by the use of sessions via cookies, or possibly HTTP basic auth).
Further, you cannot resend the HTTP body like you're doing. Once it's sent there's no going back. Techniques like long-polling delay sending the body so they can send it whenever they like, but once they send something the request is complete and a new one must be started. Thus, once you've rendered the body once via erb, you're request is finished.
What it seems like you're trying to achieve can only be done via Javascript with AJAX, or with completely separate full-page requests.

Using Cookies with Rack::Test

I'm trying to write RSpec tests for my Sinatra application using Rack::Test. I can't understand how I can use cookies. For example if my application set cookies (not via :session) how can I check whether that cookie is properly set?
Also, how can I send requests with that cookie?
Rack::Test keeps a cookie jar that persists over requests. You can access it with rack_mock_session.cookies. Let's say you have a handler like this:
get '/cookie/set' do
response.set_cookie "foo", :value => "bar"
end
Now you could test it with something like this:
it 'defines a cookie' do
get '/'
rack_mock_session.cookie_jar["foo"].should == "bar"
end
You can also access cookies with last_request.cookies, but as the name says, it contains the cookies for the last request, not the response. You can set cookies with set_cookie and clear them with clear_cookies.
it 'shows how to set a cookie' do
clear_cookies
set_cookie "foo=quux"
get '/'
last_request.cookies.should == {"foo" => "quux"}
end
Update: If you want the cookie jar to persist across the test cases (it blocks), you need to initialize the Rack session before executing any test cases. To do so, add this before hook to your describe block.
before :all do
clear_cookies
end
Alternative, you could for example use before :each to set up the necessary cookies before each request.

Resources