module Innate # Subclass Rack::Request and add some convenient methods. # # An instance is available via the #request method in your Node. # # NOTE: # Please make sure to read the documentation of Rack::Request together with # this, as there are a lot of features available. # # A list of methods from Rack::Request so you get a gist of it: # # ## Generally # # * body # * cookies # * env # * fullpath # * host # * port # * scheme # * url # # ## ENV shortcuts # # * accept_encoding # * content_charset # * content_length # * content_type # * ip # * media_type # * media_type_params # * path_info # * path_info= # * query_string # * referrer # * script_name # * script_name= # * xhr? # # ## Query request method # # * delete? # * get? # * head? # * post? # * put? # * request_method # # ## parameter handling # # * [] # * []= # * form_data? # * params # * values_at class Request < Rack::Request # Currently handled request from Thread.current[:request] # Call it from anywhere via Innate::Request.current def self.current Current.request end # Let's allow #[] to act like #values_at. # # Usage given a GET request like /hey?foo=duh&bar=doh # # request[:foo, :bar] # => ['duh', 'doh'] # # Both +value+ and the elements of +keys+ will be turned into String by #to_s. def [](value, *keys) return super(value) if keys.empty? [value, *keys].map{|key| super(key) } end # the full request URI provided by Rack::Request # e.g. "http://localhost:7000/controller/action?foo=bar.xhtml" def request_uri env['REQUEST_URI'] || env['PATH_INFO'] end # Answers with a subset of request.params with only the key/value pairs for # which you pass the keys. # Valid keys are objects that respond to :to_s # # @example usage # # request.params # # => {'name' => 'jason', 'age' => '45', 'job' => 'lumberjack'} # request.subset('name') # # => {'name' => 'jason'} # request.subset(:name, :job) # # => {'name' => 'jason', 'job' => 'lumberjack'} def subset(*keys) keys = keys.map{|key| key.to_s } params.reject{|key, value| not keys.include?(key) } end # Try to figure out the domain we are running on, this might work for some # deployments but fail for others, given the combination of servers in # front. # # @example usage # # domain # # => # # domain('/foo') # # => # # # @param [#to_s] path # # @return [URI] # # @api external # @author manveru def domain(path = nil, options = {}) uri = URI(self.url) uri.path = path.to_s if path uri.query = nil unless options[:keep_query] uri end ipv4 = %w[ 127.0.0.1/32 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 169.254.0.0/16 ] ipv6 = %w[ fc00::/7 fe80::/10 fec0::/10 ::1 ] LOCAL = (ipv4 + ipv6).map{|range| IPAddr.new(range)} unless defined?(LOCAL) # Request is from a local network? # Checks both IPv4 and IPv6 # Answer is true if the IP address making the request is from local network. # Optional argument address can be used to check any IP address. def local_net?(address = ip) addr = IPAddr.new(address) LOCAL.find{|range| range.include?(addr) } rescue ArgumentError => ex raise ArgumentError, ex unless ex.message == 'invalid address' end end end