geo_redirect 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in geo_redirect.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 TODO: Write your name
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # Waze GeoRedirect
2
+
3
+ `GeoRedirect` is a Rack middleware that can be configured to
4
+ redirect incoming clients to different hosts based on their
5
+ geo-location data.
6
+
7
+ For instance, we use it for [our advertisers site](http://biz.waze.com/)
8
+ to redirect users to:
9
+
10
+ * [biz.waze.co.il](http://biz.waze.co.il) for Israel-incoming traffic.
11
+ * [biz.waze.com](http://biz.waze.com) for US and Canada incoming traffic.
12
+ * [biz-world.waze.com](http://biz-world.waze.com/) for any other sources.
13
+
14
+ The server stores a session variable with the server it decided on, for future traffic from the same client.
15
+
16
+ In addition, you can override these redirects by adding `?redirect=1` to any URL, and by that forcing the server to host from the current domain (and saving that domain to the user's session variable).
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ gem 'geo_redirect'
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+
29
+ ## Usage
30
+
31
+ These usage instructions were written for Rails products, although I'm pretty sure you could use the gem with any other Rack-based solution.
32
+
33
+ You'll need to add this to your `production.rb`:
34
+
35
+ Rails.application.middleware.use GeoRedirect::Middleware
36
+
37
+ This will make sure `GeoRedirect` runs before your application gets rolling.
38
+
39
+ The middleware requires two additional files to be present:
40
+
41
+ ### 1. Configuration YML
42
+
43
+ This should be a YML file representing your redirection rules.
44
+
45
+ Here's a template that we use for the setup described above:
46
+
47
+ ```
48
+ :us:
49
+ :host: 'biz.waze.com'
50
+ :countries: ['US', 'CA']
51
+
52
+ :il:
53
+ :host: 'biz.waze.co.il'
54
+ :countries: ['IL']
55
+
56
+ :world: &default
57
+ :host: 'biz-world.waze.com'
58
+
59
+ :default: *default
60
+ ```
61
+
62
+ Note that:
63
+
64
+ 1. Every main item is a location, and must have a `host` configured.
65
+ 2. A location can have a `countries` array. This will cause a redirect to this location for users from that country code. For available country codes, see [ISO 3166 Country Codes list](http://www.maxmind.com/en/iso3166) from MaxMind (the Geo IP provider `GeoRedirect` uses).
66
+ 3. There must be a `default` location that would be used in case the client can't be geo-located.
67
+
68
+ ### 2. GeoIP Countries database
69
+
70
+ `GeoRedirect` uses the [`geoip`](http://geoip.rubyforge.org/) gem for its geo-location functionality. In particular, it requires the `GeoLite country` free database from [MaxMind](http://www.maxmind.com/).
71
+
72
+ You can download the database file [directly from MaxMind](http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz) and unzip it into `db/` in your project, **or** you could use the following `rake` task designed just for that:
73
+
74
+ $ rake georedirect:fetch_db
75
+
76
+ ### Custom paths
77
+
78
+ The default paths for these files are:
79
+
80
+ 1. `config/georedirect.yml`
81
+ 2. `db/GeoIP.dat`
82
+
83
+ If that doesn't suit you, you can customize these when adding `GeoRedirect` to your project:
84
+
85
+ Rails.application.middleware.use GeoRedirect::Middleware {
86
+ :db => 'db/geo_database.dat',
87
+ :config => 'geo_cfg.yml'
88
+ }
89
+
90
+ ## Known Issues
91
+
92
+ A couple issues I know about but haven't had the time to fix:
93
+
94
+ 1. Cross-domain session var is required. In particular, if your stubborn user goes to more than 1 server with `?redirect=1`, all of these servers will never redirect them again (until the session is expired).
95
+ 2. When a client accesses your site from an unknown hostname (one that was not configured in the `yml` file) with `?redirect=1`, they will stay in that hostname for the current session, but in the future would be redirected to the configured default hostname (because it was saved on their session var).
96
+
97
+
98
+ ## Contributing
99
+
100
+ 1. Fork it!
101
+ 2. Create your feature branch! (`git checkout -b my-new-feature`)
102
+ 3. Commit your changes! (`git commit -am 'Add some feature'`)
103
+ 4. Push to the branch! (`git push origin my-new-feature`)
104
+ 5. Create new Pull Request!
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'geo_redirect/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "geo_redirect"
8
+ gem.version = GeoRedirect::VERSION
9
+ gem.authors = ["Sagie Maoz"]
10
+ gem.email = ["sagie@waze.com"]
11
+ gem.description = %q{Geo-location based redirector}
12
+ gem.summary = %q{Rack middleware to redirect clients to hostnames based on geo-location}
13
+ gem.homepage = ""
14
+
15
+ gem.add_dependency "geoip"
16
+
17
+ gem.files = `git ls-files`.split($/)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.require_paths = ["lib"]
21
+ end
@@ -0,0 +1,146 @@
1
+ require 'yaml'
2
+ require 'geoip'
3
+ require 'geo_redirect/version'
4
+
5
+ module GeoRedirect
6
+ # Load rake tasks
7
+ require 'geo_redirect/railtie' if defined?(Rails)
8
+
9
+ # Rack middleware
10
+ class Middleware
11
+ def initialize(app, options = {})
12
+ # Some defaults
13
+ options[:db] ||= 'db/GeoIP.dat'
14
+ options[:config] ||= 'config/geo_redirect.yml'
15
+
16
+ @app = app
17
+
18
+ # Load GeoIP database
19
+ begin
20
+ @db = GeoIP.new(options[:db])
21
+ rescue Errno::EINVAL, Errno::ENOENT => e
22
+ puts "Could not load GeoIP database file."
23
+ puts "Please make sure you have a valid one and"
24
+ puts "add its name to the GeoRedirect middleware."
25
+ puts "Alternatively, use `rake georedirect:fetch_db`"
26
+ puts "to fetch it to the default location (under db/)."
27
+ raise e
28
+ end
29
+
30
+ # Load config object
31
+ begin
32
+ @config = YAML.load_file('config/geo_redirect.yml')
33
+ raise Errno::EINVAL unless @config
34
+ rescue Errno::EINVAL, Errno::ENOENT => e
35
+ puts "Could not load GeoRedirect config YML file."
36
+ puts "Please make sure you have a valid YML file"
37
+ puts "and pass its name when adding the"
38
+ puts "GeoRedirect middlware."
39
+ raise e
40
+ end
41
+ end
42
+
43
+ def call(env)
44
+ @request = Rack::Request.new(env)
45
+
46
+ if force_redirect?
47
+ handle_force
48
+
49
+ elsif session_exists?
50
+ handle_session
51
+
52
+ else
53
+ handle_geoip
54
+ end
55
+ end
56
+
57
+ def session_exists?
58
+ host = @request.session['geo_redirect']
59
+ if host.present? && @config[host].nil? # Invalid var, remove it
60
+ forget_host
61
+ host = nil
62
+ end
63
+
64
+ host.present?
65
+ end
66
+
67
+ def handle_session
68
+ host = @request.session['geo_redirect']
69
+ redirect_request(host)
70
+ end
71
+
72
+ def force_redirect?
73
+ url = URI.parse(@request.url)
74
+ Rack::Utils.parse_query(url.query).key? 'redirect'
75
+ end
76
+
77
+ def handle_force
78
+ url = URI.parse(@request.url)
79
+ host = host_by_hostname(url.host)
80
+ remember_host(host)
81
+ redirect_request(url.host, true)
82
+ end
83
+
84
+ def handle_geoip
85
+ # Fetch country code
86
+ begin
87
+ res = @db.country(@request.env['REMOTE_ADDR'])
88
+ code = res.try(:country_code)
89
+ country = res.try(:country_code2) unless code.nil? || code.zero?
90
+ rescue
91
+ country = nil
92
+ end
93
+
94
+ unless country.nil?
95
+ host = host_by_country(country) # desired host
96
+ remember_host(host)
97
+
98
+ redirect_request(host)
99
+ else
100
+ @app.call(@request.env)
101
+ end
102
+ end
103
+
104
+ def redirect_request(host=nil, same_host=false)
105
+ redirect = true
106
+ unless host.nil?
107
+ hostname = host.is_a?(Symbol) ? @config[host][:host] : host
108
+ redirect = hostname.present?
109
+ redirect &&= !@request.host.ends_with?(hostname) unless same_host
110
+ end
111
+
112
+ if redirect
113
+ url = URI.parse(@request.url)
114
+ url.port = nil
115
+ url.host = hostname if host
116
+ # Remove 'redirect' GET arg
117
+ url.query = Rack::Utils.parse_query(url.query).tap{ |u|
118
+ u.delete('redirect')
119
+ }.to_param
120
+ url.query = nil if url.query.empty?
121
+
122
+ [301, {'Location' => url.to_s}, ['Moved Permanently\n']]
123
+ else
124
+ @app.call(@request.env)
125
+ end
126
+ end
127
+
128
+ def host_by_country(country)
129
+ hosts = @config.select { |k, v| Array(v[:countries]).include?(country) }
130
+ hosts.keys.first || :default
131
+ end
132
+
133
+ def host_by_hostname(hostname)
134
+ hosts = @config.select { |k, v| v[:host] == hostname }
135
+ hosts.keys.first || :default
136
+ end
137
+
138
+ def remember_host(host)
139
+ @request.session['geo_redirect'] = host
140
+ end
141
+
142
+ def forget_host
143
+ remember_host(nil)
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,10 @@
1
+ require 'geo_redirect'
2
+ require 'rails'
3
+
4
+ module GeoRedirect
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ require 'tasks/geo_redirect.rb'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module GeoRedirect
2
+ VERSION = "0.2.1"
3
+ end
@@ -0,0 +1,19 @@
1
+ require 'zlib'
2
+ require 'open-uri'
3
+
4
+ namespace :georedirect do
5
+ DB_URI = 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz'
6
+
7
+ desc "Fetches an updated copy of the GeoIP countries DB from MaxMind"
8
+ task :fetch_db do
9
+ # Fetches DB copy and gunzips it
10
+ # Thx http://stackoverflow.com/a/2014317/107085
11
+ source = open(DB_URI)
12
+ gz = Zlib::GzipReader.new(source)
13
+ result = gz.read
14
+
15
+ # Write to file
16
+ filename = Rails.root.join('db', 'GeoIP.dat')
17
+ File.open(filename, 'w') { |f| f.write(result) }
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: geo_redirect
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sagie Maoz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: geoip
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Geo-location based redirector
31
+ email:
32
+ - sagie@waze.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - LICENSE.txt
40
+ - README.md
41
+ - Rakefile
42
+ - geo_redirect.gemspec
43
+ - lib/geo_redirect.rb
44
+ - lib/geo_redirect/railtie.rb
45
+ - lib/geo_redirect/version.rb
46
+ - lib/tasks/geo_redirect.rb
47
+ homepage: ''
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 1.8.24
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Rack middleware to redirect clients to hostnames based on geo-location
71
+ test_files: []