Skip to content

Commit

Permalink
Merge branch 'master' into 0.4.x
Browse files Browse the repository at this point in the history
  • Loading branch information
jodosha committed Jun 16, 2015
2 parents 8a9d801 + 189870c commit 069eca9
Show file tree
Hide file tree
Showing 11 changed files with 544 additions and 14 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -1,6 +1,11 @@
# Lotus::Router
Rack compatible HTTP router for Ruby

## v0.4.1 - 2015-06-23
### Added
- [Alfonso Uceda Pompa] Force SSL (eg `Lotus::Router.new(force_ssl: true`).
- [Alfonso Uceda Pompa] Allow router to accept a `:prefix` option, in order to generate prefixed routes.

## v0.4.0 - 2015-05-15
### Added
- [Alfonso Uceda Pompa] Nested RESTful resource(s)
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Expand Up @@ -6,6 +6,6 @@ if !ENV['TRAVIS']
gem 'yard', require: false
end

gem 'lotus-utils', '~> 0.4', require: false, github: 'lotus/utils', branch: '0.4.x'
gem 'lotus-utils', '~> 0.5', require: false, github: 'lotus/utils', branch: '0.5.x'
gem 'simplecov', require: false
gem 'coveralls', require: false
3 changes: 1 addition & 2 deletions lib/lotus/router.rb
Expand Up @@ -13,12 +13,11 @@ module Lotus
#
# endpoint = ->(env) { [200, {}, ['Welcome to Lotus::Router!']] }
# router = Lotus::Router.new do
# get '/', to: endpoint
# get '/', to: endpoint # => get and head requests
# post '/', to: endpoint
# put '/', to: endpoint
# patch '/', to: endpoint
# delete '/', to: endpoint
# head '/', to: endpoint
# options '/', to: endpoint
# trace '/', to: endpoint
# end
Expand Down
2 changes: 1 addition & 1 deletion lib/lotus/router/version.rb
@@ -1,6 +1,6 @@
module Lotus
class Router
# @since 0.1.0
VERSION = '0.4.0'.freeze
VERSION = '0.4.1'.freeze
end
end
209 changes: 209 additions & 0 deletions lib/lotus/routing/force_ssl.rb
@@ -0,0 +1,209 @@
require 'rack/request'

module Lotus
module Routing
# Force ssl
#
# Redirect response to the secure equivalent resource (https)
#
# @since 0.4.1
# @api private
class ForceSsl
# Https scheme
#
# @since 0.4.1
# @api private
SSL_SCHEME = 'https'.freeze

# @since 0.4.1
# @api private
HTTPS = 'HTTPS'.freeze

# @since 0.4.1
# @api private
ON = 'on'.freeze

# Location header
#
# @since 0.4.1
# @api private
LOCATION_HEADER = 'Location'.freeze

# Default http port
#
# @since 0.4.1
# @api private
DEFAULT_HTTP_PORT = 80

# Default ssl port
#
# @since 0.4.1
# @api private
DEFAULT_SSL_PORT = 443

# Moved Permanently http code
#
# @since 0.4.1
# @api private
MOVED_PERMANENTLY_HTTP_CODE = 301

# Temporary Redirect http code
#
# @since 0.4.1
# @api private
TEMPORARY_REDIRECT_HTTP_CODE = 307

# @since 0.4.1
# @api private
HTTP_X_FORWARDED_SSL = 'HTTP_X_FORWARDED_SSL'.freeze

# @since 0.4.1
# @api private
HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'.freeze

# @since 0.4.1
# @api private
HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'.freeze

# @since 0.4.1
# @api private
HTTP_X_FORWARDED_PROTO_SEPARATOR = ','.freeze

# @since 0.4.1
# @api private
RACK_URL_SCHEME = 'rack.url_scheme'.freeze

# @since 0.4.1
# @api private
REQUEST_METHOD = 'REQUEST_METHOD'.freeze

# @since 0.4.1
# @api private
IDEMPOTENT_METHODS = ['GET', 'HEAD'].freeze

EMPTY_BODY = ''.freeze

# Initialize ForceSsl.
#
# @param active [Boolean] activate redirection to SSL
# @param options [Hash] set of options
# @option options [String] :host
# @option options [Integer] :port
#
# @since 0.4.1
# @api private
def initialize(active, options = {})
@active = active
@host = options[:host]
@port = options[:port]

_redefine_call
end

# Set 301 status and Location header if this feature is activated.
#
# @param env [Hash] a Rack env instance
#
# @return [Array]
#
# @see Lotus::Routing::HttpRouter#call
#
# @since 0.4.1
# @api private
def call(env)
end

# Check if router has to force the response with ssl
#
# @return [Boolean]
#
# @since 0.4.1
# @api private
def force?(env)
!ssl?(env)
end

private

# @since 0.4.1
# @api private
attr_reader :host

# Return full url to redirect
#
# @param env [Hash] Rack env
#
# @return [String]
#
# @since 0.4.1
# @api private
def full_url(env)
"#{ SSL_SCHEME }://#{ host }:#{ port }#{ ::Rack::Request.new(env).fullpath }"
end

# Return redirect code
#
# @param env [Hash] Rack env
#
# @return [Integer]
#
# @since 0.4.1
# @api private
def redirect_code(env)
if IDEMPOTENT_METHODS.include?(env[REQUEST_METHOD])
MOVED_PERMANENTLY_HTTP_CODE
else
TEMPORARY_REDIRECT_HTTP_CODE
end
end

# Return correct default port for full url
#
# @return [Integer]
#
# @since 0.4.1
# @api private
def port
if @port == DEFAULT_HTTP_PORT
DEFAULT_SSL_PORT
else
@port
end
end

