Git Product home page Git Product logo

dav4rack's Introduction

DAV4Rack - Web Authoring for Rack

DAV4Rack is a framework for providing WebDAV via Rack allowing content authoring over HTTP. It is based off the original RackDAV framework adding some useful new features:

  • Better resource support for building fully virtualized resource structures

  • Generic locking as well as Resource level specific locking

  • Interceptor middleware to provide virtual mapping to resources

  • Mapped resource paths

  • Authentication support

  • Resource callbacks

  • Remote file proxying (including sendfile support for remote files)

  • Nokogiri based document parsing and building

If you find issues, please create a new issue on github. If you have fixes, please fork the repo and send me a pull request with your modifications. If you are just here to use the library, enjoy!

Install

Via RubyGems

gem install dav4rack

Documentation

Quickstart

If you just want to share a folder over WebDAV, you can just start a simple server with:

dav4rack

This will start a Unicorn, Mongrel or WEBrick server on port 3000, which you can connect to without authentication. Unicorn and Mongrel will be much more responsive than WEBrick, so if you are having slowness issues, install one of them and restart the dav4rack process. The simple file resource allows very basic authentication which is used for an example. To enable it:

dav4rack --username=user --password=pass

Rack Handler

Using DAV4Rack within a rack application is pretty simple. A very slim rackup script would look something like this:

require 'rubygems'
require 'dav4rack'

use Rack::CommonLogger
run DAV4Rack::Handler.new(:root => '/path/to/public/fileshare')

This will use the included FileResource and set the share path. However, DAV4Rack has some nifty little extras that can be enabled in the rackup script. First, an example of how to use a custom resource:

run DAV4Rack::Handler.new(:resource_class => CustomResource, :custom => 'options', :passed => 'to resource')

Next, lets venture into mapping a path for our WebDAV access. In this example, we will use default FileResource like in the first example, but instead of the WebDAV content being available at the root directory, we will map it to a specific directory: /webdav/share/

require 'rubygems'
require 'dav4rack'

use Rack::CommonLogger

app = Rack::Builder.new{
  map '/webdav/share/' do
    run DAV4Rack::Handler.new(:root => '/path/to/public/fileshare', :root_uri_path => '/webdav/share/')
  end
}.to_app
run app

Aside from the Builder#map block, notice the new option passed to the Handler’s initialization, :root_uri_path. When DAV4Rack receives a request, it will automatically convert the request to the proper path and pass it to the resource.

Another tool available when building the rackup script is the Interceptor. The Interceptor’s job is to simply intecept WebDAV requests received up the path hierarchy where no resources are currently mapped. For example, lets continue with the last example but this time include the interceptor:

require 'rubygems'
require 'dav4rack'

use Rack::CommonLogger
app = Rack::Builder.new{
  map '/webdav/share/' do
    run DAV4Rack::Handler.new(:root => '/path/to/public/fileshare', :root_uri_path => '/webdav/share/')
  end
  map '/webdav/share2/' do
    run DAV4Rack::Handler.new(:resource_class => CustomResource, :root_uri_path => '/webdav/share2/')
  end
  map '/' do
    use DAV4Rack::Interceptor, :mappings => {
      '/webdav/share/' => {:resource_class => FileResource, :custom => 'option'},
      '/webdav/share2/' => {:resource_class => CustomResource}
    }
    use Rails::Rack::Static
    run ActionController::Dispatcher.new
  end
}.to_app
run app

