Giter Club home page Giter Club logo

route_downcaser's Introduction

RouteDowncaser

Makes routing in Rails case-insensitive (and other Rack-servers like Sinatra)

This gem hooks into the Rack middleware of Rails. This way all paths are downcased before dispatching to Rails’ routing mechanism. Querystring parameters and asset paths are not changed in any way.

Requirements

This gem is tested with 5.2.x, 6.x, 7.x. It reportedly also works with Sinatra, although I do not use Sinatra myself. Sinatra test-cases will be most welcome.

It has previously worked - and I presume still works - in Rails 5.0.x and 5.1.x.

If you want this functionality in a Rails 2.x-3.0 application, please refer to this blog post. If you need it for Rails 3.1.x or 4.x version 1.2.2 should serve you just fine.

Note: from version 1.2.0, route_downcaser depends on ActiveSupport 3.2 or later. This was necessary to enable support for multibyte characters.

Installing

Rails

Just add the gem to your gemfile and run bundle

gem 'route_downcaser'

Then restart your Rails server. No configuration is needed. The gem simply hooks itself into the Rack middleware call stack.

If you have a controller named app/controllers/foo_controller.rb and the action index, you can now call it with:

http://localhost:3000/foo
http://localhost:3000/Foo
http://localhost:3000/FOO
http://localhost:3000/foo/index
http://localhost:3000/Foo/INDEX
http://localhost:3000/FOO/IndeX

All the above are valid.

Sinatra

In your Gemfile you add the gem:

gem 'route_downcaser'

In your application.rb you add the following (after loading Sinatra)

require 'route_downcaser'
use RouteDowncaser::DowncaseRouteMiddleware

Now this statement

get '/foo' do
  "Hello"
end

will respond to all these requests:

http://localhost:4567/foo
http://localhost:4567/Foo
http://localhost:4567/FOO

Configuration Options

Configuration options can be set using an initializer in your application like so:

# config/initializers/route_downcaser.rb

RouteDowncaser.configuration do |config|
  config.redirect = true

  config.exclude_patterns = [
    /assets\//i,
    /fonts\//i,
    /some\/important\/path/i
  ]
end

redirect

With this configuration option set to ‘true`, the middleware will 301 redirect all routes to their downcased version. Example: `localhost:3000/Foo` will redirect to `localhost:3000/foo`.

exclude_patterns

Array of regular expressions. If the requested path matches one of these expressions, RouteDowncaser will not change anything.

Background information

Sometimes you may wish to market a url which contains a controller name - like www.myshop.com/specialoffer. If this is done on offline media it will cause potential customers trouble, if they don’t use the correct casing. If, for some reason, the user types www.myshop.com/Specialoffer (with capital S), your Rails app will return a 404 not found.

The strange thing is, that the W3C specification states:

URLs in general are case-sensitive (with the exception of machine names). There may be URLs, or parts of URLs, where case doesn't matter, but identifying these may not be easy. Users should always consider that URLs are case-sensitive.

So www.myshop.com is case-insensitive and can be written as WWW.MYSHOP.COM, but the path (/specialoffer) is not.

It may make perfect sense for the W3C guys, but not for the average user.

When Rails introduced Rack middleware, it suddenly became possible to hook into the call-stack before the URL is used for action dispatching.

At first I just made this code as a snippet and published it on my blog. But since I found out that many people are actually using this, I finally decided to convert it into a gem.

All it really does is to take the path and downcase it before dispatching. Querystring parameters are NOT touched, they keep their casing, since it may have some contextual meaning.

route_downcaser's People

Contributors

carstengehling avatar caseypugh avatar dependabot-preview[bot] avatar flou avatar kevinebaugh avatar koppen avatar l3akage avatar mackermedia avatar pepawel avatar rmeritz avatar tgk avatar timdiggins avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

route_downcaser's Issues

NoMethodError at / - Sinatra

Error after I installed:

Screen Shot 2020-01-26 at 12 07 37 AM

Application was working fine before I installed and works fine if I comment out the use and require statements.

Things I tried:

  • Creating a route_downcaser.rb file but wasn't able to figure out what regex to use to exclude this route :(
  • Moving the use and require statements around

Also reporting because it seems like this route should be ignored in your code (assuming it's not already, I might just have something misconfigured or incorrect)

Relevant files:

## config/routes.rb


get '/' do
  send_file File.join(settings.public_folder, 'index.html')
end
## config.ru


# frozen_string_literal: true

$:.unshift File.expand_path("../", __FILE__)
require 'rubygems'
require 'sinatra'

require './routes'
run Sinatra::Application

require 'route_downcaser'
use RouteDowncaser::DowncaseRouteMiddleware

Versions:

  • route_downcaser 1.2.2
  • sinatra 2.0.8.1
  • rack 2.1.1
  • ruby 2.6.3

What about Cyrillic symbols?

Hello! Thank for this gem! Really simple and amazing solution!
I have a question. My routes now works like this:

example.com/recipes/BeeR => example.com/recipes/beer

but for Cyrillic letters

example.com/recipes/ПИВО => example.com/recipes/ПИВО

Should we improve this with mb_chars ? I mean:

      if has_querystring?(uri)
        "#{path(uri).mb_chars.downcase}?#{querystring(uri)}"
      else
        path(uri).mb_chars.downcase
      end

Maybe this idea will be interesting?

Gem can conflict with Devise/Warden

Installed this gem in Rails 4 with Devise/Warden. Works great for everything except when logging in as a user devise would error out via Warden. I don't know if there is an easy fix to it or the best way to do this, but ensuring the route_downcaser middleware was loaded before Warden fixed all my problems.

I solved this by changing railtie.rb to: app.config.middleware.insert_before 'Warden::Manager', 'RouteDowncaser::DowncaseRouteMiddleware'

Obviously this has issues in that it will error out if you're not using the Warden::Manager middleware, but I am unsure how to create this same effect without specifying explicitly.

It also has an issue in that the flash messages will no longer show up, but at least I can log in.

If you know how to solve this better, please let me know or implement it in the gem, otherwise this can be closed but I wanted to make an issue for others to find if they have the same problem.

Any interest in a :moved_permanently option ?

I converted an old site to Rails that had been indexed with mixed case URLs and would like to have the response code be 301 moved permanently.

Is there any interest in adding this? Would you accept a pull request with this change?

Thanks!

In downcase_route_middleware.rb:33 in "uri_items": NoMethodError: undefined method `split' for nil:NilClass

