lib/roda/plugins/header_matchers.rb



# frozen-string-literal: true

#
class Roda
  module RodaPlugins
    # The header_matchers plugin adds hash matchers for matching on less-common
    # HTTP headers.
    #
    #   plugin :header_matchers
    #
    # It adds a +:header+ matcher for matching on arbitrary headers, which matches
    # if the header is present:
    #
    #   r.on :header=>'HTTP-X-App-Token' do |header_value|
    #     # Looks for env['HTTP_X_APP_TOKEN']
    #   end
    #
    # For backwards compatibility, the header value is not automatically prefixed
    # with HTTP_.  You can set the +:header_matcher_prefix+ option for the application,
    # which will automatically prefix the header with HTTP_:
    #
    #   r.on :header=>'X-App-Token' do |header_value|
    #     # Looks for env['HTTP_X_APP_TOKEN'] 
    #   end
    #
    # It adds a +:host+ matcher for matching by the host of the request:
    #
    #   r.on :host=>'foo.example.com' do
    #   end
    #   r.on :host=>/\A\w+.example.com\z/ do
    #   end
    #
    # By default the +:host+ matcher does not yield matchers, but if you use a regexp
    # and set the +:host_matcher_captures+ option for the application, it will
    # yield regexp captures:
    # 
    #   r.on :host=>/\A(\w+).example.com\z/ do |subdomain|
    #   end
    #
    # It adds a +:user_agent+ matcher for matching on a user agent patterns, which
    # yields the regexp captures to the block:
    #
    #   r.on :user_agent=>/Chrome\/([.\d]+)/ do |chrome_version|
    #   end
    #
    # It adds an +:accept+ matcher for matching based on the Accept header:
    #
    #   r.on :accept=>'text/csv' do
    #   end
    #
    # Note that the accept matcher is very simple and cannot handle wildcards,
    # priorities, or anything but a simple comma separated list of mime types.
    module HeaderMatchers
      module RequestMethods
        private

        # Match if the given mimetype is one of the accepted mimetypes.
        def match_accept(mimetype)
          if @env["HTTP_ACCEPT"].to_s.split(',').any?{|s| s.strip == mimetype}
            response["Content-Type"] = mimetype
          end
        end

        # Match if the given uppercase key is present inside the environment.
        def match_header(key)
          key = key.upcase.tr("-","_")

          if roda_class.opts[:header_matcher_prefix]
            key = "HTTP_#{key}"
          end

          if v = @env[key]
            @captures << v
          end
        end

        # Match if the host of the request is the same as the hostname.  +hostname+
        # can be a regexp or a string.
        def match_host(hostname)
          if hostname.is_a?(Regexp) && roda_class.opts[:host_matcher_captures]
            if match = hostname.match(host)
              @captures.concat(match.captures)
            end
          else
            hostname === host
          end
        end

        # Match the submitted user agent to the given pattern, capturing any
        # regexp match groups.
        def match_user_agent(pattern)
          if (user_agent = @env["HTTP_USER_AGENT"]) && user_agent.to_s =~ pattern
            @captures.concat($~[1..-1])
          end
        end
      end
    end

    register_plugin(:header_matchers, HeaderMatchers)
  end
end