title: Rails 3, HTTP extensions (WebDAV) and Rack App mounting
body: <p>1 The following is more to point out to the code devs an issue of rails that can be percieved as a flaw.<br>
  2 And also me asking some oppinions from people who know better.</p>
  
  <p>I want to add WebDAV to my Rails 3 App with Warden authentication. My warden middleware is injected via Devise.</p>
  
  <blockquote>
    <p><a href="http://github.com/chrisroberts/dav4rack" rel="noreferrer">http://github.com/chrisroberts/dav4rack</a><br>
         <a href="http://github.com/hassox/warden" rel="noreferrer">http://github.com/hassox/warden</a><br>
         <a href="http://github.com/plataformatec/devise" rel="noreferrer">http://github.com/plataformatec/devise</a>  </p>
  </blockquote>
  
  <p>I cannot mount DAV4Rack handlers from inside rails app (routes), like this:</p>
  
  <pre><code># in config/routes.rb
  mount DAV4Rack::Handler.new(
    :root =&gt; Rails.root.to_s, # &lt;= it's just an example
    :root_uri_path =&gt; '/webdav',
    :resource_class =&gt; Dav::DocumentResource # &lt;= my custom resource, you could use FileResource from dav4rack
  ), :at =&gt; "/webdav"
  </code></pre>
  
  <p>because rails validates HTTP verbs (GET POST PUT ..), and webdav uses HTTP extensions like PROPFIND that do not validate, throwing the following exception:</p>
  
  <pre><code>ActionController::UnknownHttpMethod (PROPFIND, accepted HTTP methods are get, head, put, post, delete, and options)
  </code></pre>
  
  <p>This validation takes place in ActionDispatch:</p>
  
  <pre><code>/usr/local/lib/ruby/gems/1.9.1/gems/actionpack-3.0.0/lib/action_dispatch/http/request.rb +56 +72
  in (56) "def request_method" and (72) "def method"
  </code></pre>
  
  <p>Sample code from ActionDispatch that does the validation, to make things clear:</p>
  
  <pre><code>def method
    @method ||= begin
      method = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
      HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale =&gt; :en)}")
      method
    end
  end
  </code></pre>
  
  <p>In theory, we could monkey-patch this validation to comply with webdav verbs like the <a href="http://github.com/stueccles/railsdav/blob/master/lib/railsdav/methods_patch.rb" rel="noreferrer">railsdav project used to do</a> (note that is rails 2 there, in rails 3 one needs to monkey-patch action_dispatch/http/request).</p>
  
  <p>To add DAV4Rack handlers to the rails app I have to mount the handler outside of ActionDispatch, at rack level, like this:</p>
  
  <pre><code># config.ru
  require ::File.expand_path('../config/environment',  __FILE__)
  require 'dav4rack/interceptor'
  require 'dav/document_resource'
  
  app = Rack::Builder.new{
    map '/webdav/' do
      run DAV4Rack::Handler.new(
        :root =&gt; Rails.root.to_s,
        :root_uri_path =&gt; '/webdav',
        :resource_class =&gt; Dav::DocumentResource
      )
    end
  
    map '/' do
      use DAV4Rack::Interceptor, :mappings =&gt; {
        '/webdav/' =&gt; {
          :resource_class =&gt; Dav::DocumentResource
        },
      }
      run Pmp::Application
    end
  }.to_app
  run app
  </code></pre>
  
  <p>Now I have Webdav support in my application. But It still needs authentication, and for that I'd like to use warden.</p>
  
  <pre><code># in document_resource.rb
  def check_authentication
    puts request.env['warden'] # nil :(
  end
  </code></pre>
  
  <p>Warden is nil because my DAV4Rack::Handler is mounted above the session and warden middleware.
  Using "rake middleware" to inspect my stack I can see the following:</p>
  
  <pre><code>&gt; rake middleware 
  use ActionDispatch::Static
  use Rack::Lock
  use ActiveSupport::Cache::Strategy::LocalCache
  use Rack::Runtime
  use Rails::Rack::Logger
  use ActionDispatch::ShowExceptions
  use ActionDispatch::RemoteIp
  use Rack::Sendfile
  use ActionDispatch::Callbacks
  use ActiveRecord::ConnectionAdapters::ConnectionManagement
  use ActiveRecord::QueryCache
  use ActionDispatch::Cookies
  use ActionDispatch::Session::CookieStore
  use ActionDispatch::Flash
  use ActionDispatch::ParamsParser
  use Rack::MethodOverride
  use ActionDispatch::Head
  use ActionDispatch::BestStandardsSupport
  use Warden::Manager
  run Pmp::Application.routes
  </code></pre>
  
  <p>I believe that by wrapping "Pmp::Application.routes" with DAV handler (just like I do above for "Pmp::Application" in config.ru) will inject my webdav handler in the stack at the right place to satisfy the two conditions:</p>
  
  <ol>
  <li>Be above ActionDispatch method validation code, to avoid ActionController::UnknownHttpMethod</li>
  <li>Be below session and Warden::Manager so I can use warden authentication.</li>
  </ol>
  
  <p>How to do that? Looking at "rake middleware" otput it seems obvious to override the "Pmp::Application.routes" method:</p>
  
  <pre><code># in my app at APP_ROOT/config/application.rb
  # override the routes method inherited from Rails::Application#routes
  def routes
    routes_app = super
    app = Rack::Builder.new {
      map '/webdav/' do
        run DAV4Rack::Handler.new(
          :root =&gt; Rails.root.to_s,
          :root_uri_path =&gt; '/webdav',
          :resource_class =&gt; Dav::DocumentResource
        )
      end
  
      map '/' do
        use DAV4Rack::Interceptor, :mappings =&gt; {
          '/webdav/' =&gt; {
            :resource_class =&gt; Dav::DocumentResource
          },
        }
        run routes_app
      end
    }.to_app
  
    class &lt;&lt; app; self end.class_eval do
      attr_accessor :routes_app
      def method_missing(sym, *args, &amp;block)
        routes_app.send sym, *args, &amp;block
      end
    end
    app.routes_app = routes_app
  
    app
  end
  </code></pre>
  
  <p>Because our new rack application "app" will be asked a few methods down the chain, that the old rack application "routes_app" used to resopnd to, we delegate theese to the old original application "routes_app" with a little method_missing magic.</p>
  
  <p>And voila: everything is working! 
  Great success.</p>
  
  <p>Only one problem: I don't like it. There must be a better way to do all this enveloping, other than overriding routes method.</p>
  
  <p>Note that this doesn't work with passenger. The best way seems to be monkey patching rails.<br>
  See: <a href="http://github.com/chrisroberts/dav4rack/wiki" rel="noreferrer">dav4rack wiki</a></p>
  
  <h3>THE BIG QUESTION:</h3>
  
  <p>IS THERE A BETTER WAY TO ADD A RACK APP JUST ABOVE THE "Pmp::Application#routes" APP BY MEANS OF RACK MOUNT OR OTHER ???</p>
  
  <h3>THE BIG CONCLUSION</h3>
  
  <ol>
  <li>The "mount" semantics in routes.rb should be rack-level (not rails/railtie/whatever), to allow, in this way, handeling of HTTP extensions, or at least have a method for this case "mount_rack"</li>
  </ol>