conditional configuration of config.action_controller.asset_host - ruby-on-rails

I have a Rails app which uses CloudFront to speed up asset delivery, all is nice and smooth, have this added under production.rb
config.action_controller.asset_host = "http://cdn.mydomain.com/"
However, a bunch of clients on a restricted network use to go through a Varnish cache to hit my app, and cannot resolve the CDN domain. Varnish adds this header:
request.env["HTTP_X_VARNISH"]
What I'd like to do is basically to only use the CDN when this header is not set (clients not coming through varnish).
So basically something like
config.action_controller.asset_host = "http://cdn.mydomain.com/" unless request.env["HTTP_X_VARNISH"]
How could this be accomplished?
TIA

asset_host can be a Proc.
config.action_controller.asset_host = ->(source, request) do
if request.env["HTTP_X_VARNISH"]
"#{request.protocol}#{request.host_with_port}"
else
"http://cdn.mydomain.com/"
end
end

Related

Request origin not allowed: http://localhost:3001 when using Rails5 and ActionCable

Having server issues with an app in Rails 5.0.0.beta2 trying to use ActionCable.
Using localhost:3000 works fine, as that is what most of ActionCable defaults to. But if I try to run the rails server on port 3001, it gives me Request origin not allowed: http://localhost:3001
The ActionCable docs mention using something like ActionCable.server.config.allowed_request_origins = ['http://localhost:3001'] which does work for me if I put it in config.ru
But that seems like a really weird place to put it. I feel like it should be able to go in an initializer file, or my development.rb environment config file.
To further prove my point that it should be allowed to go in there, the setting ActionCable.server.config.disable_request_forgery_protection = true works to ignore request origin, even when I include it in development.rb.
Why would ActionCable.server.config.disable_request_forgery_protection work in development.rb, but ActionCable.server.config.allowed_request_origins doesn't (but does work in config.ru)?
Not a pressing issue, since I have several options as a work around. I just want to know if I'm missing something obvious about how I imagine this should be working.
You can put
Rails.application.config.action_cable.allowed_request_origins = ['http://localhost:3001'] in your development.rb
See https://github.com/rails/rails/tree/master/actioncable#allowed-request-origins for more informations
For my flutter app, request origin was nil. So, needed to add nil in the list.
I have added this code in config/environments/development.rb, and it works!
config.action_cable.allowed_request_origins = [/http:\/\/*/, /https:\/\/*/, /file:\/\/*/, 'file://', nil]
From this answer, you can also add the following code to config/environments/development.rb to allow requests from both http and https:
Rails.application.configure do
# ...
config.action_cable.allowed_request_origins = [%r{https?://\S+}]
end
config.action_cable.allowed_request_origins accepts an array of strings or regular expressions as the documentation states:
Action Cable will only accept requests from specified origins, which
are passed to the server config as an array. The origins can be
instances of strings or regular expressions, against which a check for
the match will be performed.
The regex listed below will match both http and https urls from any domain so be careful when using them. It is just a matter of preference which one to use.
[%r{https?://\S+}] # Taken from this answer
[%r{http[s]?://\S+}]
[%r{http://*}, %r{https://*}]
[/http:\/\/*/, /https:\/\/*/]

Issue with using CDN and https: (Rails)

In my Rails application, i have used CDN. I have configured the cdn by adding cdn url to
config.action_controller.asset_host = "http://cdn.mydomain.com"
in production.rb file.
Now i am trying to have https:// for certain pages like Sign In and Sign Up
But as the assets are served from CDN, the https conflicts with the cdn path.
My solution to this to make the sign in and sign up pages not to use the cdn assets and should point
as local assets.
is my solution correct? if so how do i restrict certain layout files from using CDN asset path?
I would look at this response: Configure dynamic assets_host in Rails 3
What I think you would want to do is change asset_host to be dynamic based on whether your page is served over https or not. Something like:
config.action_controller.asset_host = Proc.new { |source, request|
"#{request.ssl? ? '/assets' : 'http://cdn.mydomain.com'}"
}
My syntax may be a little off as I'm typing it up on the fly but it should be close to what you need.
NOTE : The code request.try(:ssl?) always returns false even when i run the https version.
I am working on finding the solution, will post it once i find it.
Found the solution
config.action_controller.asset_host = Proc.new do |*args|
source, request = args
if request.try(:ssl?)
'https://mydomain.com'
else
'http://cdn.mydomain.com'
end
end

Using Carrierwave with Cloudfront

I'm using CarrierWave for images and Amazon Cloudfront as a CDN (without S3).
The issue is that something like: #user.image_url returns the non CDN URL, even though i've configured my assets accordingly:
# /config/environments/production.rb
config.action_controller.asset_host = Proc.new { |source, request|
if ['jpg','jpeg','png','gif','bmp'].include?(source.split('.').last)
unless request.ssl?
"http://cdn.domain.com"
else
"https://ge95v2x8h9t3.cloudfront.net"
end
end
}
How to make CarrierWave use my asset_host proc just like other assets?
You can configure carrierwave to use a custom asset_host (config.fog_host... documented in the readme). Although not documented, you can also use a Proc - or anything responding to :call - to determine the string at runtime:
https://github.com/jnicklas/carrierwave/blob/master/lib/carrierwave/storage/fog.rb#L107
I'm not sure of a way to just point config.fog_host directly to Rails' config.asset_host, but I'm sure there must be a way to get a reference to it - even if you have to use a non-public interface. Though, I don't know how helpful that would be during development... you likely want assets served from localhost and uploads served from Cloudfront.

Switch asset host for controller

Trying to figure out a way to change up the asset host when accessed by a certain controller.
The controller is to be strictly accessed by the https protocol, so I need the asset host to be switched over to using https. At the moment the asset host is set to a CNAME subdomain that is linked to the S3 and there is no SSL cert associated to it. What I'm trying to achieve is replace the current asset host with the https Amazon S3 URL. The only assets I'm worried about are the CSS and JS includes.
I was thinking of using a helper to strip the host from the stylesheet_link_tag and javascript_include_tag and replace them with the https Amazon S3 url. Seems a bit hackish to me though.
Or perhaps there is a way to changed asset hosts if request.ssl? is true?
I'm using Rails 3.2.x.
Figure out a solution for my case.
Ended up using a Proc on config.action_controller.action_host in my Production environment file to handle a logic on request.ssl? and respond accordingly. Here is the code
config.action_controller.asset_host = Proc.new { |source, request = nil, *_|
request && request.ssl? ? 'https://s3.amazonaws.com/my_bucket' : 'http://s3.my-domain.com'
}
'request' is set to nil to accomodate the cases where asset_host is called in asset files (CSS and JS if you are using the asset helper tags). Since request doesn't exist and if request isn't assigned in the args, then the error will be thrown when assets are compiled (as shown below).
This asset host cannot be computed without a request in scope. Remove the second argument to your asset_host Proc if you do not need the request, or make it optional.
The *_ is present due to a bug with option arguments in Proc http://bugs.ruby-lang.org/issues/5694

Rails: Images on one server, CSS and Javascript on another

I am working on a rails app that has a bunch (hundreds) of images that are hosted on an S3 server. To have helpers like image_tag point here I had to add this to by config/environments/development.rb test.rb and production.rb:
config.action_controller.asset_host = "http://mybucket.s3.amazonaws.com"
However, this also means that it looks there for CSS and Javascript. This is a huge pain because each time I change the CSS I have to re-upload it to Amazon.
So.. Is there an easy way I can make my app look to Amazon for images, but locally for CSS/Javascript?
(I'm using Rails 3.0)
You can pass a Proc object to config.action_controller.asset_host and have it determine the result programmatically at runtime.
config.action_controller.asset_host = Proc.new do |source|
case source
when /^\/(images|videos|audios)/
"http://mybucket.s3.amazonaws.com"
else
"http://mydomain.com"
end
end
but left as it is, this would give you http://mybucket.s3.amazonaws.com/images/whatever.png when you use image_tag :whatever.
If you want to modify the path as well, you can do something very similar with config.action_controller.asset_path
config.action_controller.asset_path = Proc.new do |path|
path.sub /^\/(images|videos|audios)/, ""
end
which would give you http://mybucket.s3.amazonaws.com/whatever.png combined with the former.
There's nothing stopping you from passing full url to image_tag: image_tag("#{IMAGE_ROOT}/icon.png").
But to me moving static images (icons, backgrounds, etc) to S3 and leaving stylesheets/js files on rails sounds kinda inconsistent. You could either move them all to S3 or setup Apache for caching (if you're afraid users pulling big images will create too much overhead for Rails).
BTW, you don't have to put config.action_controller... into config files for all three environments: placing that line just in config/environment.rb will have the same effect.

Resources