rack-canonical-host 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.
|