Rack::Builder and Rack::TryStatic doesn't work - ruby

I have a Middleman app which I am serving using Rack::TryStatic.
Here is the config.ru.
use Rack::TryStatic,
root: 'build',
urls: %w[/],
try: ['.html', 'index.html', '/index.html']
run lambda{ |env|
four_oh_four_page = File.expand_path("../build/404.html", __FILE__)
[ 404, { 'Content-Type' => 'text/html'}, [ File.read(four_oh_four_page) ]]
}
My understanding is that when you use a config.ru with run, map or use
methods, they are converted to a Rack::Builder object.
I've tried wrapping this config in a Rack::Builder object like this:
app = Rack::Builder.new do
use Rack::TryStatic,
root: 'build',
urls: %w[/],
try: ['.html', 'index.html', '/index.html']
run lambda{ |env|
four_oh_four_page = File.expand_path("../build/404.html", __FILE__)
[ 404, { 'Content-Type' => 'text/html'}, [ File.read(four_oh_four_page) ]]
}
end
run app
When I do this, I get the 404 page for all requests.
Why doesn't this work?

It looks like there is a bug in the current released version of TryStatic that has been fixed in master, where the try array is being lost when used in way that causes the middleware to be reinitialized.
You can avoid this by making sure the app only get s initialized once by using to_app:
run app.to_app
or equivalently use app instead of new:
app = Rack::Builder.app do
# ...

Related

Rack Middleware in Rack Middleware?

I am trying to build a rack middleware GEM that uses rack middleware itself (RackWired).
I have an existing application, whose config.ru uses Rack::Builder. In that block (Rack::Builder), I would like to specify my middleware, and when it is called to use a third party middleware (rack-cors) inside my own to do some stuff. Confusing I know.
The problem is that Rack::Builder's context is in config.ru and my middleware (RackWired) is thus unable to access it to "use" the third party middleware (rack-cors).
The intent of my effort is here
Is there a way to use middleware within middleware?
Thank you
Right, I'm not entirely sure what you're trying to do. But you can do this
class CorsWired
def initialize(app)
#app = app
end
def call(env)
cors = Rack::Cors.new(#app, {}) do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :put, :options, :delete], :credentials => false
end
end
cors.call(env)
end
end
Your config.ru should have use CorsWired though, not use CorsWired.new
This is I think what you were asking but I think you're missing the point of middleware. You should just change your config.ru to use rack-cors before/after your middleware depending on what you want to do.
require 'rack'
require 'rack/cors'
require './cors_wired'
app = Rack::Builder.new do
use Rack::Cors do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :put, :options, :delete], :credentials => false
end
end
use CorsWired
run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
end
run app

How to perform an HTTP redirect (302) using Rack?

I am messing around with my first Rack application (this is just for experimentation).
When a call comes in I do something like this:
class Application
def call(env)
# Totally ignore favicons for the time being
if env['PATH_INFO'] == '/favicon.ico'
return [ 404, {'Content-Type' => 'text/html'}, [] ]
elsif env['PATH_INFO'] == '/'
return [ 302, {'http-equiv' => "refresh", 'content' => "2;url=http://google.com"}, [] ]
end
...
I know this is horrible... but, again, this isn't a serious project.
I'm trying to figure out how to do the redirect. What I have does not work. Basically, when you hit / on my site I want to redirect that require to google.com.
Here is working app with re-direct to Google
require 'rack'
require 'rack/server'
class HelloWorld
def response
[ 302, {'Location' =>"http://google.com"}, [] ]
end
end
class HelloWorldApp
def self.call(env)
HelloWorld.new.response
end
end
Rack::Server.start :app => HelloWorldApp

Ruby Rack Heroku : Unable to access multiple URLs

I have followed the Heroku guide on deploying static files using Ruby Rack (https://devcenter.heroku.com/articles/static-sites-ruby),
However I am unable to access any HTML file in \public apart from index.html (every URL resolves to the home page)
The config.ru file :
use Rack::Static,
:urls => ["/bootstrap", "/css", "/fonts", "/images", "/js", "/font-awesome"],
:root => "public"
run lambda { |env|
[
200,
{
'Content-Type' => 'text/html',
'Cache-Control' => 'public, max-age=86400'
},
File.open('public/index.html', File::RDONLY)
]
}
I have checked the following posts :
Ruby Rack Heroku: Serving Static Files
How to setup URLs for static site with Ruby Rack on Heroku
Still I am unable to go beyond the index.html page.
While I change the config.ru as shown below ... I get application error
use Rack::Static,
:urls => ["/bootstrap", "/css", "/fonts", "/images", "/js", "/font-awesome"],
:root => "public"
map "/" do
run lambda { |env|
[
200,
{
'Content-Type' => 'text/html',
'Cache-Control' => 'public, max-age=86400'
},
File.open('public/index.html', File::RDONLY)
]
}
end
map "/about" do
run lambda { |env|
[
200,
{
'Content-Type' => 'text/html',
'Cache-Control' => 'public, max-age=86400'
},
File.open('public/about/index.html', File::RDONLY)
]
}
end
Is there a way to map multiple URLs? I have 5-6 pages in my index.html, but unable to access.
Thanks for your help.
You could create a simple controller that just renders these pages. May be easier than trying to bypass the MVC. Let me know if you need help with this :)
Update -----
Example
Controller:
class PublicController < ApplicationController
def index
render "public/index"
end
def about
render "public/about/index"
end
end
Then place your html files in the views folder in the sub-directories specified in the controller.
Also don't forget to add these methods to your routes.rb file in the config directory.