In this example we have two WebDAV resources restricted by path. This means those resources will handle requests to /webdav/share/* and /webdav/share2/* but nothing above that. To allow webdav to respond, we provide the Interceptor. The Interceptor does not provide any authentication support. It simply creates a virtual file system view to the provided mapped paths. Once the actual resources have been reached, authentication will be enforced based on the requirements defined by the individual resource. Also note in the root map you can see we are running a Rails application. This is how you can easily enable DAV4Rack with your Rails application.

Enabling Logging

DAV4Rack provides some simple logging in a Rails style format (simply for consistency) so the output should look some what familiar.

DAV4Rack::Handler.new(:resource_class => CustomResource, :log_to => '/my/log/file')

You can even specify the level of logging:

DAV4Rack::Handler.new(:resource_class => CustomResource, :log_to => ['/my/log/file', Logger::DEBUG])

Custom Resources

Creating your own resource is easy. Simply inherit the DAV4Rack::Resource class, and start redefining all the methods you want to customize. The DAV4Rack::Resource class only has implementations for methods that can be provided extremely generically. This means that most things will require at least some sort of implementation. However, because the Resource is defined so generically, and the Controller simply passes the request on to the Resource, it is easy to create fully virtualized resources.

Helpers

There are some helpers worth mentioning that make things a little easier. DAV4Rack::Resource#accept_redirect? method is available to Resources. If true, the currently connected client will accept and properly use a 302 redirect for a GET request. Most clients do not properly support this, which can be a real pain when working with virtualized files that may be located some where else, like S3. To deal with those clients that don’t support redirects, a helper has been provided so resources don’t have to deal with proxying themselves. The DAV4Rack::RemoteFile is a modified Rack::File that can do some interesting things. First, lets look at its most basic use:

class MyResource < DAV4Rack::Resource
  def setup
    @item = method_to_fill_this_properly
  end

  def get
    if(accept_redirect?)
      response.redirect item[:url]
    else
      response.body = DAV4Rack::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type)
      OK
    end
  end
end

This is a simple proxy. When Rack receives the RemoteFile, it will pull a chunk of data from object, which in turn pulls it from the socket, and sends it to the user over and over again until the EOF is reached. This much the same method that Rack::File uses but instead we are pulling from a socket rather than an actual file. Now, instead of proxying these files from a remote server every time, lets cache them:

response.body = DAV4Rack::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type, :cache_directory => '/tmp')

Providing the :cache_directory will let RemoteFile cache the items locally, and then search for them on subsequent requests before heading out to the network. The cached file name is based off the SHA1 hash of the file path, size and last modified time. It is important to note that for services like S3, the path will often change, making this cache pretty worthless. To combat this, we can provide a reference to use instead:

response.body = DAV4Rack::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type, :cache_directory => '/tmp', :cache_ref => item[:static_url])

These methods will work just fine, but it would be really nice to just let someone else deal with the proxying and let the process get back to dealing with actual requests. RemoteFile will happily do that as long as the frontend server is setup correctly. Using the sendfile approach will tell the RemoteFile to simply pass the headers on and let the server deal with doing the actual proxying. First, lets look at an implementation using all the features, and then degrade that down to the bare minimum. These examples are NGINX specific, but are based off the Rack::Sendfile implementation and as such should be applicable to other servers. First, a simplified NGINX server block:

server {
  listen 80;
  location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Sendfile-Type X-Accel-Redirect;
    proxy_set_header X-Accel-Remote-Mapping webdav_redirect
    proxy_pass http://my_app_server;
  }

  location ~* /webdav_redirect {
    internal;
    resolver 127.0.0.1;
    set $r_host $upstream_http_redirect_host;
    set $r_url $upstream_http_redirect_url;
    proxy_set_header Authorization '';
    proxy_set_header Host $r_host;
    proxy_max_temp_file_size 0;
    proxy_pass $r_url;
  }
}

With this in place, the parameters for the RemoteFile change slightly:

response.body = DAV4Rack::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type, :sendfile => true)

The RemoteFile will automatically take care of building out the correct path and sending the proper headers. If the X-Accel-Remote-Mapping header is not available, you can simply pass the value:

response.body = DAV4Rack::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type, :sendfile => true, :sendfile_prefix => 'webdav_redirect')

And if you don’t have the X-Sendfile-Type header set, you can fix that by changing the value of :sendfile:

response.body = DAV4Rack::RemoteFile.new(item[:url], :size => content_length, :mime_type => content_type, :sendfile => 'X-Accel-Redirect', :sendfile_prefix => 'webdav_redirect')

And if you have none of the above because your server hasn’t been configured for sendfile support, you’re out of luck until it’s configured.

Authentication

Authentication is performed on a per Resource basis. The Controller object will check the Resource for a Resource#authenticate method. If it exists, any authentication information will be passed to the method. Depending on the result, the Controller will either continue on with the request, or send a 401 Unauthorized response.

As a nicety, Resource#authentication_realm will be checked for existence and the returning string will be used as the Realm. Resource#authentication_error_msg will also be checked for existence and the returning string will be passed in the response upon authentication failure.

Authentication can also be implemented using callbacks, as discussed below.

Callbacks

Resources can make use of callbacks to easily apply permissions, authentication or any other action that needs to be performed before or after any or all actions. Callbacks are applied to all publicly available methods. This is important for methods used internally within the resource. Methods not meant to be called by the Controller, or anyone else, should be scoped protected or private to reduce the interaction with callbacks.

Callbacks can be called before or after a method call. For example:

class MyResource < DAV4Rack::Resource
  before do |resource, method_name|
    resource.send(:my_authentication_method)
  end

  after do |resource, method_name|
    puts "#{Time.now} -> Completed: #{resource}##{method_name}"
  end

  private

  def my_authentication_method
    true
  end
end

In this example MyResource#my_authentication_method will be called before any public method is called. After any method has been called a status line will be printed to STDOUT. Running callbacks before/after every method call is a bit much in most cases, so callbacks can be applied to specific methods:

class MyResource < DAV4Rack::Resource
  before_get do |resource|
    puts "#{Time.now} -> Received GET request from resource: #{resource}"
  end
end

In this example, a simple status line will be printed to STDOUT before the MyResource#get method is called. The current resource object is always provided to callbacks. The method name is only provided to the generic before/after callbacks.

Something very handy for dealing with the mess of files OS X leaves on the system:

class MyResource < DAV4Rack::Resource
  after_unlock do |resource|
    resource.delete if resource.name[0,1] == '.'
  end
end

Because OS X implements locking correctly, we can wait until it releases the lock on the file, and remove it if it’s a hidden file.

Callbacks are called in the order they are defined, so you can easily build callbacks off each other. Like this example:

class MyResource < DAV4Rack::Resource
  before do |resource, method_name|
    resource.DAV_authenticate unless resource.user.is_a?(User)
    raise Unauthorized unless resource.user.is_a?(User)
  end
  before do |resource, method_name|
    resource.user.allowed?(method_name)
  end
end

In this example, the second block checking User#allowed? can count on Resource#user being defined because the blocks are called in order, and if the Resource#user is not a User type, an exception is raised.

Avoiding callbacks

Something special to notice in the last example is the DAV_ prefix on authenticate. Providing the DAV_ prefix will prevent any callbacks being applied to the given method. This allows us to provide a public method that the callback can access on the resource without getting stuck in a loop.

Software using DAV4Rack!

  • meishi - Lightweight CardDAV implementation in Rails

  • dav4rack_ext - CardDAV extension. (CalDAV planned)

Issues/Bugs/Questions

Known Issues

  • OS X Finder PUT fails when using NGINX (this is due to NGINX’s lack of chunked transfer encoding)

  • Windows WebDAV mini-redirector fails (this client is very broken. patches welcome.)

  • Lots of unimplemented parts of the webdav spec (patches always welcome)

Unknown Issues

Please use the issues at github: github.com/chrisroberts/dav4rack/issues

Contributors

A big thanks to everyone contributing to help make this project better.

License

Just like RackDAV before it, this software is distributed under the MIT license.

dav4rack's People

Contributors

chrisroberts avatar exabugs avatar georgi avatar geroldboehler avatar inferiorhumanorgans avatar nevir avatar schmurfy avatar sophiebits avatar turchenkoalex 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  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  avatar

dav4rack's Issues

Username Subdirectories

In a continuation of this issue:

#27

I'm trying to setup exposing subdirectories based on the user's authenticated username... ie:

  • root_dir
    • username1
    • username2

ie. expose /root_dir/username1 as / if the user logins with username1, i'm just having an issue as to where to put the logic?

I'd love to put together an advanced example for the Wiki of setting up Dav4Rack w/ Devise and subdirectories once I get it all working.

Thanks in advance!

Allow custom namespace for xml_error and render_xml functions

Currently, I am implementing CalDAV using dav4rack as base.
But sometimes CalDAV require changes for namespace in particular purpose.
Anyway I can do it with dav4rack's xml_error and render_xml functions?

For instance, I have the following functions:

xml_error(Forbidden) do |err|
err.send 'no-uid-conflict', 'hihi'
end if resource.calendar.uids.include?(ical_uid)

which will create the following result:

<D:error xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:no-uid-conflict>haha/D:no-uid-conflict
/D:error

Can I change the namespace of no-hid-conflict with prefix 'C'? Thanks a lot!

PUT failing using Cyberduck < 4.1 and Transmit

Both cyberduck and transmit are reporting failures on PUT. This looks to be stemming from the response, with transmit specifically throwing an error message about timing out when reading the response body.

Draft code for HTTP Digest authentication implementation

Hi,

I would like to allow opening files from Microsoft Word to authenticated users.
As Word and MS webdav clients forbidden access to server with HTTP Basic over simple HTTP (I will set SSL in production), I started to patch dav4rack for Digest authentication.

The code is available here: https://github.com/pifleo/dav4rack/compare/auth_digest and a wiki page here

It is very short and working in my apps as expected as dependencies are met.

I think it can't be integrated in the gem for the moment as it add some dependencies from actionpack:

  • action_controller/http_authentication
  • action_dispatch/request.

But If you have any advice or proposition to make it better we can try to integrate this feature.

DAV4Rack::Handler should lock a resource

Running the specs on ubuntu:

.....................F

Failures:

  1) DAV4Rack::Handler should lock a resource
     Failure/Error: @response = request.request(method, uri, options)
     NoMethodError:
       undefined method `to_i' for :infinity:Symbol
     # ./lib/dav4rack/resource.rb:237:in `lock'
     # ./lib/dav4rack/controller.rb:186:in `lock'
     # ./lib/dav4rack/handler.rb:29:in `call'
     # ./spec/handler_spec.rb:29:in `request'
     # ./spec/handler_spec.rb:34:in `block (3 levels) in <top (required)>'
     # ./spec/handler_spec.rb:291:in `block (2 levels) in <top (required)>'

Finished in 0.21852 seconds
22 examples, 1 failure

The issue is that in controller.rb:314 the depth is set to :infinity and later in resource.rb:237 it tries to convert it to_i.
The thing is the depth option is already converted to integer in controller.rb#depth:312 (that is unless it's the :infinity symbol).

Removing the #to_i conversion in resource.rb:237 passes the specs but i'm not sure if it's the right way to go.

TypeError: can't convert nil into String (on get file)

Environment: ubuntu 10.10, ruby 1.9.2, rack 1.2.1, dav4rack 0.1.3

Steps that reproduce the problem:

  1. Run dav4rack console command that starts dav server

    dav4rack

  2. Try to copy a file from server (copy paste, or open in editor)

The file should be cpied to local FS but instead the client gets "internal server eror".
The server backtrace is:

TypeError: can't convert nil into String
    /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.1/lib/rack/file.rb:34:in `join'
    /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.1/lib/rack/file.rb:34:in `_call'
    /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.1/lib/rack/file.rb:25:in `call'
    /usr/local/lib/ruby/gems/1.9.1/gems/dav4rack-0.1.3/lib/dav4rack/handler.rb:54:in `call'
    /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.1/lib/rack/lint.rb:48:in `_call'
    /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.1/lib/rack/lint.rb:36:in `call'
    /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.1/lib/rack/reloader.rb:44:in `call'
    /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.1/lib/rack/commonlogger.rb:18:in `call'
    /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.1/lib/rack/showexceptions.rb:24:in `call'
    /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.1/lib/rack/content_length.rb:13:in `call'
    /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.1/lib/rack/chunked.rb:15:in `call'
    /usr/local/lib/ruby/gems/1.9.1/gems/rack-1.2.1/lib/rack/handler/mongrel.rb:67:in `process'
    /usr/local/lib/ruby/gems/1.9.1/gems/mongrel-1.2.0.pre2/lib/mongrel.rb:165:in `block in process_client'
    /usr/local/lib/ruby/gems/1.9.1/gems/mongrel-1.2.0.pre2/lib/mongrel.rb:164:in `each'
    /usr/local/lib/ruby/gems/1.9.1/gems/mongrel-1.2.0.pre2/lib/mongrel.rb:164:in `process_client'
    /usr/local/lib/ruby/gems/1.9.1/gems/mongrel-1.2.0.pre2/lib/mongrel.rb:291:in `block (2 levels) in run'

Chunked Upload

Sorry if this is the incorrect place to ask a question.
Does Dav4Rack support chunked uploads?

authentication and check_authentication in FileResource

Why does file resource provide both authentication and check_authentication callbacks, since they do the same thing?

It seems enough to provide authentication.

check_authentication should be deleted, since it does the same thing.

XML abstraction

Abstract out the XML so we do not rely specifically on a single XML library. Add generalized XML searching and building and allow the user to specify XML library (nokogiri, hpricot or REXML) based on options settings on initialization.

Logger only accepts Logger type

Logger only accepts instances of a Logger type, which means that using it with a different logger (like active supports buffered logger) will fail.

Interceptor brakes Rails REST updates (and proposed solution)

Using the DAV4Rack::Interceptor with Rails app, when updating a resource from the Rails app, the request is routed by the Interceptor to the webdav handler insted of routing it to the Rails app.

The problem is that the Interceptor, to compute where to forward the request, looks at the HTTP method and PUT is both core HTPP valid verb (used by Rails on updates) and Webdav verb.

As the Interceptor is only used to create the virtual mapping (from host root to dav mount point) I think the only HTTP methods it should check are OPTIONS and PROPFIND, or all dav verbs except PUT (not sure on these).

Sample code:

# the current situation in /dav4rack/interceptor.rb +14
if(@roots.detect{|x| path =~ /^#{Regexp.escape(x.downcase)}\/?/}.nil? && %w(OPTIONS PUT PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK).include?(method))

# proposed change in /dav4rack/interceptor.rb +14
if(@roots.detect{|x| path =~ /^#{Regexp.escape(x.downcase)}\/?/}.nil? && %w(OPTIONS PROPFIND).include?(method))

For my own needs to overcome this I made a cusom Interceptor

module Webdav
  class Interceptor < DAV4Rack::Interceptor
    def call(env)
      path = env['PATH_INFO'].downcase
      method = env['REQUEST_METHOD'].upcase
      app = nil
      if(@roots.detect{|x| path =~ /^#{Regexp.escape(x.downcase)}\/?/}.nil? && %w(OPTIONS PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK).include?(method))
        app = DAV4Rack::Handler.new(:resource_class => DAV4Rack::InterceptorResource, :mappings => @args[:mappings], :log_to => @args[:log_to])
      end
      app ? app.call(env) : @app.call(env)
    end
  end
end

How to install dav4rack dependency

Every way I tried to install the dependency just leads to:

git://github.com/chrisroberts/dav4rack.git (at master) is not checked out. Please run bundle install
when running rake db:migrate_plugins RAILS_ENV=production as www-data.

I tried to install:

As root, gem install dav4rack, installed successfully but migration fails with message above.
Removed dev4rack using gem uninstall dav4rack

Then (still as root) ran bundle install --without=xapian in plugins/redmine_dmsf. Bundle list shows it's installed. Bundle list ran as www-data shows it's installed.

And finally bundle install --without=xapian as www-data just gives Errno::EACCES: Permission denied - dav4rack-0.2.11.gem. So I guess it can only be run as root.

My environment:

ruby -v
ruby 1.9.3p194 (2012-04-20) [i686-linux]

bundle -v
Bundler version 1.1.4

rake --version
rake, version 0.9.2.2

gem -v
1.8.23

CardDAV extension

I am working on implementing carddav support on top of dav4rack and was wondering if a carddav implementation would be accepted as a pull request.

I suppose it could be done in an external gem but I think it makes a lot of sense to have it directly into dav4rack, what do you think ?

PS: and yes I saw meishi but I need a modular library not a fully functional rails application, I tried to base my work on it but in the end I feel like I am going to rewrite most of it.

Controller#xml_convert doesn't properly serialize nodes with text children

If I return an element like this (from, say, get_property on my resource):

builder = Nokogiri::XML::Builder.new do | xml |
  xml.href "/user"
end
return builder.doc.root

it will be serialized as "<D:href/>" without the text content

It looks like this is because xml_convert doesn't serialize the text part element.text? returns false (which it does on the href element, since it's not a text node, even though it does have a text child)

Rails 3.x app with Devise and WebDav

I would like to enable WebDav for Rails 3.1 application, where I use Devise for authentication and CanCan for authorization.

So far I turned on WebDav and works fine. Is it possible to use Devise logins/users with dav4rack ? An then, enable/restrict access to certain files/directories on WebDav for users? Maybe there is already a howto or wiki page about that? Any help will be appreciated.

PS. is this the right way of solving this kind of problem? maybe i should switch back to apache mod_dav and synchronise apache's dav users with devise users?

Ruby 3.0 support

I see that there hasn't been a lot of activity here, but we still use this gem to provide WebDav sync for our users. Now we would like to migrate to Ruby 3.0, however there are 2 function calls that were deprecated and are removed in ruby 3.0. It is the URI.escape and URI.unescape method. (https://github.com/chrisroberts/dav4rack/blob/master/lib/dav4rack/controller.rb#L35 and https://github.com/chrisroberts/dav4rack/blob/master/lib/dav4rack/controller.rb#L45).

I am fine with opening an PR to fix this, would you be willing to update them gem in that case? What are those two lines supposed to do? Can they be replaced with URI.encode_www_form or is it encoding an full URI (so including the domain and path). I can open an PR but need to understand it a bit more.

Would be great to hear something and see if we can make it ruby 3.0 compatible

Setup message broken by default

In the current version, if one creates a custom resource class, resource#initializer will rename the "setup" method of the custom resource class unless it is added to skip_alias, so only the first request will succeed, thereafter "NoMethodErrors" without a caller will appear which are a PITA to debug. Default behavior should have the setup method already in @skip_alias.

Just add :setup to @skip_alias in resource.rb, line 52.

Authentication types

Is there a way to implement other authentication methods instead Basic auth (username and password)?
In example OpenID Connect, oauth 2 etc, which should redirect the auth to external identity provider (Google auth, Facebook auth, custom auth)

Properly implement locking for non-existent resources

DAV4Rack::Controller#lock raises a NotFound response when the resource doesn't exist. This isn't correct behavior, per RFP4918 sections 7.3 Write Locks and Unmapped URLs and 9.10.4 Locking Unmapped URLs: http://www.webdav.org/specs/rfc4918.html Finder on Mac OS X will not allow you to create files and will revert to treating the server as read-only when it doesn't properly implement this spec form of locking.

I believe the best course of action may be to allow the resource.lock(asked) call, as the resource should decide if it should create itself as a blank file (generally correct per the spec) or do something else.

Is there any reason we can't start by removing the raise NotFound unless resource.exist? line, as resource.exist? has no knowledge that this is an attempt at locking?

Nautilus answering "Response Invalid" on Rails 4 while Rails 3 works.

Hello,

I tested the basic Rails tutorial here https://github.com/chrisroberts/dav4rack/wiki/Rails-3 with success on Rails 3.

However, using Rails 4, if I try to browse Rails app directory via Nautilus webdav, I can get the list of files at the root, such as app, db, config etc... However, if I click on any of these files or subdirectory, then Nautilus gives me the following error:
"Unhandled error message: Response invalid"

Using tcpdump, I registered the whole communication, connecting to the root Rails app directory (OK) and then trying to click on the db subdirectory (KO)

Hope this help. Here is the tcdump output (sudo tcpdump -i lo -A):

16:28:10.746912 IP localhost.50779 > raphael-laptop.domain: 12293+ A? daisy.ubuntu.com. (34)
E..>..@.@...........[.5._.=0............daisy.ubuntu.com.....
16:28:10.746933 IP localhost.50779 > raphael-laptop.domain: 58388+ AAAA? daisy.ubuntu.com. (34)
E..>..@.@.
..........[.5..=.............daisy.ubuntu.com.....
16:28:10.751589 IP raphael-laptop.domain > localhost.50779: 12293 2/0/0 A 91.189.95.55, A 91.189.95.54 (66)
E..^..@.@.;..........5.[.J.]0............daisy.ubuntu.com.................[.7............[.6
16:28:10.752486 IP raphael-laptop.domain > localhost.50779: 58388 0/0/0 (34)
E..>..@.@.;..........5.[.
.=.............daisy.ubuntu.com.....
16:28:11.749756 IP localhost.52432 > raphael-laptop.domain: 9993+ A? daisy.ubuntu.com. (34)
E..>..@.@.}............5.
.=' ...........daisy.ubuntu.com.....
16:28:11.749801 IP localhost.52432 > raphael-laptop.domain: 57388+ AAAA? daisy.ubuntu.com. (34)
E..>..@.@.}............5.
.=.,...........daisy.ubuntu.com.....
16:28:11.752905 IP raphael-laptop.domain > localhost.52432: 9993 2/0/0 A 91.189.95.54, A 91.189.95.55 (66)
E..^..@.@.;..........5...J.]' ...........daisy.ubuntu.com.................[.6............[.7
16:28:11.753471 IP raphael-laptop.domain > localhost.52432: 57388 0/0/0 (34)
E..>..@.@.;..........5...
.=.,...........daisy.ubuntu.com.....
16:28:30.878026 IP localhost.53277 > localhost.53277: UDP, length 16
E..,.E@[email protected]...............+........j.m.....
16:28:30.896867 IP localhost.53277 > localhost.53277: UDP, length 24
E..4.F@[email protected]............. .3........./..q.....u.....
16:28:30.902344 IP localhost.53277 > localhost.53277: UDP, length 16
E..,.G@[email protected]...............+..........u.....
16:28:30.912905 IP localhost.53277 > localhost.53277: UDP, length 976
E....H@[email protected]........................../.. ...................................................................................................................".......".......f
..q.......................................................................................................q...................................................................................................c
..q...................................................................................................2
..q...................................................................................................w
..q...................................................................................................)
..q...................................................................................................8
.......................................................................................................
..q...................................................................................................
16:28:30.912924 IP localhost.53277 > localhost.53277: UDP, length 248
E....I@[email protected]........................../..............................+
......................................................................................................

..q...................................................................................................
16:28:30.912930 IP localhost.53277 > localhost.53277: UDP, length 456
E....J@.@.7.................................................................................................................................................................p
......................................................................................................o
..q.......................................................................................................q...................................................................................................
16:28:34.912542 IP localhost.46441 > localhost.46441: UDP, length 16
E..,..@[email protected]..........i.i...+........Z.......
16:28:37.185812 IP localhost.52976 > raphael-laptop.3000: Flags [S], seq 1992207733, win 43690, options [mss 65495,sackOK,TS val 2217650 ecr 0,nop,wscale 7], length 0
E..<{.@[email protected].........
.!..........
16:28:37.185833 IP raphael-laptop.3000 > localhost.52976: Flags [S.], seq 3776194973, ack 1992207734, win 43690, options [mss 65495,sackOK,TS val 2217650 ecr 2217650,nop,wscale 7], length 0
E..<..@.@.;...............).v..v.....0.........
.!...!......
16:28:37.185847 IP localhost.52976 > raphael-laptop.3000: Flags [.], ack 1, win 342, options [nop,nop,TS val 2217650 ecr 2217650], length 0
E..4{.@[email protected]..)....V.(.....
.!...!..
16:28:37.186102 IP localhost.52976 > raphael-laptop.3000: Flags [P.], seq 1:164, ack 1, win 342, options [nop,nop,TS val 2217650 ecr 2217650], length 163
E...{.@[email protected]..)....V.......
.!...!..OPTIONS / HTTP/1.1
Host: webdav.lvh.me:3000
Accept-Encoding: gzip, deflate
User-Agent: gvfs/1.16.1
Accept-Language: en-us, en;q=0.9
Connection: Keep-Alive

16:28:37.186117 IP raphael-laptop.3000 > localhost.52976: Flags [.], ack 164, win 350, options [nop,nop,TS val 2217650 ecr 2217650], length 0
E..4.,@.@.<...............).v......^.(.....
.!...!..
16:28:37.192532 IP raphael-laptop.3000 > localhost.52976: Flags [P.], seq 1:365, ack 164, win 350, options [nop,nop,TS val 2217651 ecr 2217650], length 364
E....-@.@.;(..............).v......^.......
.!...!..HTTP/1.1 200 OK
Dav: 1, 2
Allow: OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK
Ms-Author-Via: DAV
Cache-Control: no-cache
X-Request-Id: dbf89bc3-1aa0-40dd-b9f5-670b6d616190
X-Runtime: 0.004844
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-04-20)
Date: Sat, 27 Jul 2013 19:28:37 GMT
Content-Length: 0
Connection: Keep-Alive

16:28:37.192569 IP localhost.52976 > raphael-laptop.3000: Flags [.], ack 365, win 350, options [nop,nop,TS val 2217651 ecr 2217651], length 0
E..4{.@[email protected].....+
...^.(.....
.!...!..
16:28:37.193060 IP localhost.52976 > raphael-laptop.3000: Flags [P.], seq 164:390, ack 365, win 350, options [nop,nop,TS val 2217651 ecr 2217651], length 226
E...{.@[email protected].....+
...^.......
.!...!..PROPFIND / HTTP/1.1
Host: webdav.lvh.me:3000
Depth: 0
Content-Type: application/xml
Accept-Encoding: gzip, deflate
User-Agent: gvfs/1.16.1
Accept-Language: en-us, en;q=0.9
Connection: Keep-Alive
Content-Length: 124

16:28:37.229628 IP raphael-laptop.3000 > localhost.52976: Flags [.], ack 390, win 359, options [nop,nop,TS val 2217661 ecr 2217651], length 0
E..4..@.@.<...............+
v......g.(.....
.!...!..
16:28:37.229707 IP localhost.52976 > raphael-laptop.3000: Flags [P.], seq 390:514, ack 365, win 350, options [nop,nop,TS val 2217661 ecr 2217661], length 124
E...{.@[email protected].....+
...^.......
.!...!..
<D:propfind xmlns:D="DAV:">
<D:prop>
<D:resourcetype/>
/D:prop
/D:propfind
16:28:37.229728 IP raphael-laptop.3000 > localhost.52976: Flags [.], ack 514, win 359, options [nop,nop,TS val 2217661 ecr 2217661], length 0
E..4./@.@.<...............+
v..w...g.(.....
.!...!..
16:28:37.239824 IP raphael-laptop.3000 > localhost.52976: Flags [P.], seq 365:651, ack 514, win 359, options [nop,nop,TS val 2217663 ecr 2217661], length 286
E..R.0@.@.;s..............+
v..w...g.G.....
.!...!..HTTP/1.1 207
Content-Type: text/xml; charset="utf-8"
Content-Length: 263
Cache-Control: no-cache
X-Request-Id: 60e452e9-762a-424a-b694-d7f2bba3fa44
X-Runtime: 0.008770
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-04-20)
Date: Sat, 27 Jul 2013 19:28:37 GMT
Connection: Keep-Alive

16:28:37.277654 IP localhost.52976 > raphael-laptop.3000: Flags [.], ack 651, win 359, options [nop,nop,TS val 2217673 ecr 2217663], length 0
E..4{.@[email protected]..,(...g.(.....
.!...!..
16:28:37.277739 IP raphael-laptop.3000 > localhost.52976: Flags [P.], seq 651:914, ack 514, win 359, options [nop,nop,TS val 2217673 ecr 2217673], length 263
E..;.1@.@.;...............,(v..w...g.0.....
.!...!..
<D:multistatus xmlns:D="DAV:"><D:response><D:href>http://webdav.lvh.me:3000//D:href<D:propstat><D:prop><D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response/D:multistatus

16:28:37.277750 IP localhost.52976 > raphael-laptop.3000: Flags [.], ack 914, win 367, options [nop,nop,TS val 2217673 ecr 2217673], length 0
E..4{.@[email protected]/...o.(.....
.!...!..
16:28:37.345359 IP localhost.52976 > raphael-laptop.3000: Flags [P.], seq 514:766, ack 914, win 367, options [nop,nop,TS val 2217689 ecr 2217673], length 252
E..0{.@[email protected]/...o.%.....
.!...!..PROPFIND / HTTP/1.1
Host: webdav.lvh.me:3000
Depth: 0
Content-Type: application/xml
Apply-To-Redirect-Ref: T
Accept-Encoding: gzip, deflate
User-Agent: gvfs/1.16.1
Accept-Language: en-us, en;q=0.9
Connection: Keep-Alive
Content-Length: 235

16:28:37.381620 IP raphael-laptop.3000 > localhost.52976: Flags [.], ack 766, win 367, options [nop,nop,TS val 2217699 ecr 2217689], length 0
E..4.2@.@.<...............-/v..s...o.(.....
.!...!..
16:28:37.381680 IP localhost.52976 > raphael-laptop.3000: Flags [P.], seq 766:1001, ack 914, win 367, options [nop,nop,TS val 2217699 ecr 2217699], length 235
E...{.@[email protected]/...o.......
.!...!..
<D:propfind xmlns:D="DAV:">
<D:prop>
<D:creationdate/>
<D:displayname/>
<D:getcontentlength/>
<D:getcontenttype/>
<D:getetag/>
<D:getlastmodified/>
<D:resourcetype/>
/D:prop
/D:propfind
16:28:37.381695 IP raphael-laptop.3000 > localhost.52976: Flags [.], ack 1001, win 375, options [nop,nop,TS val 2217699 ecr 2217699], length 0
E..4.3@.@.<...............-/v..^...w.(.....
.!...!..
16:28:37.388145 IP raphael-laptop.3000 > localhost.52976: Flags [P.], seq 914:1200, ack 1001, win 375, options [nop,nop,TS val 2217700 ecr 2217699], length 286
E..R.4@.@.;o..............-/v..^...w.G.....
.!...!..HTTP/1.1 207
Content-Type: text/xml; charset="utf-8"
Content-Length: 539
Cache-Control: no-cache
X-Request-Id: 4d0b1528-f718-4f4c-bf59-e84f78851c3d
X-Runtime: 0.005268
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-04-20)
Date: Sat, 27 Jul 2013 19:28:37 GMT
Connection: Keep-Alive

16:28:37.388276 IP localhost.52976 > raphael-laptop.3000: Flags [.], ack 1200, win 375, options [nop,nop,TS val 2217700 ecr 2217700], length 0
E..4{.@[email protected]..^...M...w.(.....
.!...!..
16:28:37.388411 IP raphael-laptop.3000 > localhost.52976: Flags [P.], seq 1200:1739, ack 1001, win 375, options [nop,nop,TS val 2217700 ecr 2217700], length 539
E..O.5@.@.:q...............Mv..^...w.D.....
.!...!..
<D:multistatus xmlns:D="DAV:"><D:response><D:href>http://webdav.lvh.me:3000//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T16:23:43-03:00/D:creationdate<D:displayname/><D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67250c-1000-51f41e3f/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 19:23:43 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response/D:multistatus

16:28:37.388467 IP localhost.52976 > raphael-laptop.3000: Flags [.], ack 1739, win 384, options [nop,nop,TS val 2217700 ecr 2217700], length 0
E..4{.@[email protected]..^..0h.....(.....
.!...!..
16:28:37.474072 IP localhost.52976 > raphael-laptop.3000: Flags [P.], seq 1001:1253, ack 1739, win 384, options [nop,nop,TS val 2217722 ecr 2217700], length 252
E..0{.@[email protected]..^..0h.....%.....
.!...!..PROPFIND / HTTP/1.1
Host: webdav.lvh.me:3000
Depth: 1
Content-Type: application/xml
Apply-To-Redirect-Ref: T
Accept-Encoding: gzip, deflate
User-Agent: gvfs/1.16.1
Accept-Language: en-us, en;q=0.9
Connection: Keep-Alive
Content-Length: 235

16:28:37.513623 IP raphael-laptop.3000 > localhost.52976: Flags [.], ack 1253, win 384, options [nop,nop,TS val 2217732 ecr 2217722], length 0
E..4.6@.@.<...............0hv..Z.....(.....
.!...!..
16:28:37.513676 IP localhost.52976 > raphael-laptop.3000: Flags [P.], seq 1253:1488, ack 1739, win 384, options [nop,nop,TS val 2217732 ecr 2217732], length 235
E...{.@[email protected]...........
.!...!..
<D:propfind xmlns:D="DAV:">
<D:prop>
<D:creationdate/>
<D:displayname/>
<D:getcontentlength/>
<D:getcontenttype/>
<D:getetag/>
<D:getlastmodified/>
<D:resourcetype/>
/D:prop
/D:propfind
16:28:37.513685 IP raphael-laptop.3000 > localhost.52976: Flags [.], ack 1488, win 392, options [nop,nop,TS val 2217732 ecr 2217732], length 0
E..4.7@.@.<...............0hv..E.....(.....
.!...!..
16:28:37.535576 IP raphael-laptop.3000 > localhost.52976: Flags [P.], seq 1739:2026, ack 1488, win 392, options [nop,nop,TS val 2217737 ecr 2217732], length 287
E..S.8@.@.;j..............0hv..E.....H.....
.!. .!..HTTP/1.1 207
Content-Type: text/xml; charset="utf-8"
Content-Length: 7906
Cache-Control: no-cache
X-Request-Id: 7037bf03-0a10-4743-8c14-52029616ad08
X-Runtime: 0.021030
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-04-20)
Date: Sat, 27 Jul 2013 19:28:37 GMT
Connection: Keep-Alive

16:28:37.535614 IP localhost.52976 > raphael-laptop.3000: Flags [.], ack 2026, win 392, options [nop,nop,TS val 2217737 ecr 2217737], length 0
E..4{.@[email protected]......(.....
.!. .!.
16:28:37.535648 IP raphael-laptop.3000 > localhost.52976: Flags [P.], seq 2026:9932, ack 1488, win 392, options [nop,nop,TS val 2217737 ecr 2217737], length 7906
E....9@[email protected]...........
.!. .!.
<D:multistatus xmlns:D="DAV:"><D:response><D:href>http://webdav.lvh.me:3000//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T16:23:43-03:00/D:creationdate<D:displayname/><D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67250c-1000-51f41e3f/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 19:23:43 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/tmp//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:21:30-03:00/D:creationdate<D:displayname>tmp/D:displayname<D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67a2f6-1000-51f4019a/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:21:30 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/README.rdoc/D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:20:06-03:00/D:creationdate<D:displayname>README.rdoc/D:displayname<D:getcontentlength>478/D:getcontentlength<D:getcontenttype>application/octet-stream/D:getcontenttype<D:getetag>671d06-1de-51f40146/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:20:06 GMT/D:getlastmodified<D:resourcetype/>/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/log//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:21:30-03:00/D:creationdate<D:displayname>log/D:displayname<D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67a2ed-1000-51f4019a/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:21:30 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/test//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:20:06-03:00/D:creationdate<D:displayname>test/D:displayname<D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67a2ef-1000-51f40146/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:20:06 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/app//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:20:06-03:00/D:creationdate<D:displayname>app/D:displayname<D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67250d-1000-51f40146/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:20:06 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/Gemfile/D:href<D:propstat><D:prop><D:creationdate>2013-07-27T16:23:43-03:00/D:creationdate<D:displayname>Gemfile/D:displayname<D:getcontentlength>1178/D:getcontentlength<D:getcontenttype>application/octet-stream/D:getcontenttype<D:getetag>67255b-49a-51f41e3f/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 19:23:43 GMT/D:getlastmodified<D:resourcetype/>/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/bin//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:20:06-03:00/D:creationdate<D:displayname>bin/D:displayname<D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67850e-1000-51f40146/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:20:06 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/lib//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:20:06-03:00/D:creationdate<D:displayname>lib/D:displayname<D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67a2ea-1000-51f40146/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:20:06 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/config//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T16:23:46-03:00/D:creationdate<D:displayname>config/D:displayname<D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67850f-1000-51f41e42/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 19:23:46 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/Gemfile.lock/D:href<D:propstat><D:prop><D:creationdate>2013-07-27T16:23:57-03:00/D:creationdate<D:displayname>Gemfile.lock/D:displayname<D:getcontentlength>2906/D:getcontentlength<D:getcontenttype>application/octet-stream/D:getcontenttype<D:getetag>671d55-b5a-51f41e4d/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 19:23:57 GMT/D:getlastmodified<D:resourcetype/>/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/Rakefile/D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:20:06-03:00/D:creationdate<D:displayname>Rakefile/D:displayname<D:getcontentlength>254/D:getcontentlength<D:getcontenttype>application/octet-stream/D:getcontenttype<D:getetag>671d07-fe-51f40146/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:20:06 GMT/D:getlastmodified<D:resourcetype/>/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/db//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:20:06-03:00/D:creationdate<D:displayname>db/D:displayname<D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67a2e9-1000-51f40146/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:20:06 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/public//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:20:06-03:00/D:creationdate<D:displayname>public/D:displayname<D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67a2ee-1000-51f40146/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:20:06 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/config.ru/D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:20:06-03:00/D:creationdate<D:displayname>config.ru/D:displayname<D:getcontentlength>154/D:getcontentlength<D:getcontenttype>application/octet-stream/D:getcontenttype<D:getetag>671d0a-9a-51f40146/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:20:06 GMT/D:getlastmodified<D:resourcetype/>/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response<D:response><D:href>http://webdav.lvh.me:3000/vendor//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:20:06-03:00/D:creationdate<D:displayname>vendor/D:displayname<D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67a2f9-1000-51f40146/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:20:06 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response/D:multistatus

16:28:37.535658 IP localhost.52976 > raphael-laptop.3000: Flags [.], ack 9932, win 990, options [nop,nop,TS val 2217737 ecr 2217737], length 0
E..4{.@[email protected].....(.....
.!. .!.
16:28:55.324110 IP localhost.52976 > raphael-laptop.3000: Flags [P.], seq 1488:1742, ack 9932, win 1024, options [nop,nop,TS val 2222184 ecr 2217737], length 254
E..2{.@[email protected].....'.....
.!.h.!. PROPFIND /db HTTP/1.1
Host: webdav.lvh.me:3000
Depth: 0
Content-Type: application/xml
Apply-To-Redirect-Ref: T
Accept-Encoding: gzip, deflate
User-Agent: gvfs/1.16.1
Accept-Language: en-us, en;q=0.9
Connection: Keep-Alive
Content-Length: 235

16:28:55.361615 IP raphael-laptop.3000 > localhost.52976: Flags [.], ack 1742, win 400, options [nop,nop,TS val 2222194 ecr 2222184], length 0
E..4.:@.@.<...............Piv..C.....(.....
.!.r.!.h
16:28:55.361684 IP localhost.52976 > raphael-laptop.3000: Flags [P.], seq 1742:1977, ack 9932, win 1024, options [nop,nop,TS val 2222194 ecr 2222194], length 235
E...{.@[email protected]...........
.!.r.!.r
<D:propfind xmlns:D="DAV:">
<D:prop>
<D:creationdate/>
<D:displayname/>
<D:getcontentlength/>
<D:getcontenttype/>
<D:getetag/>
<D:getlastmodified/>
<D:resourcetype/>
/D:prop
/D:propfind
16:28:55.361699 IP raphael-laptop.3000 > localhost.52976: Flags [.], ack 1977, win 409, options [nop,nop,TS val 2222194 ecr 2222194], length 0
E..4.;@.@.<...............Piv........(.....
.!.r.!.r
16:28:55.367334 IP raphael-laptop.3000 > localhost.52976: Flags [P.], seq 9932:10218, ack 1977, win 409, options [nop,nop,TS val 2222195 ecr 2222194], length 286
E..R.<@.@.;g..............Piv........G.....
.!.s.!.rHTTP/1.1 207
Content-Type: text/xml; charset="utf-8"
Content-Length: 558
Cache-Control: no-cache
X-Request-Id: 31798bd5-132d-43a5-a1e1-276c07969aec
X-Runtime: 0.004951
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-04-20)
Date: Sat, 27 Jul 2013 19:28:55 GMT
Connection: Keep-Alive

16:28:55.367372 IP localhost.52976 > raphael-laptop.3000: Flags [.], ack 10218, win 1022, options [nop,nop,TS val 2222195 ecr 2222195], length 0
E..4{.@[email protected]......(.....
.!.s.!.s
16:28:55.367406 IP raphael-laptop.3000 > localhost.52976: Flags [P.], seq 10218:10776, ack 1977, win 409, options [nop,nop,TS val 2222195 ecr 2222195], length 558
E..b.=@.@.:V..............Q.v........W.....
.!.s.!.s
<D:multistatus xmlns:D="DAV:"><D:response><D:href>http://webdav.lvh.me:3000db//D:href<D:propstat><D:prop><D:creationdate>2013-07-27T14:20:06-03:00/D:creationdate<D:displayname>db/D:displayname<D:getcontentlength>4096/D:getcontentlength<D:getcontenttype>text/html/D:getcontenttype<D:getetag>67a2e9-1000-51f40146/D:getetag<D:getlastmodified>Sat, 27 Jul 2013 17:20:06 GMT/D:getlastmodified<D:resourcetype><D:collection/>/D:resourcetype/D:prop<D:status>HTTP/1.1 200 OK/D:status/D:propstat/D:response/D:multistatus

16:28:55.367417 IP localhost.52976 > raphael-laptop.3000: Flags [.], ack 10776, win 1018, options [nop,nop,TS val 2222195 ecr 2222195], length 0
E..4{.@[email protected]......(.....
.!.s.!.s
16:29:25.404283 IP raphael-laptop.3000 > localhost.52976: Flags [F.], seq 10776, ack 1977, win 409, options [nop,nop,TS val 2229704 ecr 2222195], length 0
E..4.>@.@.<...............S.v........(.....
."...!.s
16:29:25.441652 IP localhost.52976 > raphael-laptop.3000: Flags [.], ack 10777, win 1019, options [nop,nop,TS val 2229714 ecr 2229704], length 0
E..4{.@[email protected]......(.....
."..."..
16:29:30.885301 IP localhost.53277 > localhost.53277: UDP, length 16
E..,.K@[email protected]...............+................
16:29:30.897234 IP localhost.53277 > localhost.53277: UDP, length 24
E..4.L@[email protected]............. .3........./..q....k .....
16:29:30.900663 IP localhost.53277 > localhost.53277: UDP, length 16
E..,.M@[email protected]...............+........;Q .....
16:29:30.911981 IP localhost.53277 > localhost.53277: UDP, length 976
E....N@[email protected]........................../.. ...................................................................................................................".......".......f
..q.......................................................................................................q...................................................................................................c
..q...................................................................................................2
..q...................................................................................................w
..q...................................................................................................)
..q...................................................................................................8
.......................................................................................................
..q...................................................................................................
16:29:30.912029 IP localhost.53277 > localhost.53277: UDP, length 248
E....O@[email protected]........................../..............................+
......................................................................................................_
..q...................................................................................................
16:29:30.912052 IP localhost.53277 > localhost.53277: UDP, length 456
E....P@.@.7.................................................................................................................................................................p
......................................................................................................o
..q.......................................................................................................q...................................................................................................
16:29:34.982058 IP localhost.46441 > localhost.46441: UDP, length 16
E..,..@[email protected]..........i.i...+..........@.....

Dav4Rack gem incorrectly processes folders with spaces (and many other issues)

Dear Chris,

Below described bug (or set of bugs) found in Dav4Rack gem, which is working inside Redmine's plugin DMFS. Even if you know, what is Redmine (most probably), I assume that you haven't heard about DMSF plugin. DMSF - coolest functionality extention for Redmine, allowing company to organize internal documents storage directly in Redmine. And one of DMSF's killer features is ability to access stored documents via WebDav protocol. So, as you understand, mentioned WebDav support is realized via your plugin Dav4Rack (as Redmine is written in Ruby). So, now you know our stack. Let's describe bugs, let’s go through all of them, one-by-one:

  • When folder (or file) is created via DMSF's Web interface, and after that deleted - it's still visible via WebDav protocol (only via Windows Explorer!!!). However, columns Size, Type and Last Modified contains error message - see below screenshot:

image

From the other side, accessing the same folder via Total Commander's plugin WebDav (WebDAV / 2.5 / 2013/11/18) shows folder's content absolutely correctly - dropped folder is not visible, all is correct.

  • Next. Trying to access WebDav folder via Total Commander's plugin WebDav (folder name - /dmsf/webdav/f-0004/Fiplan - Internal Documents (e.g. spaces in folder's name) - Dav4Rack crashes, spooling following stack trace into production.log:
Started PROPFIND "/redmine/dmsf/webdav/f-0004/Fiplan%20-%20Internal%20Documents/" for 127.0.0.1 at 2015-01-27 00:41:49 +0100

NoMethodError (undefined method `xmlschema' for DAV4Rack::HTTPStatus::NotFound:Class):
  dav4rack (0.3.0) lib/dav4rack/resource.rb:367:in `get_property'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/dmsf_resource.rb:558:in `get_property'
  dav4rack (0.3.0) lib/dav4rack/resource.rb:116:in `method_missing'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/resource_proxy.rb:151:in `get_property'
  dav4rack (0.3.0) lib/dav4rack/resource.rb:116:in `method_missing'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:510:in `block in get_properties'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:508:in `each'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:508:in `get_properties'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/controller.rb:67:in `block (3 levels) in propfind'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:391:in `call'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:391:in `insert'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:375:in `method_missing'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/controller.rb:61:in `block (2 levels) in propfind'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/controller.rb:60:in `each'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/controller.rb:60:in `block in propfind'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:468:in `block (2 levels) in render_xml'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:391:in `call'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:391:in `insert'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:375:in `method_missing'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:465:in `block in render_xml'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:293:in `initialize'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:464:in `new'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:464:in `render_xml'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:487:in `multistatus'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/controller.rb:59:in `propfind'
  dav4rack (0.3.0) lib/dav4rack/handler.rb:30:in `call'
  journey (1.0.4) lib/journey/router.rb:68:in `block in call'
  journey (1.0.4) lib/journey/router.rb:56:in `each'
  journey (1.0.4) lib/journey/router.rb:56:in `call'
  actionpack (3.2.19) lib/action_dispatch/routing/route_set.rb:608:in `call'
  rack-openid (1.4.1) lib/rack/openid.rb:99:in `call'
  request_store (1.0.5) lib/request_store/middleware.rb:9:in `call'
  actionpack (3.2.19) lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
  rack (1.4.5) lib/rack/etag.rb:23:in `call'
  rack (1.4.5) lib/rack/conditionalget.rb:35:in `call'
  actionpack (3.2.19) lib/action_dispatch/middleware/head.rb:14:in `call'
  actionpack (3.2.19) lib/action_dispatch/middleware/params_parser.rb:21:in `call'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/no_parse.rb:33:in `call'
  actionpack (3.2.19) lib/action_dispatch/middleware/flash.rb:242:in `call'
  rack (1.4.5) lib/rack/session/abstract/id.rb:210:in `context'
  rack (1.4.5) lib/rack/session/abstract/id.rb:205:in `call'
  actionpack (3.2.19) lib/action_dispatch/middleware/cookies.rb:341:in `call'
  activerecord (3.2.19) lib/active_record/query_cache.rb:64:in `call'
  activerecord (3.2.19) lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in `call'
  actionpack (3.2.19) lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
  activesupport (3.2.19) lib/active_support/callbacks.rb:405:in `_run__307596463__call__409538604__callbacks'
  activesupport (3.2.19) lib/active_support/callbacks.rb:405:in `__run_callback'
  activesupport (3.2.19) lib/active_support/callbacks.rb:385:in `_run_call_callbacks'
  activesupport (3.2.19) lib/active_support/callbacks.rb:81:in `run_callbacks'
  actionpack (3.2.19) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  actionpack (3.2.19) lib/action_dispatch/middleware/remote_ip.rb:31:in `call'
  actionpack (3.2.19) lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call'
  actionpack (3.2.19) lib/action_dispatch/middleware/show_exceptions.rb:56:in `call'
  railties (3.2.19) lib/rails/rack/logger.rb:32:in `call_app'
  railties (3.2.19) lib/rails/rack/logger.rb:16:in `block in call'
  activesupport (3.2.19) lib/active_support/tagged_logging.rb:22:in `tagged'
  railties (3.2.19) lib/rails/rack/logger.rb:16:in `call'
  actionpack (3.2.19) lib/action_dispatch/middleware/request_id.rb:22:in `call'
  rack (1.4.5) lib/rack/methodoverride.rb:21:in `call'
  rack (1.4.5) lib/rack/runtime.rb:17:in `call'
  activesupport (3.2.19) lib/active_support/cache/strategy/local_cache.rb:72:in `call'
  rack (1.4.5) lib/rack/lock.rb:15:in `call'
  actionpack (3.2.19) lib/action_dispatch/middleware/static.rb:63:in `call'
  rack-cache (1.2) lib/rack/cache/context.rb:136:in `forward'
  rack-cache (1.2) lib/rack/cache/context.rb:143:in `pass'
  rack-cache (1.2) lib/rack/cache/context.rb:155:in `invalidate'
  rack-cache (1.2) lib/rack/cache/context.rb:71:in `call!'
  rack-cache (1.2) lib/rack/cache/context.rb:51:in `call'
  railties (3.2.19) lib/rails/engine.rb:484:in `call'
  railties (3.2.19) lib/rails/application.rb:231:in `call'
  railties (3.2.19) lib/rails/railtie/configurable.rb:30:in `method_missing'
  rack (1.4.5) lib/rack/urlmap.rb:64:in `block in call'
  rack (1.4.5) lib/rack/urlmap.rb:49:in `each'
  rack (1.4.5) lib/rack/urlmap.rb:49:in `call'
  thin-1.6.1-x86 (mingw32) lib/thin/connection.rb:82:in `block in pre_process'
  thin-1.6.1-x86 (mingw32) lib/thin/connection.rb:80:in `catch'
  thin-1.6.1-x86 (mingw32) lib/thin/connection.rb:80:in `pre_process'
  thin-1.6.1-x86 (mingw32) lib/thin/connection.rb:55:in `process'
  thin-1.6.1-x86 (mingw32) lib/thin/connection.rb:41:in `receive_data'
  eventmachine-1.0.3-x86 (mingw32) lib/eventmachine.rb:187:in `run_machine'
  eventmachine-1.0.3-x86 (mingw32) lib/eventmachine.rb:187:in `run'
  thin-1.6.1-x86 (mingw32) lib/thin/backends/base.rb:73:in `start'
  thin-1.6.1-x86 (mingw32) lib/thin/server.rb:162:in `start'
  thin-1.6.1-x86 (mingw32) lib/thin/controllers/controller.rb:87:in `start'
  thin-1.6.1-x86 (mingw32) lib/thin/runner.rb:200:in `run_command'
  thin-1.6.1-x86 (mingw32) lib/thin/runner.rb:156:in `run!'
  thin-1.6.1-x86 (mingw32) bin/thin:6:in `<top (required)>'
  bin/thin:16:in `load'
  bin/thin:16:in `<main>'
  • However, in the same time, accessing the same folder via any browser (Chrome in my case) - all is working fine, Chrome can traverse into folder Fiplan - Internal Documents, containing spaces in the name. During that action, Dav4Rack is spooling different command into production.log:
Started GET "/redmine/dmsf/webdav/f-0004/Fiplan%20-%20Internal%20Documents" for 127.0.0.1 at 2015-01-27 00:55:33 +0100

(e.g. TC's plugin send PROPFIND, however, Browser - GET)

  • And now - finally, complete mess - from that point I do not understand anything... Look. I rename my folder Fiplan - Internal Documents and remove all spaces: FiplanInternalDocument. Trying to get into it via Browser (Chrome) - all OK, as before. The same via Total Commander's WebDav plugin - error is the same:
Started PROPFIND "/redmine/dmsf/webdav/f-0004/FiplanInternalDocuments/" for 127.0.0.1 at 2015-01-27 01:06:07 +0100

NoMethodError (undefined method `xmlschema' for DAV4Rack::HTTPStatus::NotFound:Class):
  dav4rack (0.3.0) lib/dav4rack/resource.rb:367:in `get_property'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/dmsf_resource.rb:558:in `get_property'
  dav4rack (0.3.0) lib/dav4rack/resource.rb:116:in `method_missing'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/resource_proxy.rb:151:in `get_property'
  dav4rack (0.3.0) lib/dav4rack/resource.rb:116:in `method_missing'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:510:in `block in get_properties'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:508:in `each'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:508:in `get_properties'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/controller.rb:67:in `block (3 levels) in propfind'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:391:in `call'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:391:in `insert'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:375:in `method_missing'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/controller.rb:61:in `block (2 levels) in propfind'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/controller.rb:60:in `each'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/controller.rb:60:in `block in propfind'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:468:in `block (2 levels) in render_xml'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:391:in `call'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:391:in `insert'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:375:in `method_missing'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:465:in `block in render_xml'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:293:in `initialize'

<... cut ...>

Now I made folder's name shorter: FiInDo - result again is the same - Brower can access such folder's name TC - can't:

Started PROPFIND "/redmine/dmsf/webdav/f-0004/FiInDo/" for 127.0.0.1 at 2015-01-27 01:09:11 +0100

NoMethodError (undefined method `xmlschema' for DAV4Rack::HTTPStatus::NotFound:Class):
  dav4rack (0.3.0) lib/dav4rack/resource.rb:367:in `get_property'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/dmsf_resource.rb:558:in `get_property'
  dav4rack (0.3.0) lib/dav4rack/resource.rb:116:in `method_missing'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/resource_proxy.rb:151:in `get_property'
  dav4rack (0.3.0) lib/dav4rack/resource.rb:116:in `method_missing'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:510:in `block in get_properties'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:508:in `each'
  dav4rack (0.3.0) lib/dav4rack/controller.rb:508:in `get_properties'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/controller.rb:67:in `block (3 levels) in propfind'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:391:in `call'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:391:in `insert'
  nokogiri-1.6.5-x86 (mingw32) lib/nokogiri/xml/builder.rb:375:in `method_missing'
  plugins/redmine_dmsf/lib/redmine_dmsf/webdav/controller.rb:61:in `block (2 levels) in propfind

<... cut ...>

So, at that moment I start to crash my keyboard and scream on monitor.... :) We need your help, Chris...

P.S. I really hope, that you can still provide support for your project Dav4Rack, as, accoring to your repositiry, last activity with Dav4Rack was 2 years ago... However, it's clear, that you continue to support your other projects. So, all DMSF community is waiting for your reply, keeping fingers crossed!!!

P.P.S. As I was not sure initially, where is issue, on which level - with TC's plugin WebDav, with DMSF or with Dav4Rack - I have already started discussion with Mr. Ghisler (developer of Total Commander, www.ghisler.ch) - see http://www.ghisler.ch/board/viewtopic.php?t=40800 (however, discussion with him continued via e-mails, as his blog's engine (phpBB) is very old and I can't post logs - blog recognize my logs as URLs and says that I am spammer and declines me - sad). Mr. Ghisler's founding is following - he pointed that difference between plugin WebDav and Chrome is, that Chrome adds "/" (slash) at the end of all paths. From my side I can comment, that difference is also in command - WebDav plugin sends command PROPFIND, however, Chrome sends GET.

P.P.P.S. There is issue opened in DMSF's Issue list on GitHub (opened by another guy), where I have posted my issues, as topicstarted was experienced similar issues as me: danmunn/redmine_dmsf#334

Return empty file during GET(Rails3.0.5, Passenger 3.0.6)

I installed dav4rack to my rails project.
routes.rb:

mount DAV4Rack::Handler.new(:root => File.join(Rails.root, 'uploads'), :root_uri_path => "/box/webdav", :resource_class => ::DAV4Rack::FileResource), :at => 'webdav'

Project installed under Phusion Passenger 3.0.6 under subfolder:
RackBaseURI /box
<Directory /var/www/box>
PassengerResolveSymlinksInDocumentRoot on
AllowOverride all
Options -MultiViews

I upload files via Rails controller and then try to download them via WebDav.
I always get empty file.
Sometimes(I couldn't catch crash point) I can properly get file which was uploaded via WebDav.
I checked difference between these files(which was uploaded via rails controller and WebDav) - they looks the same.

Rack: rack (1.2.2, 1.2.1, 1.0.1)
dav4rack: dav4rack (0.2.3)

It looks like passenger incorrectly streaming Rack::File
Because all works properly under Webrick

Update specs

Update specs from original rack_dav to test new and original features

Returning anything but "Created" for resource put request results in nil-error

I have a custom resource that only allows certain content types to be uploaded. Everything works fine until the error handling kicks in. I wanted the Dav4Rack to return either Forbidden or UnspportedMediaType, but I will ran into the following error when returning anything else to the put request but "Created" or "NoContent".

My Code (TemplateResource#put):

def put(reuqest, response)
    tempfile = Tempfile.new(object_id)

    # Read the contents
    while part = request.body.read(8192)
      tempfile.write(part)
    end

    # Build an uploaded file as a normal rails request
    # would do to give Paperclip a way to access file
    # meta data
    @template.template = ActionDispatch::Http::UploadedFile.new({
      :filename => path.split('/').last,
      :tempfile => tempfile
    })

    if @template.save
      Created
    else
      UnsupportedMediaType
    end
  ensure
    tempfile.close
    tempfile.unlink        
  end

Backtrace: (Mac OS 10.7.1, Rails 3.1, dav4rack is straight from GIT)

[2011-09-24 17:36:36] INFO WEBrick 1.3.1
[2011-09-24 17:36:36] INFO ruby 1.8.7 (2010-01-10) [universal-darwin11.0]
[2011-09-24 17:36:36] INFO WEBrick::HTTPServer#start: pid=3890 port=3000
[2011-09-24 17:36:46] ERROR NoMethodError: undefined method bytesize' for nil:NilClass /Library/Ruby/Gems/1.8/gems/activesupport-3.1.0/lib/active_support/whiny_nil.rb:48:inmethod_missing'
/Library/Ruby/Gems/1.8/gems/rack-1.3.3/lib/rack/utils.rb:256:in bytesize' /Library/Ruby/Gems/1.8/gems/railties-3.1.0/lib/rails/rack/content_length.rb:28:incall'
/Library/Ruby/Gems/1.8/gems/rack-1.3.3/lib/rack/response.rb:86:in each' /Library/Ruby/Gems/1.8/gems/rack-1.3.3/lib/rack/response.rb:86:ineach'
/Library/Ruby/Gems/1.8/gems/activerecord-3.1.0/lib/active_record/query_cache.rb:45:in each' /Library/Ruby/Gems/1.8/gems/activerecord-3.1.0/lib/active_record/connection_adapters/abstract/connection_pool.rb:458:ineach'
/Library/Ruby/Gems/1.8/gems/rack-1.3.3/lib/rack/body_proxy.rb:26:in __send__' /Library/Ruby/Gems/1.8/gems/rack-1.3.3/lib/rack/body_proxy.rb:26:inmethod_missing'
/Library/Ruby/Gems/1.8/gems/railties-3.1.0/lib/rails/rack/content_length.rb:26:in call' /Library/Ruby/Gems/1.8/gems/railties-3.1.0/lib/rails/rack/log_tailer.rb:14:incall'
/Library/Ruby/Gems/1.8/gems/rack-1.3.3/lib/rack/handler/webrick.rb:59:in service' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/httpserver.rb:104:inservice'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/httpserver.rb:65:in run' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:173:instart_thread'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:162:in start' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:162:instart_thread'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:95:in start' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:92:ineach'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:92:in start' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:23:instart'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:82:in start' /Library/Ruby/Gems/1.8/gems/rack-1.3.3/lib/rack/handler/webrick.rb:13:inrun'
/Library/Ruby/Gems/1.8/gems/rack-1.3.3/lib/rack/server.rb:265:in start' /Library/Ruby/Gems/1.8/gems/railties-3.1.0/lib/rails/commands/server.rb:70:instart'
/Library/Ruby/Gems/1.8/gems/railties-3.1.0/lib/rails/commands.rb:54
/Library/Ruby/Gems/1.8/gems/railties-3.1.0/lib/rails/commands.rb:49:in tap' /Library/Ruby/Gems/1.8/gems/railties-3.1.0/lib/rails/commands.rb:49 script/rails:6:inrequire'
script/rails:6

URIs with special chars are not escaped before given to the web server

I first found this problem while working with custom resources. In order to make sure it had nothing to do with my code, I made my Rails app directory a DAV4Rack::FileResource but the problem persists, if you try to do anything with URIs that contain spaces or other characters that would require escape.

I tried doing the escaping on my own (in Resource#name i.e.) but ran into several other errors that made me believe the way it is intended to be, is not to handle the escape myself, but for some deeper layer to do it, as is done for unescaping, too?

Backtrace (Mac OS 10.7.1, Ruby 1.8.7, Rails 3.1, DAV4Rack from git, Rack 1.3.3):

Started PUT "/public/unbenannte%20Datei" for 127.0.0.1 at Mon Sep 26 21:38:13 +0200 2011
[2011-09-26 21:38:13] ERROR URI::InvalidURIError: bad URI(is not URI?): http://files.shipyard.cornerstoreapp.local:3000/public/unbenannte Datei
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/uri/generic.rb:732:in merge' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/httpresponse.rb:164:insetup_header'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/httpresponse.rb:102:in send_response' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/httpserver.rb:79:inrun'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:173:in start_thread' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:162:instart'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:162:in start_thread' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:95:instart'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:92:in each' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:92:instart'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:23:in start' /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/server.rb:82:instart'
/Library/Ruby/Gems/1.8/gems/rack-1.3.3/lib/rack/handler/webrick.rb:13:in run' /Library/Ruby/Gems/1.8/gems/rack-1.3.3/lib/rack/server.rb:265:instart'
/Library/Ruby/Gems/1.8/gems/railties-3.1.0/lib/rails/commands/server.rb:70:in start' /Library/Ruby/Gems/1.8/gems/railties-3.1.0/lib/rails/commands.rb:54 /Library/Ruby/Gems/1.8/gems/railties-3.1.0/lib/rails/commands.rb:49:intap'
/Library/Ruby/Gems/1.8/gems/railties-3.1.0/lib/rails/commands.rb:49
script/rails:6:in `require'
script/rails:6

multi-status (207) for PUT requests

First, thanks for dav4rack, it is certainly making my life easier.

I'm working on providing a dav interface for a rails application, and am running litmus (http://www.webdav.org/neon/litmus/) for testing against dav4rack with my custom resource. Digging into the source, I see that controller.rb is always returning a multi-status as a response to valid put requests. This triggers a warning in litmus, saying the correct response should be 201 (Created). Also, I believe the WebDAV spec makes mention that a 201 status should not be included in a multi-status response because it is the default success code. I'm very new to WebDAV, so I wasn't sure if your implementation is because there is a client implementation that is expecting a multi-status response even though that isn't what the spec calls for? If not, I am working on a patch, which will return 201 for successful put requests and will issue a pull request when I'm finished. Thanks.

windows xp

Hi,
did anyone managed to make it work with windows xp ?
I did many tests and cannot find a configuration which is working with it :/

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.