hanami-router 0.0.0 → 0.6.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +101 -0
- data/LICENSE.md +22 -0
- data/README.md +667 -9
- data/hanami-router.gemspec +18 -12
- data/lib/hanami-router.rb +1 -0
- data/lib/hanami/router.rb +1160 -3
- data/lib/hanami/router/version.rb +3 -2
- data/lib/hanami/routing/endpoint.rb +151 -0
- data/lib/hanami/routing/endpoint_resolver.rb +225 -0
- data/lib/hanami/routing/error.rb +7 -0
- data/lib/hanami/routing/force_ssl.rb +209 -0
- data/lib/hanami/routing/http_router.rb +187 -0
- data/lib/hanami/routing/namespace.rb +92 -0
- data/lib/hanami/routing/parsers.rb +71 -0
- data/lib/hanami/routing/parsing/json_parser.rb +28 -0
- data/lib/hanami/routing/parsing/parser.rb +58 -0
- data/lib/hanami/routing/recognized_route.rb +153 -0
- data/lib/hanami/routing/resource.rb +116 -0
- data/lib/hanami/routing/resource/action.rb +387 -0
- data/lib/hanami/routing/resource/nested.rb +39 -0
- data/lib/hanami/routing/resource/options.rb +74 -0
- data/lib/hanami/routing/resources.rb +48 -0
- data/lib/hanami/routing/resources/action.rb +150 -0
- data/lib/hanami/routing/route.rb +62 -0
- data/lib/hanami/routing/routes_inspector.rb +215 -0
- metadata +94 -16
- data/.gitignore +0 -9
- data/Gemfile +0 -4
- data/Rakefile +0 -2
- data/bin/console +0 -14
- data/bin/setup +0 -8
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'hanami/routing/error'
|
3
|
+
require 'hanami/utils/class'
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
module Routing
|
7
|
+
# Endpoint not found
|
8
|
+
# This is raised when the router fails to load an endpoint at the runtime.
|
9
|
+
#
|
10
|
+
# @since 0.1.0
|
11
|
+
class EndpointNotFound < Hanami::Routing::Error
|
12
|
+
end
|
13
|
+
|
14
|
+
# Routing endpoint
|
15
|
+
# This is the object that responds to an HTTP request made against a certain
|
16
|
+
# path.
|
17
|
+
#
|
18
|
+
# The router will use this class for:
|
19
|
+
#
|
20
|
+
# * Procs and any Rack compatible object (respond to #call)
|
21
|
+
#
|
22
|
+
# @since 0.1.0
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# require 'hanami/router'
|
28
|
+
#
|
29
|
+
# Hanami::Router.new do
|
30
|
+
# get '/proc', to: ->(env) { [200, {}, ['This will use Hanami::Routing::Endpoint']] }
|
31
|
+
# get '/rack-app', to: RackApp.new
|
32
|
+
# end
|
33
|
+
class Endpoint < SimpleDelegator
|
34
|
+
# @since 0.2.0
|
35
|
+
def inspect
|
36
|
+
case __getobj__
|
37
|
+
when Proc
|
38
|
+
source, line = __getobj__.source_location
|
39
|
+
lambda_inspector = " (lambda)" if __getobj__.lambda?
|
40
|
+
|
41
|
+
"#<Proc@#{ ::File.expand_path(source) }:#{ line }#{ lambda_inspector }>"
|
42
|
+
when Class
|
43
|
+
__getobj__
|
44
|
+
else
|
45
|
+
"#<#{ __getobj__.class }>"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Routing endpoint
|
51
|
+
# This is the object that responds to an HTTP request made against a certain
|
52
|
+
# path.
|
53
|
+
#
|
54
|
+
# The router will use this class for:
|
55
|
+
#
|
56
|
+
# * Classes
|
57
|
+
# * Hanami::Action endpoints referenced as a class
|
58
|
+
# * Hanami::Action endpoints referenced a string
|
59
|
+
# * RESTful resource(s)
|
60
|
+
#
|
61
|
+
# @since 0.1.0
|
62
|
+
#
|
63
|
+
# @api private
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# require 'hanami/router'
|
67
|
+
#
|
68
|
+
# Hanami::Router.new do
|
69
|
+
# get '/class', to: RackMiddleware
|
70
|
+
# get '/hanami-action-class', to: Dashboard::Index
|
71
|
+
# get '/hanami-action-string', to: 'dashboard#index'
|
72
|
+
#
|
73
|
+
# resource 'identity'
|
74
|
+
# resources 'articles'
|
75
|
+
# end
|
76
|
+
class ClassEndpoint < Endpoint
|
77
|
+
# Rack interface
|
78
|
+
#
|
79
|
+
# @since 0.1.0
|
80
|
+
def call(env)
|
81
|
+
__getobj__.new.call(env)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Routing endpoint
|
86
|
+
# This is the object that responds to an HTTP request made against a certain
|
87
|
+
# path.
|
88
|
+
#
|
89
|
+
# The router will use this class for the same use cases of `ClassEndpoint`,
|
90
|
+
# but when the target class can't be found, instead of raise a `LoadError`
|
91
|
+
# we reference in a lazy endpoint.
|
92
|
+
#
|
93
|
+
# For each incoming HTTP request, it will look for the referenced class,
|
94
|
+
# then it will instantiate and invoke #call on the object.
|
95
|
+
#
|
96
|
+
# This behavior is required to solve a chicken-egg situation when we try
|
97
|
+
# to load the router first and then the application with all its endpoints.
|
98
|
+
#
|
99
|
+
# @since 0.1.0
|
100
|
+
#
|
101
|
+
# @api private
|
102
|
+
#
|
103
|
+
# @see Hanami::Routing::ClassEndpoint
|
104
|
+
class LazyEndpoint < Endpoint
|
105
|
+
# Initialize the lazy endpoint
|
106
|
+
#
|
107
|
+
# @since 0.1.0
|
108
|
+
def initialize(name, namespace)
|
109
|
+
@name, @namespace = name, namespace
|
110
|
+
end
|
111
|
+
|
112
|
+
# Rack interface
|
113
|
+
#
|
114
|
+
# @raise [EndpointNotFound] when the endpoint can't be found.
|
115
|
+
#
|
116
|
+
# @since 0.1.0
|
117
|
+
def call(env)
|
118
|
+
obj.call(env)
|
119
|
+
end
|
120
|
+
|
121
|
+
# @since 0.2.0
|
122
|
+
def inspect
|
123
|
+
# TODO review this implementation once the namespace feature will be
|
124
|
+
# cleaned up.
|
125
|
+
result = klass rescue nil
|
126
|
+
|
127
|
+
if result.nil?
|
128
|
+
result = @name
|
129
|
+
result = "#{ @namespace }::#{ result }" if @namespace != Object
|
130
|
+
end
|
131
|
+
|
132
|
+
result
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
# @since 0.1.0
|
137
|
+
# @api private
|
138
|
+
def obj
|
139
|
+
klass.new
|
140
|
+
end
|
141
|
+
|
142
|
+
# @since 0.2.0
|
143
|
+
# @api private
|
144
|
+
def klass
|
145
|
+
Utils::Class.load!(@name, @namespace)
|
146
|
+
rescue NameError => e
|
147
|
+
raise EndpointNotFound.new(e.message)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
require 'hanami/utils/string'
|
2
|
+
require 'hanami/utils/class'
|
3
|
+
require 'hanami/routing/endpoint'
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
module Routing
|
7
|
+
# Resolve duck-typed endpoints
|
8
|
+
#
|
9
|
+
# @since 0.1.0
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class EndpointResolver
|
13
|
+
# @since 0.2.0
|
14
|
+
# @api private
|
15
|
+
NAMING_PATTERN = '%{controller}::%{action}'.freeze
|
16
|
+
|
17
|
+
# Default separator for controller and action.
|
18
|
+
# A different separator can be passed to #initialize with the `:separator` option.
|
19
|
+
#
|
20
|
+
# @see #initialize
|
21
|
+
# @see #resolve
|
22
|
+
#
|
23
|
+
# @since 0.1.0
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# require 'hanami/router'
|
27
|
+
#
|
28
|
+
# router = Hanami::Router.new do
|
29
|
+
# get '/', to: 'articles#show'
|
30
|
+
# end
|
31
|
+
ACTION_SEPARATOR = '#'.freeze
|
32
|
+
|
33
|
+
attr_reader :action_separator
|
34
|
+
|
35
|
+
# Initialize an endpoint resolver
|
36
|
+
#
|
37
|
+
# @param options [Hash] the options used to customize lookup behavior
|
38
|
+
#
|
39
|
+
# @option options [Class] :endpoint the endpoint class that is returned
|
40
|
+
# by `#resolve`. (defaults to `Hanami::Routing::Endpoint`)
|
41
|
+
#
|
42
|
+
# @option options [Class,Module] :namespace the Ruby namespace where to
|
43
|
+
# lookup for controllers and actions. (defaults to `Object`)
|
44
|
+
#
|
45
|
+
# @option options [String] :pattern the string to interpolate in order
|
46
|
+
# to return an action name. This string SHOULD contain
|
47
|
+
# <tt>'%{controller}'</tt> and <tt>'%{action}'</tt>, all the other keys
|
48
|
+
# will be ignored.
|
49
|
+
# See the examples below.
|
50
|
+
#
|
51
|
+
# @option options [String] :action_separator the sepatator between controller and
|
52
|
+
# action name. (defaults to `ACTION_SEPARATOR`)
|
53
|
+
#
|
54
|
+
# @return [Hanami::Routing::EndpointResolver] self
|
55
|
+
#
|
56
|
+
# @since 0.1.0
|
57
|
+
#
|
58
|
+
# @example Specify custom endpoint class
|
59
|
+
# require 'hanami/router'
|
60
|
+
#
|
61
|
+
# resolver = Hanami::Routing::EndpointResolver.new(endpoint: CustomEndpoint)
|
62
|
+
# router = Hanami::Router.new(resolver: resolver)
|
63
|
+
#
|
64
|
+
# router.get('/', to: endpoint).dest # => #<CustomEndpoint:0x007f97f3359570 ...>
|
65
|
+
#
|
66
|
+
# @example Specify custom Ruby namespace
|
67
|
+
# require 'hanami/router'
|
68
|
+
#
|
69
|
+
# resolver = Hanami::Routing::EndpointResolver.new(namespace: MyApp)
|
70
|
+
# router = Hanami::Router.new(resolver: resolver)
|
71
|
+
#
|
72
|
+
# router.get('/', to: 'articles#show')
|
73
|
+
# # => Will look for: MyApp::Articles::Show
|
74
|
+
#
|
75
|
+
#
|
76
|
+
#
|
77
|
+
# @example Specify custom pattern
|
78
|
+
# require 'hanami/router'
|
79
|
+
#
|
80
|
+
# resolver = Hanami::Routing::EndpointResolver.new(pattern: '%{controller}Controller::%{action}')
|
81
|
+
# router = Hanami::Router.new(resolver: resolver)
|
82
|
+
#
|
83
|
+
# router.get('/', to: 'articles#show')
|
84
|
+
# # => Will look for: ArticlesController::Show
|
85
|
+
#
|
86
|
+
#
|
87
|
+
#
|
88
|
+
# @example Specify custom controller-action separator
|
89
|
+
# require 'hanami/router'
|
90
|
+
#
|
91
|
+
# resolver = Hanami::Routing::EndpointResolver.new(separator: '@')
|
92
|
+
# router = Hanami::Router.new(resolver: resolver)
|
93
|
+
#
|
94
|
+
# router.get('/', to: 'articles@show')
|
95
|
+
# # => Will look for: Articles::Show
|
96
|
+
def initialize(options = {})
|
97
|
+
@endpoint_class = options[:endpoint] || Endpoint
|
98
|
+
@namespace = options[:namespace] || Object
|
99
|
+
@action_separator = options[:action_separator] || ACTION_SEPARATOR
|
100
|
+
@pattern = options[:pattern] || NAMING_PATTERN
|
101
|
+
end
|
102
|
+
|
103
|
+
# Resolve the given set of HTTP verb, path, endpoint and options.
|
104
|
+
# If it fails to resolve, it will mount the default endpoint to the given
|
105
|
+
# path, which returns an 404 (Not Found).
|
106
|
+
#
|
107
|
+
# @param options [Hash] the options required to resolve the endpoint
|
108
|
+
#
|
109
|
+
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
110
|
+
# @option options [String] :namespace an optional routing namespace
|
111
|
+
#
|
112
|
+
# @return [Endpoint] this may vary according to the :endpoint option
|
113
|
+
# passed to #initialize
|
114
|
+
#
|
115
|
+
# @since 0.1.0
|
116
|
+
#
|
117
|
+
# @see #initialize
|
118
|
+
# @see #find
|
119
|
+
#
|
120
|
+
# @example Resolve to a Proc
|
121
|
+
# require 'hanami/router'
|
122
|
+
#
|
123
|
+
# router = Hanami::Router.new
|
124
|
+
# router.get '/', to: ->(env) { [200, {}, ['Hi!']] }
|
125
|
+
#
|
126
|
+
# @example Resolve to a class
|
127
|
+
# require 'hanami/router'
|
128
|
+
#
|
129
|
+
# router = Hanami::Router.new
|
130
|
+
# router.get '/', to: RackMiddleware
|
131
|
+
#
|
132
|
+
# @example Resolve to a Rack compatible object (respond to #call)
|
133
|
+
# require 'hanami/router'
|
134
|
+
#
|
135
|
+
# router = Hanami::Router.new
|
136
|
+
# router.get '/', to: AnotherMiddleware.new
|
137
|
+
#
|
138
|
+
# @example Resolve to a Hanami::Action from a string (see Hanami::Controller framework)
|
139
|
+
# require 'hanami/router'
|
140
|
+
#
|
141
|
+
# router = Hanami::Router.new
|
142
|
+
# router.get '/', to: 'articles#show'
|
143
|
+
#
|
144
|
+
# @example Resolve to a Hanami::Action (see Hanami::Controller framework)
|
145
|
+
# require 'hanami/router'
|
146
|
+
#
|
147
|
+
# router = Hanami::Router.new
|
148
|
+
# router.get '/', to: Articles::Show
|
149
|
+
#
|
150
|
+
# @example Resolve a redirect with a namespace
|
151
|
+
# require 'hanami/router'
|
152
|
+
#
|
153
|
+
# router = Hanami::Router.new
|
154
|
+
# router.namespace 'users' do
|
155
|
+
# get '/home', to: ->(env) { ... }
|
156
|
+
# redirect '/dashboard', to: '/home'
|
157
|
+
# end
|
158
|
+
#
|
159
|
+
# # GET /users/dashboard => 301 Location: "/users/home"
|
160
|
+
def resolve(options, &endpoint)
|
161
|
+
result = endpoint || find(options)
|
162
|
+
resolve_callable(result) || resolve_matchable(result) || default
|
163
|
+
end
|
164
|
+
|
165
|
+
# Finds a path from the given options.
|
166
|
+
#
|
167
|
+
# @param options [Hash] the path description
|
168
|
+
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
169
|
+
# @option options [String] :namespace an optional namespace
|
170
|
+
#
|
171
|
+
# @since 0.1.0
|
172
|
+
#
|
173
|
+
# @return [Object]
|
174
|
+
def find(options)
|
175
|
+
options[:to]
|
176
|
+
end
|
177
|
+
|
178
|
+
protected
|
179
|
+
def default
|
180
|
+
@endpoint_class.new(
|
181
|
+
->(env) { [404, {'X-Cascade' => 'pass'}, 'Not Found'] }
|
182
|
+
)
|
183
|
+
end
|
184
|
+
|
185
|
+
def constantize(string)
|
186
|
+
klass = Utils::Class.load!(string, @namespace)
|
187
|
+
if klass.respond_to?(:call)
|
188
|
+
Endpoint.new(klass)
|
189
|
+
else
|
190
|
+
ClassEndpoint.new(klass)
|
191
|
+
end
|
192
|
+
rescue NameError
|
193
|
+
LazyEndpoint.new(string, @namespace)
|
194
|
+
end
|
195
|
+
|
196
|
+
def classify(string)
|
197
|
+
Utils::String.new(string).classify
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
def resolve_callable(callable)
|
202
|
+
if callable.respond_to?(:call)
|
203
|
+
@endpoint_class.new(callable)
|
204
|
+
elsif callable.is_a?(Class) && callable.instance_methods.include?(:call)
|
205
|
+
@endpoint_class.new(callable.new)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def resolve_matchable(matchable)
|
210
|
+
if matchable.respond_to?(:match)
|
211
|
+
constantize(
|
212
|
+
resolve_action(matchable) || classify(matchable)
|
213
|
+
)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def resolve_action(string)
|
218
|
+
if string.match(action_separator)
|
219
|
+
controller, action = string.split(action_separator).map {|token| classify(token) }
|
220
|
+
@pattern % {controller: controller, action: action}
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'rack/request'
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module Routing
|
5
|
+
# Force ssl
|
6
|
+
#
|
7
|
+
# Redirect response to the secure equivalent resource (https)
|
8
|
+
#
|
9
|
+
# @since 0.4.1
|
10
|
+
# @api private
|
11
|
+
class ForceSsl
|
12
|
+
# Https scheme
|
13
|
+
#
|
14
|
+
# @since 0.4.1
|
15
|
+
# @api private
|
16
|
+
SSL_SCHEME = 'https'.freeze
|
17
|
+
|
18
|
+
# @since 0.4.1
|
19
|
+
# @api private
|
20
|
+
HTTPS = 'HTTPS'.freeze
|
21
|
+
|
22
|
+
# @since 0.4.1
|
23
|
+
# @api private
|
24
|
+
ON = 'on'.freeze
|
25
|
+
|
26
|
+
# Location header
|
27
|
+
#
|
28
|
+
# @since 0.4.1
|
29
|
+
# @api private
|
30
|
+
LOCATION_HEADER = 'Location'.freeze
|
31
|
+
|
32
|
+
# Default http port
|
33
|
+
#
|
34
|
+
# @since 0.4.1
|
35
|
+
# @api private
|
36
|
+
DEFAULT_HTTP_PORT = 80
|
37
|
+
|
38
|
+
# Default ssl port
|
39
|
+
#
|
40
|
+
# @since 0.4.1
|
41
|
+
# @api private
|
42
|
+
DEFAULT_SSL_PORT = 443
|
43
|
+
|
44
|
+
# Moved Permanently http code
|
45
|
+
#
|
46
|
+
# @since 0.4.1
|
47
|
+
# @api private
|
48
|
+
MOVED_PERMANENTLY_HTTP_CODE = 301
|
49
|
+
|
50
|
+
# Temporary Redirect http code
|
51
|
+
#
|
52
|
+
# @since 0.4.1
|
53
|
+
# @api private
|
54
|
+
TEMPORARY_REDIRECT_HTTP_CODE = 307
|
55
|
+
|
56
|
+
# @since 0.4.1
|
57
|
+
# @api private
|
58
|
+
HTTP_X_FORWARDED_SSL = 'HTTP_X_FORWARDED_SSL'.freeze
|
59
|
+
|
60
|
+
# @since 0.4.1
|
61
|
+
# @api private
|
62
|
+
HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'.freeze
|
63
|
+
|
64
|
+
# @since 0.4.1
|
65
|
+
# @api private
|
66
|
+
HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'.freeze
|
67
|
+
|
68
|
+
# @since 0.4.1
|
69
|
+
# @api private
|
70
|
+
HTTP_X_FORWARDED_PROTO_SEPARATOR = ','.freeze
|
71
|
+
|
72
|
+
# @since 0.4.1
|
73
|
+
# @api private
|
74
|
+
RACK_URL_SCHEME = 'rack.url_scheme'.freeze
|
75
|
+
|
76
|
+
# @since 0.4.1
|
77
|
+
# @api private
|
78
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
79
|
+
|
80
|
+
# @since 0.4.1
|
81
|
+
# @api private
|
82
|
+
IDEMPOTENT_METHODS = ['GET', 'HEAD'].freeze
|
83
|
+
|
84
|
+
EMPTY_BODY = [].freeze
|
85
|
+
|
86
|
+
# Initialize ForceSsl.
|
87
|
+
#
|
88
|
+
# @param active [Boolean] activate redirection to SSL
|
89
|
+
# @param options [Hash] set of options
|
90
|
+
# @option options [String] :host
|
91
|
+
# @option options [Integer] :port
|
92
|
+
#
|
93
|
+
# @since 0.4.1
|
94
|
+
# @api private
|
95
|
+
def initialize(active, options = {})
|
96
|
+
@active = active
|
97
|
+
@host = options[:host]
|
98
|
+
@port = options[:port]
|
99
|
+
|
100
|
+
_redefine_call
|
101
|
+
end
|
102
|
+
|
103
|
+
# Set 301 status and Location header if this feature is activated.
|
104
|
+
#
|
105
|
+
# @param env [Hash] a Rack env instance
|
106
|
+
#
|
107
|
+
# @return [Array]
|
108
|
+
#
|
109
|
+
# @see Hanami::Routing::HttpRouter#call
|
110
|
+
#
|
111
|
+
# @since 0.4.1
|
112
|
+
# @api private
|
113
|
+
def call(env)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Check if router has to force the response with ssl
|
117
|
+
#
|
118
|
+
# @return [Boolean]
|
119
|
+
#
|
120
|
+
# @since 0.4.1
|
121
|
+
# @api private
|
122
|
+
def force?(env)
|
123
|
+
!ssl?(env)
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
# @since 0.4.1
|
129
|
+
# @api private
|
130
|
+
attr_reader :host
|
131
|
+
|
132
|
+
# Return full url to redirect
|
133
|
+
#
|
134
|
+
# @param env [Hash] Rack env
|
135
|
+
#
|
136
|
+
# @return [String]
|
137
|
+
#
|
138
|
+
# @since 0.4.1
|
139
|
+
# @api private
|
140
|
+
def full_url(env)
|
141
|
+
"#{ SSL_SCHEME }://#{ host }:#{ port }#{ ::Rack::Request.new(env).fullpath }"
|
142
|
+
end
|
143
|
+
|
144
|
+
# Return redirect code
|
145
|
+
#
|
146
|
+
# @param env [Hash] Rack env
|
147
|
+
#
|
148
|
+
# @return [Integer]
|
149
|
+
#
|
150
|
+
# @since 0.4.1
|
151
|
+
# @api private
|
152
|
+
def redirect_code(env)
|
153
|
+
if IDEMPOTENT_METHODS.include?(env[REQUEST_METHOD])
|
154
|
+
MOVED_PERMANENTLY_HTTP_CODE
|
155
|
+
else
|
156
|
+
TEMPORARY_REDIRECT_HTTP_CODE
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Return correct default port for full url
|
161
|
+
#
|
162
|
+
# @return [Integer]
|
163
|
+
#
|
164
|
+
# @since 0.4.1
|
165
|
+
# @api private
|
166
|
+
def port
|
167
|
+
if @port == DEFAULT_HTTP_PORT
|
168
|
+
DEFAULT_SSL_PORT
|
169
|
+
else
|
170
|
+
@port
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# @since 0.4.1
|
175
|
+
# @api private
|
176
|
+
def _redefine_call
|
177
|
+
return unless @active
|
178
|
+
|
179
|
+
define_singleton_method :call do |env|
|
180
|
+
[redirect_code(env), { LOCATION_HEADER => full_url(env) }, EMPTY_BODY] if force?(env)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Adapted from Rack::Request#scheme
|
185
|
+
#
|
186
|
+
# @since 0.4.1
|
187
|
+
# @api private
|
188
|
+
def scheme(env)
|
189
|
+
if env[HTTPS] == ON
|
190
|
+
SSL_SCHEME
|
191
|
+
elsif env[HTTP_X_FORWARDED_SSL] == ON
|
192
|
+
SSL_SCHEME
|
193
|
+
elsif env[HTTP_X_FORWARDED_SCHEME]
|
194
|
+
env[HTTP_X_FORWARDED_SCHEME]
|
195
|
+
elsif env[HTTP_X_FORWARDED_PROTO]
|
196
|
+
env[HTTP_X_FORWARDED_PROTO].split(HTTP_X_FORWARDED_PROTO_SEPARATOR)[0]
|
197
|
+
else
|
198
|
+
env[RACK_URL_SCHEME]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# @since 0.4.1
|
203
|
+
# @api private
|
204
|
+
def ssl?(env)
|
205
|
+
scheme(env) == SSL_SCHEME
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|