How to tell Faraday to preserve hashbang in site URL? - ruby

I'm working on a fork of a library that implements Faraday to build URLs.
site = "https://example.io/#/"
path = "oauth/authorize"
connection = Faraday.new(site)
resource = Faraday::Utils.URI(path)
URL = connection.build_url(resource)
Notice that my site URL ends with a hashbang. But when the above code is executed, Faraday strips out the hashbang entirely:
https://example.io/oauth/authorize
But my application requires it to build this URL (with the hashbang):
https://example.io/#/oauth/authorize
Now before I go ripping out Faraday and monkey-patching something terrible.. can I do this by setting an option on Faraday?

I think the answer here would be to quit trying to preserve the hash portion of the URL in Faraday since that portion is ignored for HTTP requests.
The hash part of the URL (also known as URI "fragment identifier") is never sent to the server. It can only have a meaning in the client. Typically, when the HTTP client is a web browser, the fragment identifier holds the name of the element to scroll to. Or, hashbang tricks can be employed with some JavaScript interaction.
But to use such URLs in Faraday doesn't make sense because the hash portion will never get sent to the server anyway.

Having '#' in path variable instead of site variable i am getting the output as you require.
site = "https://example.io/"
path = "#/oauth/authorize"
connection = Faraday.new(site)
resource = Faraday::Utils.URI(path)
URL = connection.build_url(resource)
Please try the above code and let me know the result.

Related

Capybara 'visit' is not considering '#' character in URL?

Im running into a strange issue, I have my app host set to a remote site like so:
Capybara.configure do |config|
config.default_max_wait_time = 10 # seconds
config.default_driver = :selenium_chrome
config.app_host = 'https://dev.stagingsite.com/widget-web/#/'
end
All relative paths come after the /#/, so in my visit methods (such as session.visit('login') will take me to the URL:
https://dev.stagingsite.com/widget-web/login#/
I've tried adding '/' in various places, escaping the # but it seems to not be able to put relative url's after the # and instead always tries to put it after the widget-web/
Any ideas what is going on here?
From MDN's URL documentation:
It is worth noting that the part after the #, also known as the fragment identifier, is never sent to the server with the request.
Check your server logs; you'll see that any request to /widget-web/#/ is sent as a request to /widget-web/. The # prevents anything after it from being sent to the server.
What is a URL? docs
UPDATE Actually after thinking about this a while it would probably do what you want if you did visit '#login' since the fragment passed in visit would get merged over the app_host fragment
Capybara visit doesn't just append to app_host - it parses app_host and what's passed to visit and merges them. visit accepts either a full url or a relative path, what you're trying to set is the URL fragment - which visit has no way of identifying as what you want to do - https://en.wikipedia.org/wiki/Fragment_identifier. If you want to be setting the fragment in browser you're going to need to pass a full URL to visit (possibly by using your own helper function to build the URL you want)

HttpClient is caching requests to the same URL

On Xamarin iOS. I'm using HttpClient to get a JSON string. The problem is that it ignores updates and gives me the same JSON response if I query the same URL. I want it to not cache anything and always actually query the URL and give me the new response.
This sounds trivial, there must be a simple way for this. I'm using a Forms shared project.
I assume you are setting the cache-control header to no-cache?
client.DefaultRequestHeaders.CacheControl.NoCache = true;
If so but it still doesn't work - maybe the server is caching the response? If it comes down to it, you can usually defeat something like that by adding a cachebuster to the querystring though. Just append a bogus param and pass it a unique value each time. For example, if your URL is http://my.url.com/resource/someid then you can defeat the caching by using http://my.url.com/resource/someid?b=1 and then increment that "b" param with each call.

RestClient using Resource for GET action

I am writing a ruby client for the REST API using RestClient gem. While going through examples, I see different code used to achieve basically the same result, without any explanation on the difference.
client = RestClient::Resource.new('https://example.com/')
response = client.get
VS
response = RestClient.get('https://example.com/')
What is the benefit of using Resource class, if I can achieve same thing with get method?
Code reuse. It's especially useful when you're dealing with APIs, and you need to hit the same base urls over and over, with different params and/or paths. As the docs show you, once you build a base resource:
client = RestClient::Resource.new('https://example.com/')
You can access other paths under this resource quite easily:
response = client["/users/1"].get
Which is equivalent to
response = RestClient.get("https://example.com/users/1")
but less typing/repetition.