# @since 0.4.1
# @api private
def _redefine_call
return unless @active

define_singleton_method :call do |env|
[redirect_code(env), { LOCATION_HEADER => full_url(env) }, EMPTY_BODY] if force?(env)
end
end

# Adapted from Rack::Request#scheme
#
# @since 0.4.1
# @api private
def scheme(env)
if env[HTTPS] == ON
SSL_SCHEME
elsif env[HTTP_X_FORWARDED_SSL] == ON
SSL_SCHEME
elsif env[HTTP_X_FORWARDED_SCHEME]
env[HTTP_X_FORWARDED_SCHEME]
elsif env[HTTP_X_FORWARDED_PROTO]
env[HTTP_X_FORWARDED_PROTO].split(HTTP_X_FORWARDED_PROTO_SEPARATOR)[0]
else
env[RACK_URL_SCHEME]
end
end

# @since 0.4.1
# @api private
def ssl?(env)
scheme(env) == SSL_SCHEME
end
end
end
end
84 changes: 76 additions & 8 deletions lib/lotus/routing/http_router.rb
Expand Up @@ -3,6 +3,8 @@
require 'lotus/routing/endpoint_resolver'
require 'lotus/routing/route'
require 'lotus/routing/parsers'
require 'lotus/routing/force_ssl'
require 'lotus/utils/path_prefix'

Lotus::Utils::IO.silence_warnings do
HttpRouter::Route::VALID_HTTP_VERBS = %w{GET POST PUT PATCH DELETE HEAD OPTIONS TRACE}
Expand Down Expand Up @@ -37,12 +39,14 @@ class HttpRouter < ::HttpRouter
def initialize(options = {}, &blk)
super(options, &nil)

@default_scheme = options[:scheme] if options[:scheme]
@default_host = options[:host] if options[:host]
@default_port = options[:port] if options[:port]
@route_class = options[:route] || Routing::Route
@resolver = options[:resolver] || Routing::EndpointResolver.new(options)
@parsers = Routing::Parsers.new(options[:parsers])
@default_scheme = options[:scheme] if options[:scheme]
@default_host = options[:host] if options[:host]
@default_port = options[:port] if options[:port]
@route_class = options[:route] || Routing::Route
@resolver = options[:resolver] || Routing::EndpointResolver.new(options)
@parsers = Routing::Parsers.new(options[:parsers])
@prefix = Utils::PathPrefix.new(options[:prefix] || '')
@force_ssl = Lotus::Routing::ForceSsl.new(!!options[:force_ssl], host: @default_host, port: @default_port)
end

# Separator between controller and action name.
Expand Down Expand Up @@ -85,14 +89,74 @@ def url(route, *args)
_rescue_url_recognition { super }
end

# Support for GET HTTP verb
#
# @see Lotus::Router#options
#
# @since 0.4.1
# @api private
def get(path, options = {}, &blk)
super(@prefix.join(path), options, &blk)
end

# Support for POST HTTP verb
#
# @see Lotus::Router#post
#
# @since 0.4.1
# @api private
def post(path, options = {}, &blk)
super(@prefix.join(path), options, &blk)
end

# Support for PUT HTTP verb
#
# @see Lotus::Router#put
#
# @since 0.4.1
# @api private
def put(path, options = {}, &blk)
super(@prefix.join(path), options, &blk)
end

# Support for PATCH HTTP verb
#
# @see Lotus::Router#patch
#
# @since 0.4.1
# @api private
def patch(path, options = {}, &blk)
super(@prefix.join(path), options, &blk)
end

# Support for DELETE HTTP verb
#
# @see Lotus::Router#delete
#
# @since 0.4.1
# @api private
def delete(path, options = {}, &blk)
super(@prefix.join(path), options, &blk)
end

# Support for TRACE HTTP verb
#
# @see Lotus::Router#trace
#
# @since 0.4.1
# @api private
def trace(path, options = {}, &blk)
super(@prefix.join(path), options, &blk)
end

# Support for OPTIONS HTTP verb
#
# @see Lotus::Router#options
#
# @since 0.1.0
# @api private
def options(path, options = {}, &blk)
add_with_request_method(path, :options, options, &blk)
add_with_request_method(@prefix.join(path), :options, options, &blk)
end

# Allow to mount a Rack app
Expand All @@ -109,7 +173,11 @@ def mount(app, options)

# @api private
def raw_call(env, &blk)
super(@parsers.call(env))
if response = @force_ssl.call(env)
response
else
super(@parsers.call(env))
end
end

# @api private
Expand Down
6 changes: 6 additions & 0 deletions lib/lotus/routing/namespace.rb
Expand Up @@ -57,6 +57,12 @@ def trace(path, options = {}, &endpoint)
super(@name.join(path), options, &endpoint)
end

# @api private
# @since 0.1.0
def options(path, options = {}, &endpoint)
super(@name.join(path), options, &endpoint)
end

# @api private
# @since 0.1.0
def resource(name, options = {})
Expand Down
2 changes: 1 addition & 1 deletion lotus-router.gemspec
Expand Up @@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = '>= 2.0.0'

spec.add_dependency 'http_router', '~> 0.11'
spec.add_dependency 'lotus-utils', '~> 0.4', '>= 0.4.1'
spec.add_dependency 'lotus-utils', '~> 0.5'

spec.add_development_dependency 'bundler', '~> 1.5'
spec.add_development_dependency 'minitest', '~> 5'
Expand Down

0 comments on commit 069eca9

Please sign in to comment.