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