rack-canonical-host 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +24 -0
- data/Gemfile +3 -0
- data/README.md +97 -0
- data/Rakefile +3 -0
- data/lib/rack-canonical-host.rb +1 -30
- data/lib/rack/canonical_host.rb +48 -0
- data/lib/rack/canonical_host/version.rb +5 -0
- data/rack-canonical-host.gemspec +17 -0
- data/spec/rack/canonical_host_spec.rb +73 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/matchers/be_redirect.rb +67 -0
- metadata +65 -52
- data/README.rdoc +0 -32
data/.gitignore
ADDED
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## Rack::CanonicalHost 0.0.4
|
4
|
+
|
5
|
+
* Add option to ignored certain hosts ([Eric Allam][rubymaverick])
|
6
|
+
* Add tests ([Nathaniel Bibler][nbibler])
|
7
|
+
* Add HTML response body on redirect
|
8
|
+
* Set `Content-Type` header on redirect ([Jon Wood][jellybob])
|
9
|
+
|
10
|
+
## Rack::CanonicalHost 0.0.3
|
11
|
+
|
12
|
+
* Allow `env` to be passed to the optional block
|
13
|
+
|
14
|
+
## Rack::CanonicalHost 0.0.2
|
15
|
+
|
16
|
+
* Move `CanonicalHost` into `Rack` namespace
|
17
|
+
|
18
|
+
## Rack::CanonicalHost 0.0.1
|
19
|
+
|
20
|
+
* Initial release
|
21
|
+
|
22
|
+
[jellybob]: http://github.com/jellybob
|
23
|
+
[nbibler]: http://github.com/nbibler
|
24
|
+
[rubymaverick]: http://github.com/rubymaverick
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# Rack Canonical Host
|
2
|
+
|
3
|
+
Rack middleware that lets you define a single host name as the canonical host
|
4
|
+
for your application. Requests for other host names will then be redirected to
|
5
|
+
the canonical host.
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's `Gemfile`:
|
11
|
+
|
12
|
+
``` ruby
|
13
|
+
gem 'rack-canonical-host'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install rack-canonical-host
|
23
|
+
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
For most applications, you can insert the middleware into the `config.ru` file
|
28
|
+
in the root of the application.
|
29
|
+
|
30
|
+
Here's a simple example of what the `config.ru` in a Rails application might
|
31
|
+
look like after adding the `Rack::CanonicalHost` middleware.
|
32
|
+
|
33
|
+
``` ruby
|
34
|
+
require ::File.expand_path('../config/environment', __FILE__)
|
35
|
+
|
36
|
+
use Rack::CanonicalHost, 'example.com'
|
37
|
+
run YourRailsApp::Application
|
38
|
+
```
|
39
|
+
|
40
|
+
In this case, any requests coming in that aren't for the specified host,
|
41
|
+
`example.com`, will be redirected, keeping the requested path intact.
|
42
|
+
|
43
|
+
|
44
|
+
### Environment-Specific Configuration
|
45
|
+
|
46
|
+
You probably don't want your redirect to happen when developing locally. One
|
47
|
+
way to prevent this from happening is to use environment variables in your
|
48
|
+
production environment to set the canonical host name.
|
49
|
+
|
50
|
+
With Heroku, you would do this like so:
|
51
|
+
|
52
|
+
$ heroku config:add CANONICAL_HOST=example.com
|
53
|
+
|
54
|
+
Then, can configure the middleware like this:
|
55
|
+
|
56
|
+
``` ruby
|
57
|
+
use Rack::CanonicalHost, ENV['CANONICAL_HOST'] if ENV['CANONICAL_HOST']
|
58
|
+
```
|
59
|
+
|
60
|
+
Now, the middleware will only be used if a canonical host has been defined.
|
61
|
+
|
62
|
+
|
63
|
+
### Options
|
64
|
+
|
65
|
+
If you'd like the middleware to ignore certain hosts, use the `:ignore_hosts`
|
66
|
+
option:
|
67
|
+
|
68
|
+
``` ruby
|
69
|
+
use Rack::CanonicalHost, 'example.com', ignored_hosts: ['api.example.com']
|
70
|
+
```
|
71
|
+
|
72
|
+
In this case, requests for the host `api.example.com` will not be redirected.
|
73
|
+
|
74
|
+
Alternatively, you can pass a block whose return value will be used as the
|
75
|
+
canonical host name.
|
76
|
+
|
77
|
+
``` ruby
|
78
|
+
use Rack::CanonicalHost do
|
79
|
+
case ENV['RACK_ENV'].to_sym
|
80
|
+
when :staging then 'example.com'
|
81
|
+
when :production then 'staging.example.com'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
|
87
|
+
## Contributors
|
88
|
+
|
89
|
+
Thanks to the following people who have contributed patches or helpful
|
90
|
+
suggestions:
|
91
|
+
|
92
|
+
* [Peter Baker](http://github.com/finack)
|
93
|
+
* [Jon Wood](http://github.com/jellybob)
|
94
|
+
* [Nathaniel Bibler](http://github.com/nbibler)
|
95
|
+
* [Eric Allam](http://github.com/rubymaverick)
|
96
|
+
|
97
|
+
|
data/Rakefile
ADDED
data/lib/rack-canonical-host.rb
CHANGED
@@ -1,30 +1 @@
|
|
1
|
-
|
2
|
-
class CanonicalHost
|
3
|
-
def initialize(app, host=nil, &block)
|
4
|
-
@app = app
|
5
|
-
@host = host
|
6
|
-
@block = block
|
7
|
-
end
|
8
|
-
|
9
|
-
def call(env)
|
10
|
-
if url = url(env)
|
11
|
-
[301, { 'Location' => url }, ['Redirecting...']]
|
12
|
-
else
|
13
|
-
@app.call(env)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def url(env)
|
18
|
-
if (host = host(env)) && env['SERVER_NAME'] != host
|
19
|
-
url = Rack::Request.new(env).url
|
20
|
-
url.sub(%r{\A(https?://)(.*?)(:\d+)?(/|$)}, "\\1#{host}\\3/")
|
21
|
-
end
|
22
|
-
end
|
23
|
-
private :url
|
24
|
-
|
25
|
-
def host(env)
|
26
|
-
@block ? @block.call(env) || @host : @host
|
27
|
-
end
|
28
|
-
private :host
|
29
|
-
end
|
30
|
-
end
|
1
|
+
require 'rack/canonical_host'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module Rack # :nodoc:
|
4
|
+
class CanonicalHost
|
5
|
+
HTML_TEMPLATE = <<-HTML
|
6
|
+
<!DOCTYPE html>
|
7
|
+
<html lang="en-US">
|
8
|
+
<head><title>301 Moved Permanently</title></head>
|
9
|
+
<body>
|
10
|
+
<h1>Moved Permanently</h1>
|
11
|
+
<p>The document has moved <a href="%s">here</a>.</p>
|
12
|
+
</body>
|
13
|
+
</html>
|
14
|
+
HTML
|
15
|
+
|
16
|
+
def initialize(app, host=nil, options={}, &block)
|
17
|
+
@app = app
|
18
|
+
@host = host
|
19
|
+
@block = block
|
20
|
+
@ignored_hosts = options[:ignored_hosts] || []
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(env)
|
24
|
+
if url = url(env)
|
25
|
+
[
|
26
|
+
301,
|
27
|
+
{ 'Location' => url, 'Content-Type' => 'text/html' },
|
28
|
+
[HTML_TEMPLATE % url]
|
29
|
+
]
|
30
|
+
else
|
31
|
+
@app.call(env)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def url(env)
|
36
|
+
if (host = host(env)) && env['SERVER_NAME'] != host && !@ignored_hosts.include?(env['SERVER_NAME'])
|
37
|
+
url = Rack::Request.new(env).url
|
38
|
+
url.sub(%r{\A(https?://)(.*?)(:\d+)?(/|$)}, "\\1#{host}\\3/")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
private :url
|
42
|
+
|
43
|
+
def host(env)
|
44
|
+
@block ? @block.call(env) || @host : @host
|
45
|
+
end
|
46
|
+
private :host
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require './lib/rack/canonical_host/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'rack-canonical-host'
|
5
|
+
gem.version = Rack::CanonicalHost::VERSION
|
6
|
+
gem.summary = 'Rack middleware for defining a canonical host name.'
|
7
|
+
gem.homepage = 'http://github.com/tylerhunt/rack-canonical-host'
|
8
|
+
gem.author = 'Tyler Hunt'
|
9
|
+
|
10
|
+
gem.add_dependency 'rack', '~> 1.0'
|
11
|
+
gem.add_development_dependency 'rspec', '~> 2.0'
|
12
|
+
|
13
|
+
gem.files = `git ls-files`.split($\)
|
14
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.require_paths = ['lib']
|
17
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rack::CanonicalHost do
|
4
|
+
let(:response) { [200, { 'Content-Type' => 'text/plain' }, 'OK'] }
|
5
|
+
let(:inner_app) { lambda { |env| response } }
|
6
|
+
|
7
|
+
def build_app(host=nil, options={}, inner_app=inner_app, &block)
|
8
|
+
Rack::Builder.new do
|
9
|
+
use Rack::Lint
|
10
|
+
use Rack::CanonicalHost, host, options, &block
|
11
|
+
run inner_app
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
shared_context 'matching and non-matching requests' do
|
16
|
+
context 'with a request to a matching host' do
|
17
|
+
let(:url) { 'http://example.com/full/path' }
|
18
|
+
|
19
|
+
it { should_not be_redirect }
|
20
|
+
|
21
|
+
it 'calls the inner app' do
|
22
|
+
inner_app.should_receive(:call).with(env).and_return(response)
|
23
|
+
subject
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with a request to a non-matching host' do
|
28
|
+
let(:url) { 'http://www.example.com/full/path' }
|
29
|
+
|
30
|
+
it { should be_redirect.via(301).to('http://example.com/full/path') }
|
31
|
+
|
32
|
+
it 'does not call the inner app' do
|
33
|
+
inner_app.should_not_receive(:call)
|
34
|
+
subject
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context '#call' do
|
40
|
+
let(:env) { Rack::MockRequest.env_for(url) }
|
41
|
+
|
42
|
+
subject { app.call(env) }
|
43
|
+
|
44
|
+
context 'without any options' do
|
45
|
+
let(:app) { build_app('example.com') }
|
46
|
+
|
47
|
+
include_context 'matching and non-matching requests'
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'with ignored hosts' do
|
51
|
+
let(:app) { build_app('example.com', ignored_hosts: ['example.net']) }
|
52
|
+
|
53
|
+
include_context 'matching and non-matching requests'
|
54
|
+
|
55
|
+
context 'with a request to an ignored host' do
|
56
|
+
let(:url) { 'http://example.net/full/path' }
|
57
|
+
|
58
|
+
it { should_not be_redirect }
|
59
|
+
|
60
|
+
it 'calls the inner app' do
|
61
|
+
inner_app.should_receive(:call).with(env).and_return(response)
|
62
|
+
subject
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'with a block' do
|
68
|
+
let(:app) { build_app { 'example.com' } }
|
69
|
+
|
70
|
+
include_context 'matching and non-matching requests'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rack/canonical_host'
|
2
|
+
|
3
|
+
Dir[File.expand_path('../support/**/*.rb', __FILE__)].each do |file|
|
4
|
+
require(file)
|
5
|
+
end
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
9
|
+
config.run_all_when_everything_filtered = true
|
10
|
+
config.filter_run :focus
|
11
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module BeRedirect
|
2
|
+
class Matcher
|
3
|
+
attr :expected_status_code
|
4
|
+
attr :expected_location
|
5
|
+
attr :actual_status_code
|
6
|
+
attr :actual_location
|
7
|
+
|
8
|
+
def matches?(response)
|
9
|
+
@actual_status_code, headers, _ = response
|
10
|
+
@actual_location = headers['Location']
|
11
|
+
|
12
|
+
status_code_matches? && location_matches?
|
13
|
+
end
|
14
|
+
|
15
|
+
def via(expected_status_code)
|
16
|
+
@expected_status_code = expected_status_code
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def to(expected_location)
|
21
|
+
@expected_location = expected_location
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def description
|
26
|
+
if expected_status_code && expected_location
|
27
|
+
"redirect via #{expected_status_code} to #{expected_location.inspect}"
|
28
|
+
elsif expected_status_code
|
29
|
+
"redirect via #{expected_status_code}"
|
30
|
+
elsif expected_location
|
31
|
+
"redirect to #{expected_location.inspect}"
|
32
|
+
else
|
33
|
+
"be a redirect"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def failure_message_for_should
|
38
|
+
"Expected response to #{description}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def failure_message_for_should_not
|
42
|
+
"Did not expect response to #{description}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def status_code_matches?
|
46
|
+
if expected_status_code
|
47
|
+
actual_status_code == expected_status_code
|
48
|
+
else
|
49
|
+
actual_status_code.to_s =~ /^30[1237]$/
|
50
|
+
end
|
51
|
+
end
|
52
|
+
private :status_code_matches?
|
53
|
+
|
54
|
+
def location_matches?
|
55
|
+
!expected_location || (expected_location == actual_location)
|
56
|
+
end
|
57
|
+
private :location_matches?
|
58
|
+
end
|
59
|
+
|
60
|
+
def be_redirect
|
61
|
+
Matcher.new
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
RSpec.configure do |config|
|
66
|
+
config.include(BeRedirect)
|
67
|
+
end
|
metadata
CHANGED
@@ -1,80 +1,93 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-canonical-host
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 0
|
8
|
-
- 3
|
9
|
-
version: 0.0.3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Tyler Hunt
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
dependencies:
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-06-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: rack
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
- 1
|
30
|
-
- 0
|
31
|
-
- 0
|
32
|
-
version: 1.0.0
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.0'
|
33
22
|
type: :runtime
|
34
|
-
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '2.0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '2.0'
|
35
46
|
description:
|
36
47
|
email:
|
37
48
|
executables: []
|
38
|
-
|
39
49
|
extensions: []
|
40
|
-
|
41
50
|
extra_rdoc_files: []
|
42
|
-
|
43
|
-
|
51
|
+
files:
|
52
|
+
- .gitignore
|
53
|
+
- .rspec
|
54
|
+
- CHANGELOG.md
|
55
|
+
- Gemfile
|
44
56
|
- LICENSE
|
45
|
-
- README.
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
46
59
|
- lib/rack-canonical-host.rb
|
47
|
-
|
60
|
+
- lib/rack/canonical_host.rb
|
61
|
+
- lib/rack/canonical_host/version.rb
|
62
|
+
- rack-canonical-host.gemspec
|
63
|
+
- spec/rack/canonical_host_spec.rb
|
64
|
+
- spec/spec_helper.rb
|
65
|
+
- spec/support/matchers/be_redirect.rb
|
48
66
|
homepage: http://github.com/tylerhunt/rack-canonical-host
|
49
67
|
licenses: []
|
50
|
-
|
51
68
|
post_install_message:
|
52
69
|
rdoc_options: []
|
53
|
-
|
54
|
-
require_paths:
|
70
|
+
require_paths:
|
55
71
|
- lib
|
56
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
73
|
none: false
|
58
|
-
requirements:
|
59
|
-
- -
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
|
62
|
-
|
63
|
-
version: "0"
|
64
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
79
|
none: false
|
66
|
-
requirements:
|
67
|
-
- -
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
|
70
|
-
- 0
|
71
|
-
version: "0"
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
72
84
|
requirements: []
|
73
|
-
|
74
85
|
rubyforge_project:
|
75
|
-
rubygems_version: 1.
|
86
|
+
rubygems_version: 1.8.24
|
76
87
|
signing_key:
|
77
88
|
specification_version: 3
|
78
89
|
summary: Rack middleware for defining a canonical host name.
|
79
|
-
test_files:
|
80
|
-
|
90
|
+
test_files:
|
91
|
+
- spec/rack/canonical_host_spec.rb
|
92
|
+
- spec/spec_helper.rb
|
93
|
+
- spec/support/matchers/be_redirect.rb
|
data/README.rdoc
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
= Rack Canonical Host
|
2
|
-
|
3
|
-
Rack Canonical Host is a bit of Rack middleware that lets you define a single
|
4
|
-
host as the canonical host for your site, and redirect all other hostnames to
|
5
|
-
that canonical host.
|
6
|
-
|
7
|
-
In the simplest case, just specify the host name and any requests coming in
|
8
|
-
that aren't for the host example.com will be rewritten with the proper host.
|
9
|
-
|
10
|
-
use Rack::CanonicalHost, 'example.com'
|
11
|
-
|
12
|
-
You could also define a constant in your environment-specific configuration
|
13
|
-
files to set up a canonical host redirect for only certain environments.
|
14
|
-
|
15
|
-
CANONICAL_HOST = 'example.com'
|
16
|
-
|
17
|
-
Then, you can set up the middle are like this.
|
18
|
-
|
19
|
-
use Rack::CanonicalHost, CANONICAL_HOST if defined?(CANONICAL_HOST)
|
20
|
-
|
21
|
-
Alternatively, you can pass a block whose return value will be used to set the
|
22
|
-
canonical host.
|
23
|
-
|
24
|
-
use Rack::CanonicalHost do
|
25
|
-
case ENV['RACK_ENV'].to_sym
|
26
|
-
when :staging then 'example.com'
|
27
|
-
when :production then 'staging.example.com'
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
Copyright (c) 2009 Tyler Hunt. See LICENSE for details.
|