I got this error in my production environment last night. It's only occurred once so far but I figured it might be good to log anyway.

As you can see, the env['REQUEST_URI'] exists but the line that caused the error doesn't think so.
Is it possible the @env was not set to env in def call(env)?

Environment (anonymized):

CONTENT_TYPE
text/plain
GATEWAY_INTERFACE
CGI/1.2
HTTP_ACCEPT
*/*
HTTP_ACCEPT_ENCODING
gzip, deflate
HTTP_ACCEPT_LANGUAGE
en;q=1
HTTP_CONNECTION
close
HTTP_HOST
throwdown.us
HTTP_IF_NONE_MATCH
"xxxxx"
HTTP_USER_AGENT
xxxxx
HTTP_VERSION
HTTP/1.0
HTTP_X_FORWARDED_FOR
xxxxx
HTTP_X_FORWARDED_PROTO
https
HTTP_X_REAL_IP
xxxxx
ORIGINAL_FULLPATH
/users.json?token=xxxxx
ORIGINAL_SCRIPT_NAME
PATH_INFO
/users.json
QUERY_STRING
token=xxxxx
REMOTE_ADDR
127.0.0.1
REQUEST_METHOD
GET
REQUEST_PATH
/users.json
REQUEST_URI
/users.json?token=xxxxx
ROUTES_58746340_SCRIPT_NAME
SCRIPT_NAME
SERVER_NAME
xxxxx
SERVER_PORT
443
SERVER_PROTOCOL
HTTP/1.1
SERVER_SOFTWARE
2.8.2
action_dispatch.cookies_serializer
action_dispatch.parameter_filter
["password"]
action_dispatch.redirect_filter
[]
action_dispatch.remote_ip
xxxxx
action_dispatch.request_id
xxxxx
action_dispatch.show_detailed_exceptions
false
action_dispatch.show_exceptions
true
newrelic.captured_request
true
puma.config
#<Puma::Configuration:0x00000001c8f388>
puma.socket
#<UNIXSocket:0x007fa5ce044c70>
rack.after_reply
[]
rack.hijack
#<Puma::Client:0x007fa5ce044c48>
rack.hijack?
true
rack.multiprocess
false
rack.multithread
true
rack.request.query_hash
{
  "token": "xxxxx"
}
rack.request.query_string
token=xxxxx
rack.run_once
false
rack.url_scheme
http
rack.version
["1", "2"]

Backtrace:

/gems/route_downcaser-0.2.2/lib/route_downcaser/downcase_route_middleware.rb:33 in "uri_items"
/gems/route_downcaser-0.2.2/lib/route_downcaser/downcase_route_middleware.rb:37 in "path"
/gems/route_downcaser-0.2.2/lib/route_downcaser/downcase_route_middleware.rb:46 in "downcased_uri"
/gems/route_downcaser-0.2.2/lib/route_downcaser/downcase_route_middleware.rb:16 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/warden-1.2.3/lib/warden/manager.rb:35 in "block in call"
/gems/warden-1.2.3/lib/warden/manager.rb:34 in "catch"
/gems/warden-1.2.3/lib/warden/manager.rb:34 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/rack-1.5.2/lib/rack/etag.rb:23 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/rack-1.5.2/lib/rack/conditionalget.rb:25 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/rack-1.5.2/lib/rack/head.rb:11 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/actionpack-4.1.0/lib/action_dispatch/middleware/params_parser.rb:27 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/actionpack-4.1.0/lib/action_dispatch/middleware/flash.rb:254 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/rack-1.5.2/lib/rack/session/abstract/id.rb:225 in "context"
/gems/rack-1.5.2/lib/rack/session/abstract/id.rb:220 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/actionpack-4.1.0/lib/action_dispatch/middleware/cookies.rb:560 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/activerecord-4.1.0/lib/active_record/query_cache.rb:36 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/activerecord-4.1.0/lib/active_record/connection_adapters/abstract/connection_pool.rb:621 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/actionpack-4.1.0/lib/action_dispatch/middleware/callbacks.rb:29 in "block in call"
/gems/activesupport-4.1.0/lib/active_support/callbacks.rb:82 in "run_callbacks"
/gems/actionpack-4.1.0/lib/action_dispatch/middleware/callbacks.rb:27 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/actionpack-4.1.0/lib/action_dispatch/middleware/remote_ip.rb:76 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/actionpack-4.1.0/lib/action_dispatch/middleware/debug_exceptions.rb:17 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/actionpack-4.1.0/lib/action_dispatch/middleware/show_exceptions.rb:30 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/railties-4.1.0/lib/rails/rack/logger.rb:38 in "call_app"
/gems/railties-4.1.0/lib/rails/rack/logger.rb:20 in "block in call"
/gems/activesupport-4.1.0/lib/active_support/tagged_logging.rb:68 in "block in tagged"
/gems/activesupport-4.1.0/lib/active_support/tagged_logging.rb:26 in "tagged"
/gems/activesupport-4.1.0/lib/active_support/tagged_logging.rb:68 in "tagged"
/gems/railties-4.1.0/lib/rails/rack/logger.rb:20 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/actionpack-4.1.0/lib/action_dispatch/middleware/request_id.rb:21 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/rack-1.5.2/lib/rack/methodoverride.rb:21 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/rack-1.5.2/lib/rack/runtime.rb:17 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/activesupport-4.1.0/lib/active_support/cache/strategy/local_cache_middleware.rb:26 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/rack-1.5.2/lib/rack/sendfile.rb:112 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/railties-4.1.0/lib/rails/engine.rb:514 in "call"
/gems/railties-4.1.0/lib/rails/application.rb:144 in "call"
/gems/newrelic_rpm-3.9.9.275/lib/new_relic/agent/instrumentation/middleware_tracing.rb:57 in "call"
/gems/puma-2.8.2/lib/puma/configuration.rb:71 in "call"
/gems/puma-2.8.2/lib/puma/server.rb:490 in "handle_request"
/gems/puma-2.8.2/lib/puma/server.rb:361 in "process_client"
/gems/puma-2.8.2/lib/puma/server.rb:254 in "block in run"
/gems/puma-2.8.2/lib/puma/thread_pool.rb:92 in "call"
/gems/puma-2.8.2/lib/puma/thread_pool.rb:92 in "block in spawn_thread"

Unable to downcase routes when using a custom controller action

I have a project with movies and genres. I've successfully been able to index these movies on their own special pages according to what genre they belong to with the help of this StackOverflow post. For a little bit more detail this is how my movies controller and route file looks thus far.

Movies Controller

def genre_movies
  @movies = Genre.find_by(name: params[:genre]).movies
  render 'index'
end

Routes

get '/:genre', to: 'movies#genre_movies'

Unfortunately since the genres are entered in a capitalized fashion I have to enter urls like so to avoid receiving any NilClass errors undefined method 'movies' for nil:NilClass:

localhost:3000/Comedy

In hopes to remove any case-sensitivity limitations I decided to use this gem but it seems like it downcases all but these particular routes. What would I need to do to get this to work?

How to exclude route pattern

RouteDowncaser.configuration do |config|
config.redirect = true
end

How can the config be set to exclude a matching route pattern, such as /posts/*

Great idea/gem btw..

Thanks

URL path traversal attacks can cause downcase ArgumentError

On v2.0.0, navigating to a URl that contains a directory traversal attack in its path (e.g. localhost:5000/%c0%ae%c0%ae) will cause the following error to be raised:

ArgumentError: input string invalid
/usr/local/bundle/gems/route_downcaser-2.0.0/lib/route_downcaser/downcase_route_middleware.rb:64:in `downcase': input string invalid (ArgumentError)

Calling URI.decode_www_form_component("%c0") returns "\xC0", which is considered an invalid string for downcasing.

Infinite redirection loop when malformed url path given

When a url path is given that has non ascii characters we get an infinite redirection loop.
example:
www.somedomain.com/��

ERR_TOO_MANY_REDIRECTS

Started GET "/1%C3%83%C6%92%C3%82%C2%AF%C3%83%E2%80%9A%C3%82%C2%BF%C3%83%E2%80%9A%C3%82%C2%BD%C3%83%C6%92%C3%82%C2%AF%C3%83%E2%80%9A%C3%82%C2%BF%C3%83%E2%80%9A%C3%82%C2%BD"

followed by (until max redirects reached)

Started GET "/1%c3%83%c6%92%c3%82%c2%af%c3%83%e2%80%9a%c3%82%c2%bf%c3%83%e2%80%9a%c3%82%c2%bd%c3%83%c6%92%c3%82%c2%af%c3%83%e2%80%9a%c3%82%c2%bf%c3%83%e2%80%9a%c3%82%c2%bd"


Granted this is a garbage query from random internet bots, but I don't think a redirect loop is the correct way to handle such a request.

it is not working if add path to routes.rb

If I add a path to routes, it is not work anymore.

resources :classes, :path => "HowToStartAClass"

It just shows 'No route matches'
I will be happy if you can fix this problem.
Thanks.

Dependabot can't resolve your Ruby dependency files

Dependabot can't resolve your Ruby dependency files.

As a result, Dependabot couldn't update your dependencies.

The error Dependabot encountered was:

Bundler::GemNotFound with message: Could not find nokogiri-1.11.0-arm64-darwin in any of the sources

If you think the above is an error on Dependabot's side please don't hesitate to get in touch - we'll do whatever we can to fix it.

View the update logs.

Incompatibility with Rails 5.2 ActiveStorage

ActiveStorage uses signed_blob_id parameters that are case-sensitive. At present, this is incompatible with the route_downcaser gem. However, the downcasing of routes in general is a useful concept. If there would be any way to exempt these ActiveStorage generated urls (they are typically of the form /rails/active_storage/... with the built-in routing in Rails), that would really help me out.

Sinatra support

I am in need of this exact gem for Sinatra. Do you have plans to support Sinatra in the future?

is transformation of REQUEST_URI actually needed (could it be removed?)

Wondering if it's possible to drop transformation of REQUEST_URI for simplification...

REQUEST_URI env this is not actually part of the Rack api and is not needed for supporting case-insensitive routing? This stems from the CGI standard which defines PATH_INFO but not REQUEST_URI.

Rails 5.1 support

Hi,

It appears this gem doesn't support Rails 5.1. The problem is caused by ActiveSupport dependency which is set to < 5.1.

Errors in rack when running inside docker container

When we try to use this gem within a docker container,
and configuring the gem to redirect /FooBar => /foobar
and then visit a url that would trigger the case insensitive redirect, we get one of these 2 errors seemingly at random.

Rack::Lint::LintError at /FooBar
a header value must be a String, but the value of 'Location' is a ActiveSupport::Multibyte::Chars
ThreadError at /FooBar
deadlock; recursive locking 

However we get no exception if a parameter is tacked onto the end of the url
examples:
mydomain.com/foobar -- works as expected
mydomain.com/FooBar -- fails with one of the 2 exceptions
mydomain.com/FooBar?BAZ -- works as expected

NOTE: no issues when config.redirect = false

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.