Set Charset to UTF-8 on html pages for Middleman blog site

I have a Middleman blog hosted on Heroku (http://tomgillard.herokuapp.com) and have been trying to optimise it based on google's PageSpeed recommendations. One recommendation is that I provide a character set on the site's HTML pages.
HTML pages contain the html5 <meta charset="utf-8"> in the <head> but this doesn't seem to be enough os I thought I could set it server side.
Here is my config.ru
require 'rack/contrib'
# Modified version of TryStatic, from rack-contrib
# https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/try_static.rb
# Serve static files under a `build` directory:
# - `/` will try to serve your `build/index.html` file
# - `/foo` will try to serve `build/foo` or `build/foo.html` in that order
# - missing files will try to serve build/404.html or a tiny default 404 page
module Rack
class TryStatic
def initialize(app, options)
#app = app
#try = ['', *options.delete(:try)]
#static = ::Rack::Static.new(lambda { [404, {}, []] }, options)
end
def call(env)
orig_path = env['PATH_INFO']
found = nil
#try.each do |path|
resp = #static.call(env.merge!({'PATH_INFO' => orig_path + path}))
break if 404 != resp[0] && found = resp
end
found or #app.call(env.merge!('PATH_INFO' => orig_path))
end
end
end
# Serve GZip files to browsers that support them
use Rack::Deflater
# Custom HTTP Headers
use Rack::ResponseHeaders do |headers|
headers['Charset'] = 'UTF-8'
end
#Custom Cache Expiry
use Rack::StaticCache, :urls => ["/img", "/css", "/js", "/fonts"], :root => "build"
# Attempt to serve static HTML file
use Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html', 'index.html', '/index.html']
# Serve 404 messages:
run lambda{ |env|
not_found_page = File.expand_path("../build/404.html", __FILE__)
if File.exist?(not_found_page)
[ 404, { 'Content-Type' => 'text/html', 'Charset' => 'UTF-8' }, [File.read(not_found_page)] ]
else
[ 404, { 'Content-Type' => 'text/html', 'Charset' => 'UTF-8' }, ['404 - page not found'] ]
end
}
I thought I could use Rack::ResponseHeaders from rack-contrib but I don't think I'm using it correctly;
# Custom HTTP Headers
use Rack::ResponseHeaders do |headers|
headers['Charset'] = 'UTF-8'
end
Like I said, I've searched high and low; consulted docs (Rack, heroku), SO questions, blog posts, github, you name it.
Any help with this is much appreciated.
Cheers,
Tom
I had a similar problem and wanted to optimize my site. You just need to manually set your Content-Type header.
Here's my complete config.ru which achieves a 98/100 on Google PageSpeed (it complains about not embedding my css in the page):
# encoding: utf-8
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
require 'rack/contrib'
require File.expand_path("../rack_try_static", __FILE__)
use Rack::ResponseHeaders do |headers|
headers['Content-Type'] = 'text/html; charset=utf-8' if headers['Content-Type'] == 'text/html'
end
use Rack::Deflater
use Rack::StaticCache, urls: ["/images", "/stylesheets", "/javascripts", "/fonts"], root: "build"
use ::Rack::TryStatic,
root: "build",
urls: ["/"],
try: [".html", "index.html", "/index.html"]
run lambda { [404, {"Content-Type" => "text/plain"}, ["File not found!"]] }
You'll also need to add rack-contrib to your Gemfile for Rack::StaticCache:
gem 'rack-contrib'
You'll also want my rack_try_static.
Edit: Note the current implementation of Rack::StaticCache removes Last-Modified and Etag headers and will break on assets ending with a - followed by a number. I have PRs open for both of these (83 and 84).
This no longer applies as I have moved my blog from heroku to github pages. Thanks for looking.

Serve files using Rack TryStatic directly?

I'm using Middleman to create a static site.
Middleman generates static html files into ./build directory.
Here's the config I'm currently using:
require 'rubygems'
require 'middleman'
require 'rack/contrib/try_static'
use Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html']
run Middleman::Application.server
So Middleman is serving the static files right now. How can I make Rack::TryStatic handle the requests directly?
I tried to make it something like
run Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html']
But it doesn't work is run only accepts 1 argument. And Rack::TryStatic requires 2 arguments, app and options to initialize, and I don't have any app.
How can I do this?
(And if it matters, I'm deploying to Heroku)
As you’ve noticed, a Rack middleware component such as Rack::TryStatic needs another app to pass requests onto. You could create a simple one to use that for example just returned a 404 response, such as:
app = lambda {|env| [404, {'Content-type' => 'text/plain'}, ['Not found']
run Rack::TryStatic.new app, :root => "build", :urls => %w[/], :try => ['.html']
or equivalently:
use Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html']
run lambda {|env| [404, {'Content-type' => 'text/plain'}, ['Not found']]}
If you have your own 404 file, you could use rack-contrib’s Rack::NotFound instead of a custom end point of your own:
use Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html']
run Rack::NotFound.new('path/to/your/404.html')
If you weren’t using the :try array of file extensions to try and serve, you could use Rack::File directly. Internally, Rack::TryStatic uses Rack::Static, which in turn uses Rack::File. Unlike TryStatic and Static, Rack::File is a Rack application in its own right, and so doesn’t need a separate app to pass requests to. Your config.ru would then simply be:
run Rack::File.new './build'
although this wouldn’t allow for “bare” requests to be served with the corresponding .html file — all requests would need to specify the whole file name.

Resources