Sinatra Url '/' interpretations

I am a ruby newbie and have been trying Sinatra for quite some time now, one thing that Iam not able to figure out is why does a '/' in the url make such a big difference.
I mean isnt:
get 'some_url' do
end
and
get 'some_url/' do
end
Supposed to point to the same route? why is that Sinatra considers it as different routes? I spent a good one hour trying to figure that out.
According to RFC 2616 and RFC 2396 (RFCs defining resource identity) those URLs do not define the same resource. Therefore Sinatra treats them differently. This is esp. important if you imagine the route returning a page with relative links. This link
click me
Would point to /bar if you're coming from /foo, to /foo/bar if you're coming from /foo/.
You can use the following syntax to define a route matching both:
get '/foo/?' do
# ...
end
Or the Regexp version mentioned in the comments above.
They are different routes. The second is a URL with a directory extension ('/'); the first is a URL with no extension. A lot of frameworks (like Rails) will interpret both as the same route, or append the `/' (e.g., Django, and Apache can be configured to do that as well), but technically they are different URLs.

Ruby's open-uri and cookies

I would like to store the cookies from one open-uri call and pass them to the next one. I can't seem to find the right docs for doing this. I'd appreciate it if you could tell me the right way to do this.
NOTES: w3.org is not the actual url, but it's shorter; pretend cookies matter here.
h1 = open("http://www.w3.org/")
h2 = open("http://www.w3.org/People/Berners-Lee/", "Cookie" => h1.FixThisSpot)
Update after 2 nays: While this wasn't intended as rhetorical question I guarantee that it's possible.
Update after tumbleweeds: See (the answer), it's possible. Took me a good while, but it works.
I thought someone would just know, but I guess it's not commonly done with open-uri.
Here's the ugly version that neither checks for privacy, expiration, the correct domain, nor the correct path:
h1 = open("http://www.w3.org/")
h2 = open("http://www.w3.org/People/Berners-Lee/",
"Cookie" => h1.meta['set-cookie'].split('; ',2)[0])
Yes, it works. No it's not pretty, nor fully compliant with recommendations, nor does it handle multiple cookies (as is).
Clearly, HTTP is a very straight-forward protocol, and open-uri lets you at most of it. I guess what I really needed to know was how to get the cookie from the h1 request so that it could be passed to the h2 request (that part I already knew and showed). The surprising thing here is how many people basically felt like answering by telling me not to use open-uri, and only one of those showed how to get a cookie set in one request passed to the next request.
You need to add a "Cookie" header.
I'm not sure if open-uri can do this or not, but it can be done using Net::HTTP.
# Create a new connection object.
conn = Net::HTTP.new(site, port)
# Get the response when we login, to set the cookie.
# body is the encoded arguments to log in.
resp, data = conn.post(login_path, body, {})
cookie = resp.response['set-cookie']
# Headers need to be in a hash.
headers = { "Cookie" => cookie }
# On a get, we don't need a body.
resp, data = conn.get(path, headers)
Thanks Matthew Schinckel your answer was really useful. Using Net::HTTP I was successful
# Create a new connection object.
site = "google.com"
port = 80
conn = Net::HTTP.new(site, port)
# Get the response when we login, to set the cookie.
# body is the encoded arguments to log in.
resp, data = conn.post(login_path, body, {})
cookie = resp.response['set-cookie']
# Headers need to be in a hash.
headers = { "Cookie" => cookie }
# On a get, we don't need a body.
resp, data = conn.get(path, headers)
puts resp.body
Depending on what you are trying to accomplish, check out webrat. I know it is usually used for testing, but it can also hit live sites, and it does a lot of the stuff that your web browser would do for you, like store cookies between requests and follow redirects.
you would have to roll your own cookie support by parsing the meta headers when reading and adding a cookie header when submitting a request if you are using open-uri. Consider using httpclient http://raa.ruby-lang.org/project/httpclient/ or something like mechanize instead http://mechanize.rubyforge.org/ as they have cookie support built in.
There is a RFC 2109 and RFC 2965 cookie jar implementation to be found here for does that want standard compliant cookie handling.
https://github.com/dwaite/cookiejar

Resources