class Cuba
Secure HTTP Headers¶ ↑
This plugin will automatically apply several headers that are related to security. This includes:
- HTTP Strict Transport Security (HSTS) [2]. - X-Frame-Options [3]. - X-XSS-Protection [4]. - X-Content-Type-Options [5]. - X-Download-Options [6]. - X-Permitted-Cross-Domain-Policies [7].
References¶ ↑
[1]: github.com/twitter/secureheaders [2]: tools.ietf.org/html/rfc6797 [3]: tools.ietf.org/html/draft-ietf-websec-x-frame-options-02 [4]: msdn.microsoft.com/en-us/library/dd565647(v=vs.85).aspx [5]: msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx [6]: msdn.microsoft.com/en-us/library/ie/jj542450(v=vs.85).aspx [7]: www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html
Constants
- DEFAULT
- EMPTY
- REGEXES
- SEGMENT
- SLASH
Attributes
Public Class Methods
# File lib/cuba.rb, line 89 def self.app @app ||= Rack::Builder.new end
# File lib/cuba.rb, line 105 def self.call(env) prototype.call(env) end
# File lib/cuba.rb, line 120 def self.deepclone(obj) Marshal.load(Marshal.dump(obj)) end
# File lib/cuba.rb, line 97 def self.define(&block) app.run new(&block) end
# File lib/cuba.rb, line 124 def self.inherited(child) child.settings.replace(deepclone(settings)) end
# File lib/cuba.rb, line 133 def initialize(&blk) @blk = blk @captures = [] end
# File lib/cuba.rb, line 109 def self.plugin(mixin) include mixin extend mixin::ClassMethods if defined?(mixin::ClassMethods) mixin.setup(self) if mixin.respond_to?(:setup) end
# File lib/cuba.rb, line 101 def self.prototype @prototype ||= app.to_app end
# File lib/cuba.rb, line 84 def self.reset! @app = nil @prototype = nil end
# File lib/cuba.rb, line 116 def self.settings @settings ||= {} end
# File lib/cuba.rb, line 93 def self.use(middleware, *args, &block) app.use(middleware, *args, &block) end
Public Instance Methods
If you want to match against the HTTP_ACCEPT value.
@example
# HTTP_ACCEPT=application/xml on accept("application/xml") do # automatically set to application/xml. res.write res["Content-Type"] end
# File lib/cuba.rb, line 313 def accept(mimetype) lambda do accept = String(env["HTTP_ACCEPT"]).split(",") if accept.any? { |s| s.strip == mimetype } res[Rack::CONTENT_TYPE] = mimetype end end end
# File lib/cuba.rb, line 142 def call(env) dup.call!(env) end
# File lib/cuba.rb, line 146 def call!(env) @env = env @req = settings[:req].new(env) @res = settings[:res].new(settings[:default_headers].dup) # This `catch` statement will either receive a # rack response tuple via a `halt`, or will # fall back to issuing a 404. # # When it `catch`es a throw, the return value # of this whole `call!` method will be the # rack response tuple, which is exactly what we want. catch(:halt) do instance_eval(&@blk) not_found res.finish end end
Syntactic sugar for providing catch-all matches.
@example
on default do res.write "404" end
# File lib/cuba.rb, line 329 def default true end
# File lib/cuba.rb, line 357 def delete; req.delete? end
A matcher for files with a certain extension.
@example
# PATH_INFO=/style/app.css on "style", extension("css") do |file| res.write file # writes app end
# File lib/cuba.rb, line 271 def extension(ext = "\\w+") lambda { consume("([^\\/]+?)\.#{ext}\\z") } end
Syntatic sugar for providing HTTP Verb matching.
@example
on get, "signup" do end on post, "signup" do end
# File lib/cuba.rb, line 353 def get; req.get? end
# File lib/cuba.rb, line 381 def halt(response) throw :halt, response end
# File lib/cuba.rb, line 358 def head; req.head? end
Useful for matching against the request host (i.e. HTTP_HOST).
@example
on host("account1.example.com"), "api" do res.write "You have reached the API of account1." end
# File lib/cuba.rb, line 301 def host(hostname) hostname === req.host end
# File lib/cuba.rb, line 360 def link; req.link? end
# File lib/cuba.rb, line 253 def match(matcher, segment = SEGMENT) case matcher when String then consume(matcher.gsub(/:\w+/, segment)) when Regexp then consume(matcher) when Symbol then consume(segment) when Proc then matcher.call else matcher end end
# File lib/cuba.rb, line 428 def not_found res.status = 404 end
The heart of the path / verb / any condition matching.
@example
on get do res.write "GET" end on get, "signup" do res.write "Signup" end on "user/:id" do |uid| res.write "User: #{uid}" end on "styles", extension("css") do |file| res.write render("styles/#{file}.sass") end
# File lib/cuba.rb, line 192 def on(*args, &block) try do # For every block, we make sure to reset captures so that # nesting matchers won't mess with each other's captures. @captures = [] # We stop evaluation of this entire matcher unless # each and every `arg` defined for this matcher evaluates # to a non-false value. # # Short circuit examples: # on true, false do # # # PATH_INFO=/user # on true, "signup" return unless args.all? { |arg| match(arg) } # The captures we yield here were generated and assembled # by evaluating each of the `arg`s above. Most of these # are carried out by #consume. yield(*captures) if res.status.nil? if res.body.empty? not_found else res.headers[Rack::CONTENT_TYPE] ||= DEFAULT res.status = 200 end end halt(res.finish) end end
# File lib/cuba.rb, line 359 def options; req.options? end
Ensures that certain request parameters are present. Acts like a precondition / assertion for your route. A default value can be provided as a second argument. In that case, it always matches and the result is either the parameter or the default value.
@example
# POST with data like user[fname]=John&user[lname]=Doe on "signup", param("user") do |atts| User.create(atts) end on "login", param("username", "guest") do |username| # If not provided, username == "guest" end
# File lib/cuba.rb, line 289 def param(key, default = nil) value = req.params[key.to_s] || default lambda { captures << value unless value.to_s.empty? } end
# File lib/cuba.rb, line 356 def patch; req.patch? end
# File lib/cuba.rb, line 354 def post; req.post? end
# File lib/cuba.rb, line 355 def put; req.put? end
Access the root of the application.
@example
# GET / on root do res.write "Home" end
# File lib/cuba.rb, line 341 def root env[Rack::PATH_INFO] == SLASH || env[Rack::PATH_INFO] == EMPTY end
If you want to halt the processing of an existing handler and continue it via a different handler.
@example
def redirect(*args) run Cuba.new { on(default) { res.redirect(*args) }} end on "account" do redirect "/login" unless session["uid"] res.write "Super secure account info." end
# File lib/cuba.rb, line 377 def run(app) halt app.call(req.env) end
# File lib/cuba.rb, line 166 def session env["rack.session"] || raise(RuntimeError, "You're missing a session handler. You can get started " + "by adding Cuba.use Rack::Session::Cookie") end
# File lib/cuba.rb, line 138 def settings self.class.settings end
# File lib/cuba.rb, line 362 def trace; req.trace? end
# File lib/cuba.rb, line 361 def unlink; req.unlink? end
Returns a hash with the information set by the with
method.
with(role: "admin", site: "main") do on default do res.write(vars.inspect) end end # => '{:role=>"admin", :site=>"main"}'
# File lib/cuba.rb, line 424 def vars env["cuba.vars"] ||= {} end
Adds ability to pass information to a nested Cuba
application. It receives two parameters: a hash that represents the passed information and a block. The vars
method is used to retrieve a hash with the passed information.
class Platforms < Cuba define do platform = vars[:platform] on default do res.write(platform) # => "heroku" or "salesforce" end end end Cuba.define do on "(heroku|salesforce)" do |platform| with(platform: platform) do run(Platforms) end end end
# File lib/cuba.rb, line 408 def with(dict = {}) old, env["cuba.vars"] = vars, vars.merge(dict) yield ensure env["cuba.vars"] = old end
Private Instance Methods
# File lib/cuba.rb, line 239 def consume(pattern) matchdata = env[Rack::PATH_INFO].match(REGEXES[pattern]) return false unless matchdata path, *vars = matchdata.captures env[Rack::SCRIPT_NAME] += "/#{path}" env[Rack::PATH_INFO] = "#{vars.pop}#{matchdata.post_match}" captures.push(*vars) end
@private Used internally by on
to ensure that SCRIPT_NAME and
PATH_INFO are reset to their proper values.
# File lib/cuba.rb, line 229 def try script, path = env[Rack::SCRIPT_NAME], env[Rack::PATH_INFO] yield ensure env[Rack::SCRIPT_NAME], env[Rack::PATH_INFO] = script, path end