lib/eeny-meeny/middleware.rb in eeny-meeny-2.1.1 vs lib/eeny-meeny/middleware.rb in eeny-meeny-2.1.2
- old
+ new
@@ -6,57 +6,54 @@
require 'eeny-meeny/models/cookie'
module EenyMeeny
class Middleware
+ # Headers
+ HTTP_COOKIE = 'HTTP_COOKIE'.freeze
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
+ QUERY_STRING = 'QUERY_STRING'.freeze
+
def initialize(app)
@app = app
@experiments = EenyMeeny::Experiment.find_all
@cookie_config = EenyMeeny.config.cookies
end
def call(env)
- request = Rack::Request.new(env)
- cookies = request.cookies
- now = Time.zone.now
- new_cookies = {}
- existing_set_cookie_header = env['Set-Cookie']
+ cookies = Rack::Utils.parse_query(env[HTTP_COOKIE],';,') { |s| Rack::Utils.unescape(s) rescue s }
+ query_parameters = query_hash(env)
+ now = Time.zone.now
+ new_cookies = {}
# Prepare for experiments.
@experiments.each do |experiment|
# Skip inactive experiments
next unless experiment.active?(now)
- # Trigger experiment through query parmeters
+ # Trigger experiment through query parameters
cookie_name = EenyMeeny::Cookie.cookie_name(experiment)
- has_experiment_trigger = EenyMeeny.config.query_parameters[:experiment] && request.params.has_key?(cookie_name)
+ has_experiment_trigger = EenyMeeny.config.query_parameters[:experiment] && query_parameters.key?(cookie_name)
# skip experiments that already have a cookie
- if has_experiment_trigger || !cookies.has_key?(cookie_name)
- cookie = if has_experiment_trigger
- # Trigger experiment variation through query parameter.
- EenyMeeny::Cookie.create_for_experiment_variation(experiment, request.params[cookie_name].to_sym, @cookie_config)
- else
- EenyMeeny::Cookie.create_for_experiment(experiment, @cookie_config)
- end
- # Set HTTP_COOKIE header to enable experiment on first pageview
- env = add_http_cookie(env, cookie, precede: has_experiment_trigger)
- new_cookies[cookie.name] = cookie
- end
+ next unless has_experiment_trigger || !cookies.key?(cookie_name)
+ cookie = if has_experiment_trigger
+ # Trigger experiment variation through query parameter.
+ EenyMeeny::Cookie.create_for_experiment_variation(experiment, query_parameters[cookie_name].to_sym, @cookie_config)
+ else
+ EenyMeeny::Cookie.create_for_experiment(experiment, @cookie_config)
+ end
+ # Set HTTP_COOKIE header to enable experiment on first pageview
+ env = add_or_replace_http_cookie(env, cookie)
+ new_cookies[cookie.name] = cookie
end
# Prepare smoke tests (if enabled through query parameters)
if EenyMeeny.config.query_parameters[:smoke_test]
- if request.params.has_key?('smoke_test_id') && (request.params['smoke_test_id'] =~ /[A-Za-z_]+/)
+ if query_parameters.key?('smoke_test_id') && (query_parameters['smoke_test_id'] =~ /\A[A-Za-z_]+\z/)
# Set HTTP_COOKIE header to enable smoke test on first pageview
- cookie = EenyMeeny::Cookie.create_for_smoke_test(request.params['smoke_test_id'])
- env = add_http_cookie(env, cookie, precede: true)
+ cookie = EenyMeeny::Cookie.create_for_smoke_test(query_parameters['smoke_test_id'])
+ env = add_or_replace_http_cookie(env, cookie)
new_cookies[cookie.name] = cookie
end
end
- # Clean up 'Set-Cookie' header.
- if existing_set_cookie_header.nil?
- env.delete('Set-Cookie')
- else
- env['Set-Cookie'] = existing_set_cookie_header
- end
# Delegate to app
status, headers, body = @app.call(env)
response = Rack::Response.new(body, status, headers)
# Add new cookies to 'Set-Cookie' header
new_cookies.each do |key, value|
@@ -64,22 +61,25 @@
end
response.finish
end
private
- def add_http_cookie(env, cookie, precede: false)
- env['Set-Cookie'] = ''
- Rack::Utils.set_cookie_header!(env,
- cookie.name,
- cookie.to_h)
- env['HTTP_COOKIE'] = '' if env['HTTP_COOKIE'].nil?
- if precede
- # Prepend cookie to the 'HTTP_COOKIE' header. This ensures it overwrites existing cookies when present.
- env['HTTP_COOKIE'] = env['Set-Cookie'] + '; ' + env['HTTP_COOKIE']
- else
- env['HTTP_COOKIE'] += '; ' unless env['HTTP_COOKIE'].empty?
- env['HTTP_COOKIE'] += env['Set-Cookie']
- end
+
+ def query_hash(env)
+ # Query Params are only relevant if EenyMeeny.config have them enabled.
+ return {} unless EenyMeeny.config.query_parameters[:experiment] || EenyMeeny.config.query_parameters[:smoke_test]
+ # Query Params are only relevant to HTTP GET requests.
+ return {} unless env[REQUEST_METHOD] == 'GET'
+ Rack::Utils.parse_query(env[QUERY_STRING], '&;')
+ end
+
+ def add_or_replace_http_cookie(env, cookie)
+ cookie_name_escaped = Rack::Utils.escape(cookie.name)
+ cookie_string = "#{cookie_name_escaped}=#{Rack::Utils.escape(cookie.value)}"
+ env[HTTP_COOKIE] = '' if env[HTTP_COOKIE].nil?
+ return env if env[HTTP_COOKIE].sub!(/#{Regexp.escape(cookie_name_escaped)}=[^;]+/, cookie_string)
+ env[HTTP_COOKIE] += '; ' unless env[HTTP_COOKIE].empty?
+ env[HTTP_COOKIE] += cookie_string
env
end
end
end