diff options
author | Thomas Klemm <github@tklemm.eu> | 2012-08-31 21:24:55 +0200 |
---|---|---|
committer | James Tucker <raggi@google.com> | 2013-01-04 09:30:59 -0500 |
commit | fb5785294241a07deab95dea565267098fe186d2 (patch) | |
tree | fb7d9df71373cedcb169c8ddc64393f47dff6f91 | |
parent | ceeccb110df7167bb0dfe3fa4e0a12f681211ce5 (diff) | |
download | rack-fb5785294241a07deab95dea565267098fe186d2.tar.gz |
Allow Rack::Static to assign HTTP Headers based on rules
-rw-r--r-- | lib/rack/static.rb | 96 | ||||
-rw-r--r-- | test/cgi/assets/folder/test.js | 1 | ||||
-rw-r--r-- | test/cgi/assets/fonts/font.eot | 1 | ||||
-rw-r--r-- | test/cgi/assets/images/image.png | 1 | ||||
-rw-r--r-- | test/cgi/assets/index.html | 1 | ||||
-rw-r--r-- | test/cgi/assets/javascripts/app.js | 1 | ||||
-rw-r--r-- | test/cgi/assets/stylesheets/app.css | 1 | ||||
-rw-r--r-- | test/spec_static.rb | 56 |
8 files changed, 133 insertions, 25 deletions
diff --git a/lib/rack/static.rb b/lib/rack/static.rb index 28754360..d42e0364 100644 --- a/lib/rack/static.rb +++ b/lib/rack/static.rb @@ -28,19 +28,58 @@ module Rack # use Rack::Static, :urls => [""], :root => 'public', :index => # 'index.html' # - # Set a fixed Cache-Control header for all served files: + # Set custom HTTP Headers for based on rules: # - # use Rack::Static, :root => 'public', :cache_control => 'public' + # use Rack::Static, :root => 'public', + # :header_rules => { + # rule => { header_field => content }, + # another_rule => { header_field => content } + # } # - # Set custom HTTP Headers for all served files: + # These rules for generating HTTP Headers are shipped along: + # 1) Global + # :global => Matches every file # - # use Rack::Static, :root => 'public', :headers => - # {'Cache-Control' => 'public, max-age=31536000', - # 'Access-Control-Allow-Origin' => '*'} + # Ex.: + # :header_rules => { + # :global => {'Cache-Control' => 'public, max-age=123'} + # } # - # Note: If both :headers => {'Cache-Control' => 'public, max-age=42'} - # and :cache_control => 'public, max-age=38' are being provided - # the :headers setting takes precedence + # 2) Folders + # '/folder' => Matches all files in a certain folder + # '/folder/subfolder' => ... + # Note: Provide the folder as a string, + # with or without the starting slash + # + # 3) File Extensions + # ['css', 'js'] => Will match all files ending in .css or .js + # %w(css js) => ... + # Note: Provide the file extensions in an array, + # use any ruby syntax you like to set that array up + # + # 4) Regular Expressions / Regexp + # %r{\.(?:css|js)\z} => will match all files ending in .css or .js + # /\.(?:eot|ttf|otf|woff|svg)\z/ => will match all files ending + # in the most common web font formats + # + # 5) Shortcuts + # There is currently only one shortcut defined. + # :fonts => will match all files ending in eot, ttf, otf, woff, svg + # using the Regexp stated right above + # + # Example use: + # + # use Rack::Static, :root => 'public', + # :header_rules => { + # # Cache all static files in http caches as well as on the client + # :global => { 'Cache-Control' => 'public, max-age=31536000' }, + # # Provide Web Fonts with Access-Control-Headers + # :fonts => { 'Access-Control-Allow-Origin' => '*' } + # } + # + # Note: The rules will be applied in the order they are provided, + # thus more special rules further down below can override + # general global HTTP header settings # class Static @@ -50,11 +89,12 @@ module Rack @urls = options[:urls] || ["/favicon.ico"] @index = options[:index] root = options[:root] || Dir.pwd - headers = options[:headers] || {} - # Allow for legacy :cache_control option - # while prioritizing :headers => {'Cache-Control' => ''} settings - headers['Cache-Control'] ||= options[:cache_control] if options[:cache_control] - @file_server = Rack::File.new(root, headers) + @headers = {} + @header_rules = options[:header_rules] || {} + # Allow for legacy :cache_control option while prioritizing global header_rules setting + @header_rules[:global] ||= {} + @header_rules[:global]['Cache-Control'] ||= options[:cache_control] if options[:cache_control] + @file_server = Rack::File.new(root, @headers) end def overwrite_file_path(path) @@ -74,11 +114,39 @@ module Rack if can_serve(path) env["PATH_INFO"] = (path =~ /\/$/ ? path + @index : @urls[path]) if overwrite_file_path(path) + @path = env["PATH_INFO"] + set_headers @file_server.call(env) else @app.call(env) end end + # Convert header rules to headers + def set_headers + @header_rules.each do |rule, headers| + case rule + when :global # Global + set_header(headers) + when :fonts # Fonts Shortcut + set_header(headers) if @path.match(%r{\.(?:ttf|otf|eot|woff|svg)\z}) + when String # Folder + path = ::Rack::Utils.unescape(@path) + set_header(headers) if + (path.start_with?(rule) || path.start_with?('/' + rule)) + when Array # Extension/Extensions + extensions = rule.join('|') + set_header(headers) if @path.match(%r{\.(#{extensions})\z}) + when Regexp # Flexible Regexp + set_header(headers) if @path.match(rule) + else + end + end + end + + def set_header(headers) + headers.each { |field, content| @headers[field] = content } + end + end end diff --git a/test/cgi/assets/folder/test.js b/test/cgi/assets/folder/test.js new file mode 100644 index 00000000..6874e45a --- /dev/null +++ b/test/cgi/assets/folder/test.js @@ -0,0 +1 @@ +### TestFile ### diff --git a/test/cgi/assets/fonts/font.eot b/test/cgi/assets/fonts/font.eot new file mode 100644 index 00000000..6874e45a --- /dev/null +++ b/test/cgi/assets/fonts/font.eot @@ -0,0 +1 @@ +### TestFile ### diff --git a/test/cgi/assets/images/image.png b/test/cgi/assets/images/image.png new file mode 100644 index 00000000..6874e45a --- /dev/null +++ b/test/cgi/assets/images/image.png @@ -0,0 +1 @@ +### TestFile ### diff --git a/test/cgi/assets/index.html b/test/cgi/assets/index.html new file mode 100644 index 00000000..6874e45a --- /dev/null +++ b/test/cgi/assets/index.html @@ -0,0 +1 @@ +### TestFile ### diff --git a/test/cgi/assets/javascripts/app.js b/test/cgi/assets/javascripts/app.js new file mode 100644 index 00000000..6874e45a --- /dev/null +++ b/test/cgi/assets/javascripts/app.js @@ -0,0 +1 @@ +### TestFile ### diff --git a/test/cgi/assets/stylesheets/app.css b/test/cgi/assets/stylesheets/app.css new file mode 100644 index 00000000..6874e45a --- /dev/null +++ b/test/cgi/assets/stylesheets/app.css @@ -0,0 +1 @@ +### TestFile ### diff --git a/test/spec_static.rb b/test/spec_static.rb index 5e722e6a..07572309 100644 --- a/test/spec_static.rb +++ b/test/spec_static.rb @@ -70,7 +70,7 @@ describe Rack::Static do res.body.should == "Hello World" end - it "supports serving fixed cache-control" do + it "supports serving fixed cache-control (legacy option)" do opts = OPTIONS.merge(:cache_control => 'public') request = Rack::MockRequest.new(static(DummyApp.new, opts)) res = request.get("/cgi/test") @@ -78,23 +78,57 @@ describe Rack::Static do res.headers['Cache-Control'].should == 'public' end - it "supports serving custom http headers" do - opts = OPTIONS.merge(:headers => {'Cache-Control' => 'public, max-age=42', - 'Access-Control-Allow-Origin' => '*'}) + it "allows :header_rules to take priority over fixed cache-control (legacy option)" do + opts = OPTIONS.merge( + :cache_control => 'public, max-age=38', + :header_rules => { + :global => {'Cache-Control' => 'public, max-age=42'} + }) + request = Rack::MockRequest.new(static(DummyApp.new, opts)) res = request.get("/cgi/test") res.should.be.ok res.headers['Cache-Control'].should == 'public, max-age=42' - res.headers['Access-Control-Allow-Origin'].should == '*' end - it "allows headers hash to take priority over fixed cache-control" do - opts = OPTIONS.merge(:cache_control => 'public, max-age=38', - :headers => {'Cache-Control' => 'public, max-age=42'}) + it "support HTTP header rules" do + opts = OPTIONS.merge(:header_rules => { + :global => {'Cache-Control' => 'public, max-age=100'}, + :fonts => {'Cache-Control' => 'public, max-age=200'}, + %w(png jpg) => {'Cache-Control' => 'public, max-age=300'}, + 'cgi/assets/folder/' => {'Cache-Control' => 'public, max-age=400'}, + '/cgi/assets/javascripts' => {'Cache-Control' => 'public, max-age=500'}, + /\.(css|erb)\z/ => {'Cache-Control' => 'public, max-age=600'}, + }) request = Rack::MockRequest.new(static(DummyApp.new, opts)) - res = request.get("/cgi/test") + + # Global Headers via :global shortcut + res = request.get('/cgi/assets/index.html') res.should.be.ok - res.headers['Cache-Control'].should == 'public, max-age=42' - end + res.headers['Cache-Control'].should == 'public, max-age=100' + + # Headers for web fonts via :fonts shortcut + res = request.get('/cgi/assets/fonts/font.eot') + res.should.be.ok + res.headers['Cache-Control'].should == 'public, max-age=200' + # Headers for file extensions via array + res = request.get('/cgi/assets/images/image.png') + res.should.be.ok + res.headers['Cache-Control'].should == 'public, max-age=300' + + # Headers for files in folder via string + res = request.get('/cgi/assets/folder/test.js') + res.should.be.ok + res.headers['Cache-Control'].should == 'public, max-age=400' + + res = request.get('/cgi/assets/javascripts/app.js') + res.should.be.ok + res.headers['Cache-Control'].should == 'public, max-age=500' + + # Flexible Headers via Regexp + res = request.get('/cgi/assets/stylesheets/app.css') + res.should.be.ok + res.headers['Cache-Control'].should == 'public, max-age=600' + end end |