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