rack-canonical-host 0.2.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8a6f24e954b7680ed4e2571398bc6d2012deaf38
4
- data.tar.gz: 224fd0063568a627b4f37bf0ca6dc33d579645ba
2
+ SHA256:
3
+ metadata.gz: 60b35e0827776d7a2e063e985a5239bd479314bcf9c5f4e14f36d68ee995c850
4
+ data.tar.gz: 71463943b6c8707eb4a73db1f2f2e43f73539c5beddd03fd677112a13a26126b
5
5
  SHA512:
6
- metadata.gz: 981074ba73744daeff5a3e7e3256e42ed19cfdb56759ff8e133390ab353804886e9b3fce6bc5268934daa41275b6d1d38ec1c7c8e048e0d4017ce38df3539cb3
7
- data.tar.gz: 6c191906b97d467c729b055abbf9ffade959d35f32071566d0796500f431fa790f9d59c03a5f1427e374ebd3b0e0c385ac749e661314e1007a52fcc982c3ab0b
6
+ metadata.gz: a34e6a6850579a3d8f0cdd1244468b5cc5b392912879ba9733b0a76e4895eedae167f2b2219fdabc3abf8450bb60ced81141842e3ed1418ce84e6926dfd6adb1
7
+ data.tar.gz: b8256000841f0bf5798f400e642b3f93e46abec2ae3a344306b5ad84f475cefb74c38cb0499965c2dd028d7f08391c9c38de3c7a5110167b25c7e7a1d27d7a1d
@@ -0,0 +1,30 @@
1
+ name: CI
2
+ on:
3
+ - push
4
+ - pull_request
5
+ jobs:
6
+ test:
7
+ name: Ruby ${{ matrix.ruby }} & Rack ${{ matrix.rack }}
8
+ runs-on: 'ubuntu-latest'
9
+ strategy:
10
+ matrix:
11
+ ruby:
12
+ - '3.0'
13
+ - '2.7'
14
+ - '2.6'
15
+ rack:
16
+ - '1.5'
17
+ - '1.6'
18
+ - '2.0'
19
+ - '2.1'
20
+ - '2.2'
21
+ fail-fast: false
22
+ env:
23
+ BUNDLE_GEMFILE: "gemfiles/rack_${{ matrix.rack }}.gemfile"
24
+ steps:
25
+ - uses: actions/checkout@v2
26
+ - uses: ruby/setup-ruby@v1
27
+ with:
28
+ ruby-version: ${{ matrix.ruby }}
29
+ - run: bundle install
30
+ - run: bundle exec rake
data/.gitignore CHANGED
@@ -9,6 +9,7 @@ InstalledFiles
9
9
  _yardoc
10
10
  coverage
11
11
  doc
