Rails: Proxy Pass? - ruby-on-rails

I've got a web-app that I want to migrate to Rails, which is currently just plain HTML with an Apache proxy to another server, running a custom database/webserver that serves the site's dynamic content.
For the moment, I want to do a staged move, since the content on the proxied server I won't be able to update until I update the static (HTML) server.
So... how would I configure a Rails route to proxy all requests to /dynamic/* to this other server? Or, how would I translate the Apache rule below to Rails?
Apache Proxy Rule:
ProxyPassMatch ^((?i)/dynamic/)(.*)$ http://xxx.xxx.com:8080/$2
ProxyPassReverse /dynamic/ http://xxx.xxx.com:8080/

Rails routes are used to route some urls to one controller and one action.
You can't make a route to a distant URL or proxying like this.
And that wouldn't be a good idea as it would force us to load all rails (activerecord and everything) when it wouldn't be necessary at all. That's the opposite of scalability.
Using an Apache Proxy rule remains the most appropriate here.

Related

Rails APIs and path based load balancer routing

We're breaking our monolithic Rails application in to microservices. Our services are hosted on AWS and are behind ALBs. We cannot use host based routing as we are multi-tenant via subdomain, and it would be an SSL nightmare to maintain the required certs for each tenant/environment/service combination. So we are using path-based API routing with rules on the load balancer. A request looks like this:
Client -> www.example.com/api/:service_name/the_rest_of_the_path -> ALB -> route to rails service by name of :service_name
Because ALB cannot modify the path of a request before it sends it on to the serive, when it reaches the Rails services the path is still /api/:service_name/the_rest_of_the_path . This means in order to route to the proper controllers/actions in this case, we'd need to actually create a rails scope on namespace of /api/:service_name . This would work in theory but it has two drawbacks.
Firstly it means local developers have to deal with ALB/client specific concerns -- the path used for external service/cluster routing for ALB.
The second is that it couples the application to that path. If the load balancer decided the path should be /:service_name/the_rest_of_the_path instead then it would mean changing the application code in conjunction with the load balancer rules to accommodate it. It's not optimal and I'd prefer to avoid it if at all possible.
I thought then perhaps we could introduce a webserver to the mix, in between the load balancer and the application layer. I worked on a proof of concept for this and had it stripping out /api/:service_name before it got to the service -- leaving the Rails app with just "the_rest_of_the_path" which is all it cares about. Great! Perfect! Or so I thought.
It works well enough to route initial requests to, It however falls flat when any sort of redirects or links are used by taking the current path (as Rails sees it) in to consideration.
In the event /api/:service_name is stripped off before it hits the service, any subsequent links or redirects made from the Rails server itself naturally do not include it in there any longer. You may be on www.example.com/api/:service_name/foo/bar but Rails only thinks you're at /foo/bar. When it tries to tack something on to the path for a redirect or link like /foo/bar/baz, it loses the thing that identifies what service to send it to so the route dies at the load balancer.
This has particularly been an issue with Omniauth/Oauth2 flows for us. Omniauth wants to live at /auth/:provider by default. If the request path is actually /api/:service_name/auth/:provider then it won't match and the Oauth flow wont initiate. Further if there is a failure with the Oauth flow, Omniauth will hard redirect to www.example.com/auth/failure -- which of course does not resolve as the LB does not know where to route the request to.
If we provide a path_prefix to Omniauth as /api/:service_name/auth then it wont match when testing locally at /auth and it won't initiate the flow there.
We won't have control over all of the gems we use and where they redirect to so my question is: Is there a proper way of hanging Rails API microservices off a path on a load balancer, and not have to pull teeth to preserve the necessary prefix in all routes and links and redirects? Something that is essentially a global base href that we can set there, but not set locally so that we can continue to develop at localhost:3000/path instead of remembering to use (and coupling with) an LB path like localhost:3000/api/:service_name/path ?

Angular dart bookmarking views

It is my experience that Angular Dart is agnostic to your backend server implementation. it doesn't care if your server is in java, ruby or whatever. Angular dart has the concept of views and has a module that deals with routing between them. these routes also modify the address bar of the browser when it changes views.
I have come across this issue. Though the angular router module will change the address bar, because said route doesn't actually exist as far as the backend server is concerned, and as such will always issue a 404 response.
If such is the case, then I find the ability to route to different pages via angular to be pointless. Might as well I code in a more traditional server oriented fashion to transition between pages, than to sue angular.
Is it that there is something that is missing? Is there a way you can can get your server to resolve to the correct angular page?
You can use usePushState: false then only the (client) local part of the URL is changed.
see https://github.com/angular/angular.dart.tutorial/blob/master/Chapter_06/web/main.dart#L27
This part after the hash is never sent to the server.
This might cause some additional work for SEO.
http://example.com/index.html#someRoutePath/anotherRoutePath
or you can configure your server in a way that each request is handled independent of the path in the request and use the route package server side too.
see also https://stackoverflow.com/a/17909743/217408
You can configure your backend server to point all routes to the same file (using some kind of wildcard route which all decent servers should support). So app/some/page and app/another/page would both be served app.html. Then on your app startup you could have Angular parse the URL of the page, and manually route to that page.
I have used this approach with a Polymer app (with the Route library) and it works great. It should work similarly for Angular.

Rails 3.2.9 Intercept Asset Requests For Remapping

In Ruby on Rails 3.2.9 is there a way to intercept asset requests and remap the URL that is being requested.
For example, for a request for /assets/javascripts/app.js I would like to intercept the request and strip out javascripts/. I've tried in the application.rb
config.asset_path = proc { |path|
path.slice! 'javascripts/'
}
I'm not aware of any way to intercept requests to assets and I highly doubt there's one or there'll ever be one at all.
Simply because it wouldn't work with compiled assets. What if the assets are on another server with a completely different software stack? For example if someone chooses to host the assets on Amazon S3, how could requests be intercepted at all?
If you really need this feature and you are self-hosting your assets the best way mght be configuring your web server to redirect the request.
Have a look at RewriteEngine for Apache or HttpRewriteModule for nginx. They provide mechanisms for URL rewriting. (I guess most production-grade web servers do have something simlar, too)

How do I serve multiple rails apps on one domain (and sub)?

This is kind of weird but I'd like to serve multiple websites on the same domain. If possible, we want to avoid subdomains to keep urls simple for our users - no need for them to know it's two separate apps. This is purely for keeping the code bases separate. Any ideas?
For example:
Rails App 1 (Refinery CMS) serves:
http://example.com/
http://example.com/about
http://example.com/pricing
Rails App 2 (our real App) serves:
http://example.com/account
http://example.com/store
http://example.com/listings
We use ruby 1.9.2, ruby on rails, refinery cms, apache and passenger.
If you're using Passenger, check out the Deploying to a sub URI portion of the manual - it's quite simple to set up an app on a sub-URI. You may need to set config.action_controller.relative_url_root in your app configuration, as well.
Edit: I misread the question; not one app per URI, but one app serving some (but not all) endpoints. This is actually moderately easy to do with some basic rewrites, as well.
Deploy your Rails app to, let's say, /railsapp (but without setting relative_url_root). Now, in .htaccess:
RewriteRule ^account/(.*)$ railsapp/account/$1 [L]
This will internally remap /account/* to /railsapp/account/*, so as long as you set up a rewrite per path your Rails app handles, it should work fine.
Subdomains make it easier (thus why most sites have shop.example.com), but you could probably use rewrite rules with name based virtual host routing. How exactly to do that I'm not sure. More of a Apache rewrite question for SuperUser.
A word of warning if you are using SSL you might have issues arise.
You could set it up to first hit one app where you expect most URLs would work and if it 404s you could instruct it to try the other app next, though this will be slower than routing per route but it will work without having to hardcode a route for every page that is created on say, Refinery CMS.
Currently I'm also working on a same kind of CMS. In my case also I need multiple sub domains, like
www.test1.mydomain.com
www.test2.mydomain.com
www.test3.mydomain.com
www.test4.mydomain.com
here is what I did
in rails 3 (if you are on rails3) you can get the sub domain by using request object. (If you are on rails 2.x you can use sub domain_fu plugin)
In my case I have used a before filter to get the sub domain, after that I load the site according to the sub domain
For development use the following public domain "lvh.me"
http://tbaggery.com/2010/03/04/smack-a-ho-st.html
this was very useful for me http://railscasts.com/episodes/221-subdomains-in-rails-3
let users have their domains forwarded to your subdomain (with masking)
ex : www.clientdomain.com --> http://client.mydomain.com
hope this helps
cheers
sameera

A route to serve static assets (like .jpgs, etc?)

I've worked my way through a number of interesting routing problems - turning a request URL into a hash, etc., but just out of curiosity, is there a way to tell the routing system that you want anything that comes under a certain url subpath to be served literally - without going through a controller?
For instance, if I have /home/me/public_html/rails_proj/images/foo.jpg, and .../rails_proj/images/other/bar.jpg, can I insert a route that says "anything under images should just be served as an object of the default mime type?"
Might be interesting.
If you put the "images" directory into the "public" folder of the Rails app (for example: /public/images/) then you shouldn't have any problems with MIME types unless your web server is configured wrongly.
According to your examples, you want the images dir in the root of the app. I don't think there is a way though Rails to make those images visible, but if you really wanted to you could use mod_rewrite to make it work. Once again, it would be up to the web server to make sure the images had the correct MIME type.
Things that are served out of the public directory will not go through Rails - they'll just be handled by your server (probably apache). The only reason why you would need to serve images through the rails system is if you wanted some kind of control on who could access them. Just put everything else in public and access ala:
siteurl.whatever/images/*.jpg
I typically use nginx as a frontend and Apache/Passenger as a backend. Ngingx proxies all Rails requests to Apache but handles all static content itself. Check out the examples on the English nginx wiki. Here is a small excerpt for nginx config:
server {
listen 80;
server_name www.domain.com;
location ~* \.(jpg|jpeg|gif|png|ico|css|bmp|js)$ {
root /path/to/static/assets/dir;
}
location / {
proxy_pass http://127.0.0.1:81;
}
}
So have apache listen on port 81 to handle Rails requests proxied by nginx and let nginx deliver static content. Not only is nginx supposedly faster than Apache at delivering static content, but this also offloads your Rails application for every image, stylesheet, javascript or whatever other static content.
I think the simplest way solve this problem is just using image_path helper method, which provide you the path for the image you want to display in the view.
For example, if you want to refer a logo.png under the /assets/images/logo.png, you can just use image_path('logo.png').
Caveat: if your request URL matches a static resource WEBrick, mongrel or whatever will happily serve it. At any cost, you don't want this in production: if your traffic is high enough your app will be brought to its knees just because its mongrels will be busy serving static content.
So make sure that your web server is properly configured for all kinds of static content as the previous commenters have pointed out.

Resources