12
+ gemfiles/*.lock
12
13
  lib/bundler/man
13
14
  pkg
14
15
  rdoc
data/Appraisals ADDED
@@ -0,0 +1,19 @@
1
+ appraise 'rack-1.5' do
2
+ gem 'rack', '~> 1.5.0'
3
+ end
4
+
5
+ appraise 'rack-1.6' do
6
+ gem 'rack', '~> 1.6.0'
7
+ end
8
+
9
+ appraise 'rack-2.0' do
10
+ gem 'rack', '~> 2.0.0'
11
+ end
12
+
13
+ appraise 'rack-2.1' do
14
+ gem 'rack', '~> 2.1.0'
15
+ end
16
+
17
+ appraise 'rack-2.2' do
18
+ gem 'rack', '~> 2.2.0'
19
+ end
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.0 (2021-11-10)
4
+
5
+ * Support lambda/proc on `:if` and `:ignore` options ([Sean Huber][shuber])
6
+ * Drop support for Ruby versions 2.3, 2.4, and 2.5 ([Tyler Hunt][tylerhunt])
7
+
8
+ ## 1.0.0 (2020-04-16)
9
+
10
+ * Use equality to determine string matches on `:if` and `:ignore`
11
+
12
+ ## 0.2.3 (2017-04-20)
13
+
14
+ * Add regexp support for `:ignore` option ([Daniel Searles][squaresurf])
15
+
16
+ ## 0.2.2 (2016-05-17)
17
+
18
+ * Add `:cache_control` option ([Pete Nicholls][Aupajo])
19
+
3
20
  ## 0.2.1 (2016-03-28)
4
21
 
5
22
  * Relax Rack dependency to allow for Rack 2 ([Tyler Ewing][zoso10])
@@ -57,6 +74,7 @@
57
74
 
58
75
  * Initial release ([Tyler Hunt][tylerhunt])
59
76
 
77
+ [Aupajo]: http://github.com/Aupajo
60
78
  [finack]: http://github.com/finack
61
79
  [firedev]: http://github.com/firedev
62
80
  [jcarbo]: http://github.com/jcarbo
@@ -64,6 +82,8 @@
64
82
  [jschuur]: http://github.com/jschuur
65
83
  [nbibler]: http://github.com/nbibler
66
84
  [rubymaverick]: http://github.com/ericallam
85
+ [shuber]: http://github.com/shuber
86
+ [squaresurf]: https://github.com/squaresurf
67
87
  [tma]: http://github.com/tma
68
88
  [tylerhunt]: http://github.com/tylerhunt
69
89
  [zoso10]: http://github.com/zoso10
data/README.md CHANGED
@@ -4,16 +4,15 @@ Rack middleware that lets you define a single host name as the canonical host
4
4
  for your application. Requests for other host names will then be redirected to
5
5
  the canonical host.
6
6
 
7
- [![Gem Version](https://img.shields.io/gem/v/rack-canonical-host.svg)](http://rubygems.org/gems/rack-canonical-host)
8
- [![Build Status](https://img.shields.io/travis/tylerhunt/rack-canonical-host/master.svg)](https://travis-ci.org/tylerhunt/rack-canonical-host)
9
- [![Code Climate](https://img.shields.io/codeclimate/github/tylerhunt/rack-canonical-host.svg)](https://codeclimate.com/github/tylerhunt/rack-canonical-host)
10
- [![Dependency Status](https://gemnasium.com/tylerhunt/rack-canonical-host.svg)](https://gemnasium.com/tylerhunt/rack-canonical-host)
7
+ [![Gem Version](https://img.shields.io/gem/v/rack-canonical-host)](http://rubygems.org/gems/rack-canonical-host)
8
+ [![Build Status](https://github.com/tylerhunt/rack-canonical-host/actions/workflows/ci.yml/badge.svg)](https://github.com/tylerhunt/rack-canonical-host/actions/workflows/ci.yml)
9
+ [![Code Climate](https://img.shields.io/codeclimate/maintainability/tylerhunt/rack-canonical-host)](https://codeclimate.com/github/tylerhunt/rack-canonical-host)
11
10
 
12
11
  ## Installation
13
12
 
14
- Add this line to your application's `Gemfile`:
13
+ Add this line to your applications `Gemfile`:
15
14
 
16
- ``` ruby
15
+ ```ruby
17
16
  gem 'rack-canonical-host'
18
17
  ```
19
18
 
@@ -31,10 +30,11 @@ Or install it yourself as:
31
30
  For most applications, you can insert the middleware into the `config.ru` file
32
31
  in the root of the application.
33
32
 
34
- Here's a simple example of what the `config.ru` in a Rails application might
33
+ Heres a simple example of what the `config.ru` in a Rails application might
35
34
  look like after adding the `Rack::CanonicalHost` middleware.
36
35
 
37
- ``` ruby
36
+ ```ruby
37
+ require 'rack/canonical_host'
38
38
  require ::File.expand_path('../config/environment', __FILE__)
39
39
 
40
40
  use Rack::CanonicalHost, 'example.com'
@@ -57,7 +57,7 @@ With Heroku, you would do this like so:
57
57
 
58
58
  Then, can configure the middleware like this:
59
59
 
60
- ``` ruby
60
+ ```ruby
61
61
  use Rack::CanonicalHost, ENV['CANONICAL_HOST'] if ENV['CANONICAL_HOST']
62
62
  ```
63
63
 
@@ -66,11 +66,12 @@ Now, the middleware will only be used if a canonical host has been defined.
66
66
 
67
67
  ### Options
68
68
 
69
- If you'd like the middleware to ignore certain hosts, use the `:ignore`
70
- option:
69
+ If youd like the middleware to ignore certain hosts, use the `:ignore` option,
70
+ which accepts a string, a regular expression, a proc, or an array of those
71
+ objects.
71
72
 
72
- ``` ruby
73
- use Rack::CanonicalHost, 'example.com', ignore: ['api.example.com']
73
+ ```ruby
74
+ use Rack::CanonicalHost, 'example.com', ignore: 'api.example.com'
74
75
  ```
75
76
 
76
77
  In this case, requests for the host `api.example.com` will not be redirected.
@@ -78,7 +79,7 @@ In this case, requests for the host `api.example.com` will not be redirected.
78
79
  Alternatively, you can pass a block whose return value will be used as the
79
80
  canonical host name.
80
81
 
81
- ``` ruby
82
+ ```ruby
82
83
  use Rack::CanonicalHost do |env|
83
84
  case env['RACK_ENV'].to_sym
84
85
  when :staging then 'staging.example.com'
@@ -87,12 +88,31 @@ use Rack::CanonicalHost do |env|
87
88
  end
88
89
  ```
89
90
 
90
- If you want it to react only on specific hosts for multi-domain environment use `:if`,
91
- it accepts string, regexp or an array.
91
+ If you want it to react only on specific hosts within a multi-domain
92
+ environment, use the `:if` option, which accepts a string, a regular
93
+ expression, a `lambda` or `proc`, or an array of those objects.
92
94
 
93
- ``` ruby
95
+ ```ruby
94
96
  use Rack::CanonicalHost, 'example.com', if: /.*\.example\.com/
95
- use Rack::CanonicalHost, 'example.ru', if: /.*\.example\.ru/
97
+ use Rack::CanonicalHost, 'example.org',
98
+ if: ->(uri) { uri.host == 'www.example.org' }
99
+ ```
100
+
101
+ ### Cache-Control
102
+
103
+ To avoid browsers indefinitely caching a `301` redirect, it’s a sensible idea
104
+ to set an expiry on each redirect, to hedge against the chance you may need to
105
+ change that redirect in the future.
106
+
107
+ ```ruby
108
+ # Leave caching up to the browser (which could cache it indefinitely):
109
+ use Rack::CanonicalHost, 'example.com'
110
+
111
+ # Cache the redirect for up to an hour:
112
+ use Rack::CanonicalHost, 'example.com', cache_control: 'max-age=3600'
113
+
114
+ # Prevent caching of redirects:
115
+ use Rack::CanonicalHost, 'example.com', cache_control: 'no-cache'
96
116
  ```
97
117
 
98
118
  ## Contributing
@@ -109,6 +129,7 @@ use Rack::CanonicalHost, 'example.ru', if: /.*\.example\.ru/
109
129
  Thanks to the following people who have contributed patches or helpful
110
130
  suggestions:
111
131
 
132
+ * [Pete Nicholls](https://github.com/Aupajo)
112
133
  * [Tyler Ewing](https://github.com/zoso10)
113
134
  * [Thomas Maurer](https://github.com/tma)
114
135
  * [Jeff Carbonella](https://github.com/jcarbo)
@@ -118,10 +139,12 @@ suggestions:
118
139
  * [Nathaniel Bibler](https://github.com/nbibler)
119
140
  * [Eric Allam](https://github.com/ericallam)
120
141
  * [Fabrizio Regini](https://github.com/freegenie)
121
-
142
+ * [Daniel Searles](https://github.com/squaresurf)
143
+ * [Daniel Searles](https://github.com/squaresurf)
144
+ * [Sean Huber](https://github.com/shuber)
122
145
 
123
146
  ## Copyright
124
147
 
125
- Copyright © 2009-2016 Tyler Hunt.
148
+ Copyright © 2009-2021 Tyler Hunt.
126
149
 
127
150
  Released under the terms of the MIT license. See LICENSE for details.
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rack", "~> 1.5.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rack", "~> 1.6.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rack", "~> 2.0.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rack", "~> 2.1.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rack", "~> 2.2.0"
6
+
7
+ gemspec path: "../"
@@ -20,6 +20,7 @@ module Rack
20
20
  self.host = host
21
21
  self.ignore = Array(options[:ignore])
22
22
  self.conditions = Array(options[:if])
23
+ self.cache_control = options[:cache_control]
23
24
  end
24
25
 
25
26
  def canonical?
@@ -37,29 +38,39 @@ module Rack
37
38
  attr_accessor :host
38
39
  attr_accessor :ignore
39
40
  attr_accessor :conditions
41
+ attr_accessor :cache_control
40
42
 
41
43
  private
42
44
 
43
- def any_match?(patterns, string)
44
- patterns.any? { |pattern| string[pattern] }
45
+ def any_match?(patterns, request_uri)
46
+ patterns.any? { |pattern|
47
+ case pattern
48
+ when Proc then pattern.call(request_uri)
49
+ when Regexp then request_uri.host =~ pattern
50
+ when String then request_uri.host == pattern
51
+ else false
52
+ end
53
+ }
45
54
  end
46
55
 
47
56
  def headers
48
57
  {
49
- 'Location' => new_url,
58
+ 'Cache-Control' => cache_control,
50
59
  'Content-Type' => 'text/html',
51
- }
60
+ 'Location' => new_url,
61
+ }.reject { |_, value| !value }
52
62
  end
53
63
 
54
64
  def enabled?
55
65
  return true if conditions.empty?
56
66
 
57
- conditions.include?(request_uri.host) ||
58
- any_match?(conditions, request_uri.host)
67
+ any_match?(conditions, request_uri)
59
68
  end
60
69
 
61
70
  def ignored?
62
- ignore.include?(request_uri.host)
71
+ return false if ignore.empty?
72
+
73
+ any_match?(ignore, request_uri)
63
74
  end
64
75
 
65
76
  def known?
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class CanonicalHost
3
- VERSION = '0.2.1'
3
+ VERSION = '1.1.0'
4
4
  end
5
5
  end
@@ -4,11 +4,12 @@ Gem::Specification.new do |gem|
4
4
  gem.name = 'rack-canonical-host'
5
5
  gem.version = Rack::CanonicalHost::VERSION
6
6
  gem.summary = 'Rack middleware for defining a canonical host name.'
7
- gem.homepage = 'http://github.com/tylerhunt/rack-canonical-host'
7
+ gem.homepage = 'https://github.com/tylerhunt/rack-canonical-host'
8
8
  gem.author = 'Tyler Hunt'
9
9
 
10
10
  gem.add_dependency 'addressable', '> 0', '< 3'
11
11
  gem.add_dependency 'rack', ['>= 1.0.0', '< 3']
12
+ gem.add_development_dependency 'appraisal', '~> 2.2'
12
13
  gem.add_development_dependency 'rake'
13
14
  gem.add_development_dependency 'rspec', '~> 3.0'
14
15
 
@@ -1,8 +1,13 @@
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 } }
1
+ RSpec.describe Rack::CanonicalHost do
2
+ let(:app_response) { [200, { 'Content-Type' => 'text/plain' }, %w(OK)] }
3
+ let(:inner_app) { ->(env) { response } }
4
+
5
+ before do
6
+ allow(inner_app)
7
+ .to receive(:call)
8
+ .with(env)
9
+ .and_return(app_response)
10
+ end
6
11
 
7
12
  def build_app(host=nil, options={}, inner_app=inner_app(), &block)
8
13
  Rack::Builder.new do
@@ -19,8 +24,8 @@ describe Rack::CanonicalHost do
19
24
  it { should_not be_redirect }
20
25
 
21
26
  it 'calls the inner app' do
22
- expect(inner_app).to receive(:call).with(env).and_return(response)
23
- subject
27
+ expect(inner_app).to receive(:call).with(env)
28
+ call_app
24
29
  end
25
30
  end
26
31
  end
@@ -29,12 +34,14 @@ describe Rack::CanonicalHost do
29
34
  context 'with a request to a non-matching host' do
30
35
  let(:url) { 'http://www.example.com/full/path' }
31
36
 
32
- it { should be_redirect.via(301).to('http://example.com/full/path') }
37
+ it { should redirect_to('http://example.com/full/path') }
33
38
 
34
39
  it 'does not call the inner app' do
35
40
  expect(inner_app).to_not receive(:call)
36
- subject
41
+ call_app
37
42
  end
43
+
44
+ it { expect(response).to_not have_header('Cache-Control') }
38
45
  end
39
46
  end
40
47
 
@@ -44,119 +51,225 @@ describe Rack::CanonicalHost do
44
51
  end
45
52
 
46
53
  context '#call' do
47
- let(:env) { Rack::MockRequest.env_for(url) }
54
+ let(:headers) { {} }
48
55
 
49
- subject { app.call(env) }
56
+ let(:app) { build_app('example.com') }
57
+ let(:env) { Rack::MockRequest.env_for(url, headers) }
50
58
 
51
- context 'without a host' do
52
- let(:app) { build_app(nil) }
59
+ def call_app
60
+ app.call(env)
61
+ end
53
62
 
54
- include_context 'a matching request'
63
+ subject(:response) { call_app }
64
+
65
+ include_context 'a matching request'
66
+ include_context 'a non-matching request'
67
+
68
+ context 'when the request has a pipe in the URL' do
69
+ let(:url) { 'https://example.com/full/path?value=withPIPE' }
70
+
71
+ before { env['QUERY_STRING'].sub!('PIPE', '|') }
72
+
73
+ it { expect { call_app }.to_not raise_error }
55
74
  end
56
75
 
57
- context 'with an X-Forwarded-Host' do
76
+ context 'when the request has JavaScript in the URL' do
77
+ let(:url) { 'http://www.example.com/full/path' }
78
+
79
+ let(:headers) {
80
+ { 'QUERY_STRING' => '"><script>alert(73541);</script>' }
81
+ }
82
+
58
83
  let(:app) { build_app('example.com') }
59
- let(:url) { 'http://proxied.url/full/path' }
84
+
85
+ it 'escapes the JavaScript' do
86
+ expect(response)
87
+ .to redirect_to('http://example.com/full/path?%22%3E%3Cscript%3Ealert(73541)%3B%3C/script%3E')
88
+ end
89
+ end
90
+
91
+ context 'with an X-Forwarded-Host' do
92
+ let(:url) { 'http://proxy.test/full/path' }
60
93
 
61
94
  context 'which matches the canonical host' do
62
- let(:env) { Rack::MockRequest.env_for(url, 'HTTP_X_FORWARDED_HOST' => 'example.com:80') }
95
+ let(:headers) { { 'HTTP_X_FORWARDED_HOST' => 'example.com:80' } }
63
96
 
64
97
  include_context 'a matching request'
65
98
  end
66
99
 
67
100
  context 'which does not match the canonical host' do
68
- let(:env) { Rack::MockRequest.env_for(url, 'HTTP_X_FORWARDED_HOST' => 'www.example.com:80') }
101
+ let(:headers) { { 'HTTP_X_FORWARDED_HOST' => 'www.example.com:80' } }
69
102
 
70
103
  include_context 'a non-matching request'
71
104
  end
72
105
  end
73
106
 
74
- context 'without any options' do
75
- let(:app) { build_app('example.com') }
107
+ context 'without a host' do
108
+ let(:app) { build_app(nil) }
109
+
110
+ include_context 'a matching request'
111
+ end
112
+
113
+ context 'with :ignore option' do
114
+ context 'with lambda/proc' do
115
+ let(:app) {
116
+ build_app(
117
+ 'example.com',
118
+ ignore: ->(uri) { uri.host == 'example.net' }
119
+ )
120
+ }
76
121
 
77
- include_context 'matching and non-matching requests'
122
+ include_context 'a matching request'
123
+ include_context 'a non-matching request'
78
124
 
79
- context 'when the request has a pipe in the URL' do
80
- let(:url) { 'https://example.com/full/path?value=withPIPE' }
125
+ context 'with a request to an ignored host' do
126
+ let(:url) { 'http://example.net/full/path' }
81
127
 
82
- before { env['QUERY_STRING'].sub!('PIPE', '|') }
128
+ it { should_not be_redirect }
83
129
 
84
- it { expect { subject }.to_not raise_error }
130
+ it 'calls the inner app' do
131
+ expect(inner_app).to receive(:call).with(env)
132
+ call_app
133
+ end
134
+ end
85
135
  end
86
- end
87
136
 
88
- context 'with :ignore option' do
89
- let(:app) { build_app('example.com', :ignore => 'example.net') }
137
+ context 'with string' do
138
+ let(:app) { build_app('example.com', ignore: 'example.net') }
90
139
 
91
- include_context 'matching and non-matching requests'
140
+ include_context 'a matching request'
141
+ include_context 'a non-matching request'
92
142
 
93
- context 'with a request to an ignored host' do
94
- let(:url) { 'http://example.net/full/path' }
143
+ context 'with a request to an ignored host' do
144
+ let(:url) { 'http://example.net/full/path' }
95
145
 
96
- it { should_not be_redirect }
146
+ it { should_not be_redirect }
97
147
 
98
- it 'calls the inner app' do
99
- expect(inner_app).to receive(:call).with(env).and_return(response)
100
- subject
148
+ it 'calls the inner app' do
149
+ expect(inner_app).to receive(:call).with(env)
150
+ call_app
151
+ end
152
+ end
153
+ end
154
+
155
+ context 'with regular expression' do
156
+ let(:app) { build_app('example.com', ignore: /ex.*\.net/) }
157
+
158
+ include_context 'a matching request'
159
+ include_context 'a non-matching request'
160
+
161
+ context 'with a request to an ignored host' do
162
+ let(:url) { 'http://example.net/full/path' }
163
+
164
+ it { should_not be_redirect }
165
+
166
+ it 'calls the inner app' do
167
+ expect(inner_app).to receive(:call).with(env)
168
+ call_app
169
+ end
101
170
  end
102
171
  end
103
172
  end
104
173
 
105
174
  context 'with :if option' do
175
+ context 'with a lambda/proc' do
176
+ let(:app) {
177
+ build_app(
178
+ 'www.example.com',
179
+ if: ->(uri) { uri.host == 'example.com' }
180
+ )
181
+ }
182
+
183
+ context 'with a request to a matching host' do
184
+ let(:url) { 'http://example.com/full/path' }
185
+
186
+ it { should redirect_to('http://www.example.com/full/path') }
187
+ end
106
188
 
107
- let(:app) { build_app('example.com', :if => 'www.example.net') }
189
+ context 'with a request to a non-matching host' do
190
+ let(:url) { 'http://api.example.com/full/path' }
108
191
 
109
- context 'with a request to a :if matching host' do
110
- let(:url) { 'http://www.example.net/full/path' }
111
- it { should be_redirect.to('http://example.com/full/path') }
192
+ it { should_not be_redirect }
193
+ end
112
194
  end
113
195
 
114
- context 'with a request to a :if non-matching host' do
115
- let(:url) { 'http://www.sexample.net/full/path' }
116
- it { should_not be_redirect }
196
+ context 'with string' do
197
+ let(:app) { build_app('www.example.com', if: 'example.com') }
198
+
199
+ context 'with a request to a matching host' do
200
+ let(:url) { 'http://example.com/full/path' }
201
+
202
+ it { should redirect_to('http://www.example.com/full/path') }
203
+ end
204
+
205
+ context 'with a request to a non-matching host' do
206
+ let(:url) { 'http://api.example.com/full/path' }
207
+
208
+ it { should_not be_redirect }
209
+ end
117
210
  end
118
211
 
212
+ context 'with a regular expression' do
213
+ let(:app) { build_app('example.com', if: '.*\.example\.com') }
214
+
215
+ context 'with a request to a matching host' do
216
+ let(:url) { 'http://www.example.com/full/path' }
217
+
218
+ it { should_not redirect_to('http://example.com/full/path') }
219
+ end
220
+
221
+ context 'with a request to a non-matching host' do
222
+ let(:url) { 'http://www.example.net/full/path' }
223
+
224
+ it { should_not be_redirect }
225
+ end
226
+ end
119
227
  end
120
228
 
121
- context 'with :if and regexp as an option' do
229
+ context 'with a :cache_control option' do
230
+ let(:url) { 'http://subdomain.example.net/full/path' }
231
+
232
+ context 'with a max-age value' do
233
+ let(:app) {
234
+ build_app('example.com', cache_control: 'max-age=3600')
235
+ }
236
+
237
+ it {
238
+ expect(response).to have_header('Cache-Control').with('max-age=3600')
239
+ }
240
+ end
122
241
 
123
- let(:app) { build_app('example.com', :if => /.*\.example\.net/) }
242
+ context 'with a no-cache value' do
243
+ let(:app) { build_app('example.com', cache_control: 'no-cache') }
124
244
 
125
- context 'with a request to a :if matching host' do
126
- let(:url) { 'http://subdomain.example.net/full/path' }
127
- it { should be_redirect.to('http://example.com/full/path') }
245
+ it { expect(subject).to have_header('Cache-Control').with('no-cache') }
128
246
  end
129
247
 
130
- context 'with a request to a :if non-matching host' do
131
- let(:url) { 'http://example.net/full/path' }
132
- it { should_not be_redirect }
248
+ context 'with a false value' do
249
+ let(:app) { build_app('example.com', cache_control: false) }
250
+
251
+ it { expect(subject).to_not have_header('Cache-Control') }
133
252
  end
134
253
 
254
+ context 'with a nil value' do
255
+ let(:app) { build_app('example.com', cache_control: false) }
256
+
257
+ it { expect(subject).to_not have_header('Cache-Control') }
258
+ end
135
259
  end
136
260
 
137
261
  context 'with a block' do
138
262
  let(:app) { build_app { 'example.com' } }
139
263
 
140
- include_context 'matching and non-matching requests'
264
+ include_context 'a matching request'
265
+ include_context 'a non-matching request'
141
266
 
142
267
  context 'that returns nil' do
143
268
  let(:app) { build_app('example.com') { nil } }
144
269
 
145
- include_context 'matching and non-matching requests'
146
- end
147
- end
148
-
149
- context 'with URL containing JavaScript XSS' do
150
- let(:url) { 'http://subdomain.example.net/full/path' }
151
- let(:env) do
152
- Rack::MockRequest.env_for(url).tap do |env|
153
- env['QUERY_STRING'] = '"><script>alert(73541);</script>'
154
- end
270
+ include_context 'a matching request'
271
+ include_context 'a non-matching request'
155
272
  end
156
-
157
- let(:app) { build_app('example.com') }
158
-
159
- it { should be_redirect.to('http://example.com/full/path?%22%3E%3Cscript%3Ealert(73541)%3B%3C/script%3E') }
160
273
  end
161
274
  end
162
275
  end
@@ -0,0 +1,56 @@
1
+ module HaveHeader
2
+ class Matcher
3
+ def initialize(expected_header)
4
+ self.expected_header = expected_header
5
+ end
6
+
7
+ def matches?(response)
8
+ _, self.actual_headers, _ = response
9
+
10
+ if expected_value
11
+ actual_header == expected_value
12
+ else
13
+ actual_header
14
+ end
15
+ end
16
+
17
+ def with(expected_value)
18
+ self.expected_value = expected_value
19
+ self
20
+ end
21
+
22
+ def description
23
+ sentence = "have header #{expected_header.inspect}"
24
+ sentence << " with #{expected_value.inspect}" if expected_value
25
+ sentence
26
+ end
27
+
28
+ def failure_message
29
+ "Expected response to #{description}"
30
+ end
31
+
32
+ def failure_message_when_negated
33
+ "Did not expect response to #{description}"
34
+ end
35
+
36
+ protected
37
+
38
+ attr_accessor :actual_headers
39
+ attr_accessor :expected_header
40
+ attr_accessor :expected_value
41
+
42
+ private
43
+
44
+ def actual_header
45
+ actual_headers[expected_header]
46
+ end
47
+ end
48
+
49
+ def have_header(name)
50
+ Matcher.new(name)
51
+ end
52
+ end
53
+
54
+ RSpec.configure do |config|
55
+ config.include HaveHeader
56
+ end
@@ -1,36 +1,30 @@
1
- module BeRedirect
1
+ module RedirectTo
2
2
  class Matcher
3
- attr :expected_status_code
4
- attr :expected_location
5
- attr :actual_status_code
6
- attr :actual_location
3
+ def initialize(expected_location)
4
+ self.expected_location = expected_location
5
+ self.expected_status_code = STATUS
6
+ end
7
7
 
8
8
  def matches?(response)
9
- @actual_status_code, headers, _ = response
10
- @actual_location = headers['Location']
9
+ self.actual_status_code, self.actual_headers, _ = response
11
10
 
12
11
  status_code_matches? && location_matches?
13
12
  end
14
13
 
15
14
  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
15
+ self.expected_status_code = expected_status_code
22
16
  self
23
17
  end
24
18
 
25
19
  def description
26
20
  if expected_status_code && expected_location
27
- "redirect via #{expected_status_code} to #{expected_location.inspect}"
21
+ "redirect to #{expected_location.inspect} via #{expected_status_code}"
28
22
  elsif expected_status_code
29
23
  "redirect via #{expected_status_code}"
30
24
  elsif expected_location
31
25
  "redirect to #{expected_location.inspect}"
32
26
  else
33
- "be a redirect"
27
+ 'be a redirect'
34
28
  end
35
29
  end
36
30
 
@@ -42,6 +36,22 @@ module BeRedirect
42
36
  "Did not expect response to #{description}"
43
37
  end
44
38
 
39
+ protected
40
+
41
+ attr_accessor :actual_headers
42
+ attr_accessor :actual_status_code
43
+ attr_accessor :expected_location
44
+ attr_accessor :expected_status_code
45
+
46
+ private
47
+
48
+ LOCATION = 'Location'
49
+ STATUS = 301
50
+
51
+ def actual_location
52
+ actual_headers[LOCATION]
53
+ end
54
+
45
55
  def status_code_matches?
46
56
  if expected_status_code
47
57
  actual_status_code == expected_status_code
@@ -49,19 +59,23 @@ module BeRedirect
49
59
  actual_status_code.to_s =~ /^30[1237]$/
50
60
  end
51
61
  end
52
- private :status_code_matches?
53
62
 
54
63
  def location_matches?
55
- !expected_location || (expected_location == actual_location)
64
+ if expected_location
65
+ expected_location == actual_location
66
+ end
56
67
  end
57
- private :location_matches?
68
+ end
69
+
70
+ def redirect_to(location)
71
+ Matcher.new(location)
58
72
  end
59
73
 
60
74
  def be_redirect
61
- Matcher.new
75
+ Matcher.new(nil).via(nil)
62
76
  end
63
77
  end
64
78
 
65
79
  RSpec.configure do |config|
66
- config.include(BeRedirect)
80
+ config.include RedirectTo
67
81
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-canonical-host
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Hunt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-28 00:00:00.000000000 Z
11
+ date: 2021-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -50,6 +50,20 @@ dependencies:
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
52
  version: '3'
53
+ - !ruby/object:Gem::Dependency
54
+ name: appraisal
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '2.2'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '2.2'
53
67
  - !ruby/object:Gem::Dependency
54
68
  name: rake
55
69
  requirement: !ruby/object:Gem::Requirement
@@ -84,14 +98,20 @@ executables: []
84
98
  extensions: []
85
99
  extra_rdoc_files: []
86
100
  files:
101
+ - ".github/workflows/ci.yml"
87
102
  - ".gitignore"
88
103
  - ".rspec"
89
- - ".travis.yml"
104
+ - Appraisals
90
105
  - CHANGELOG.md
91
106
  - Gemfile
92
107
  - LICENSE
93
108
  - README.md
94
109
  - Rakefile
110
+ - gemfiles/rack_1.5.gemfile
111
+ - gemfiles/rack_1.6.gemfile
112
+ - gemfiles/rack_2.0.gemfile
113
+ - gemfiles/rack_2.1.gemfile
114
+ - gemfiles/rack_2.2.gemfile
95
115
  - lib/rack-canonical-host.rb
96
116
  - lib/rack/canonical_host.rb
97
117
  - lib/rack/canonical_host/redirect.rb
@@ -99,8 +119,9 @@ files:
99
119
  - rack-canonical-host.gemspec
100
120
  - spec/rack/canonical_host_spec.rb
101
121
  - spec/spec_helper.rb
102
- - spec/support/matchers/be_redirect.rb
103
- homepage: http://github.com/tylerhunt/rack-canonical-host
122
+ - spec/support/matchers/have_header.rb
123
+ - spec/support/matchers/redirect_to.rb
124
+ homepage: https://github.com/tylerhunt/rack-canonical-host
104
125
  licenses: []
105
126
  metadata: {}
106
127
  post_install_message:
@@ -118,12 +139,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
139
  - !ruby/object:Gem::Version
119
140
  version: '0'
120
141
  requirements: []
121
- rubyforge_project:
122
- rubygems_version: 2.4.5
142
+ rubygems_version: 3.0.3
123
143
  signing_key:
124
144
  specification_version: 4
125
145
  summary: Rack middleware for defining a canonical host name.
126
146
  test_files:
127
147
  - spec/rack/canonical_host_spec.rb
128
148
  - spec/spec_helper.rb
129
- - spec/support/matchers/be_redirect.rb
149
+ - spec/support/matchers/have_header.rb
150
+ - spec/support/matchers/redirect_to.rb
data/.travis.yml DELETED
@@ -1,9 +0,0 @@
1
- language: ruby
2
- cache: bundler
3
- script: bundle exec rspec
4
- sudo: false
5
- rvm:
6
- - 1.9.3
7
- - 2.0.0
8
- - 2.1.0
9
- - 2.1.1