You're right; cadaver *does* require OPTIONS to function.
More details:
The cadaver client initializes the connection by calling `open_connection` (the is the cadaver client) -> `ne_options` (just a wrapper) -> `ne_options2` -> `ne_request_dispatch` -> `ne_discard_response` -> `ne_read_response_block` -> `read_response_block`. The last function `read_response_block` behaves exactly like 'man read(3)': returns <0 in error or reads a chunk of data, possibly less than requested. The function `ne_discard_response` loops until all the data is read and returns NE_OK, or NE_ERROR if there was an error in `read_response_block`. This status is bubbles back to `ne_options2` which re-interprets NE_OK with a non-200 class HTTP response code as NE_ERROR. The cadaver client in `open_connection` requires NE_OK (i.e. HTTP 200) in order to continue, so OPTIONS must be supported (`ne_options` only creates an OPTIONS request). The `--tolerant` option is only applied by cadaver (via the `set_path` function) upon receiving NE_OK.
While cadaver won't work with any servers not supporting OPTIONS, Total Commander closes the connection by sending RST rather than FIN/ACK. Even though the end-result is the same, it shouldn't happen:
Regular WebDav server without OPTIONS:
Total Commander:
Out-of-the box, the nginx webdav module does not support either PROPFIND or OPTIONS. I used this for my tests. Configuration below:
Code: Select all
daemon off;
worker_processes auto;
error_log nginx.logs/error.log;
pid nginx.temp/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
load_module <path-to:ngx_http_dav_ext_module.so>;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log nginx.logs/access.log main;
client_body_temp_path nginx.temp/client_body;
proxy_temp_path nginx.temp/proxy;
fastcgi_temp_path nginx.temp/fastcgi;
uwsgi_temp_path nginx.temp/uwsgi;
scgi_temp_path nginx.temp/scgi;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
server {
listen 8080 default_server;
listen [::]:8080 default_server;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
location /dav {
alias <path-to:webdav-root>;
dav_methods PUT DELETE MKCOL COPY MOVE;
dav_ext_methods PROPFIND OPTIONS;
dav_access user:rw group:rw all:rw;
create_full_put_path on;
# non-dav module options
client_max_body_size 0;
autoindex on;
allow 192.168.1.0/24;
allow 127.0.0.1;
deny all;
}
}
}
I then added PROPFIND and OPTIONS support to webdav via the nginx-dav-ext-module module [1]. The case for `NGX_HTTP_OPTIONS` seems relatively simple, so it should be a good guide to implementing OPTIONS in Total Commander. Unfortunately this nginx module requires both OPTIONS and PROPFIND; only enabling OPTIONS in 'nginx.conf' via `dav_ext_methods OPTIONS;` breaks the WebDav implementation. The server claims to support PROPFIND (even though it is disabled), so cadaver receives an HTTP 405 when it sends a PROPFIND request, leading to this message:
Code: Select all
Ignored error: /dav/source/ not WebDAV-enabled:
405 Not Allowed
In any case I've attached the wireshark trace and cadaver log from cadaver interacting with a server supporting OPTIONS and PROPFIND:
- Cadaver response:
Code: Select all
$ cadaver -t http://localhost:8080/dav/source
dav:/dav/source/> ls
Listing collection `/dav/source/': succeeded.
...
dav:/dav/source/> quit
Connection to `localhost' closed.
- NGINX logs:
Code: Select all
$ cat nginx.logs/access.log
127.0.0.1 - - [13/Sep/2017:16:16:51 -0400] "OPTIONS /dav/source/ HTTP/1.1" 200 0 "-" "cadaver/0.23.3 neon/0.30.1" "-"
127.0.0.1 - - [13/Sep/2017:16:16:51 -0400] "PROPFIND /dav/source/ HTTP/1.1" 207 608 "-" "cadaver/0.23.3 neon/0.30.1" "-"
127.0.0.1 - - [13/Sep/2017:16:16:53 -0400] "PROPFIND /dav/source/ HTTP/1.1" 207 3752 "-" "cadaver/0.23.3 neon/0.30.1" "-"
- Screenshot of wireshark trace showing regular WebDav server supporing OPTIONS+PROPFIND, as well as the extracted TCP stream:
Image: https://s26.postimg.org/cqevw8u87/cadaver-nginx.OPTIONS.PROPFIND-wireshark.png
- This is the wireshark trace. The server responses are indented and the final server response listing each directory entry is truncated because it is very long.
Code: Select all
OPTIONS /dav/source/ HTTP/1.1
User-Agent: cadaver/0.23.3 neon/0.30.1
Keep-Alive:
Connection: TE, Keep-Alive
TE: trailers
Host: localhost:8080
HTTP/1.1 200 OK
Server: nginx/1.13.5
Date: Wed, 13 Sep 2017 20:18:31 GMT
Content-Length: 0
Connection: keep-alive
DAV: 1
Allow: GET,HEAD,PUT,DELETE,MKCOL,COPY,MOVE,PROPFIND,OPTIONS
PROPFIND /dav/source/ HTTP/1.1
User-Agent: cadaver/0.23.3 neon/0.30.1
Connection: TE
TE: trailers
Host: localhost:8080
Depth: 0
Content-Length: 288
Content-Type: application/xml
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:"><prop>
<getcontentlength xmlns="DAV:"/>
<getlastmodified xmlns="DAV:"/>
<executable xmlns="http://apache.org/dav/props/"/>
<resourcetype xmlns="DAV:"/>
<checked-in xmlns="DAV:"/>
<checked-out xmlns="DAV:"/>
</prop></propfind>
HTTP/1.1 207 Multi-Status
Server: nginx/1.13.5
Date: Wed, 13 Sep 2017 20:18:31 GMT
Transfer-Encoding: chunked
Connection: keep-alive
47
<?xml version="1.0" encoding="utf-8" ?>
<D:multistatus xmlns:D="DAV:">
1f0
<D:response>
<D:href>/dav/source/</D:href>
<D:propstat>
<D:prop>
<D:creationdate>2017-09-13T19:54:27Z</D:creationdate>
<D:displayname></D:displayname>
<D:getcontentlanguage/>
<D:getcontentlength>182</D:getcontentlength>
<D:getcontenttype/>
<D:getetag/>
<D:getlastmodified>Wed, 13 Sep 2017 19:54:27 GMT</D:getlastmodified>
<D:lockdiscovery/>
<D:resourcetype><D:collection/></D:resourcetype>
<D:source/>
<D:supportedlock/>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
11
</D:multistatus>
0
PROPFIND /dav/source/ HTTP/1.1
User-Agent: cadaver/0.23.3 neon/0.30.1
Connection: TE
TE: trailers
Host: localhost:8080
Depth: 1
Content-Length: 288
Content-Type: application/xml
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:"><prop>
<getcontentlength xmlns="DAV:"/>
<getlastmodified xmlns="DAV:"/>
<executable xmlns="http://apache.org/dav/props/"/>
<resourcetype xmlns="DAV:"/>
<checked-in xmlns="DAV:"/>
<checked-out xmlns="DAV:"/>
</prop></propfind>
HTTP/1.1 207 Multi-Status
Server: nginx/1.13.5
Date: Wed, 13 Sep 2017 20:18:33 GMT
Transfer-Encoding: chunked
Connection: keep-alive
47
<?xml version="1.0" encoding="utf-8" ?>
<D:multistatus xmlns:D="DAV:">
1f0
<D:response>
<D:href>/dav/source/</D:href>
<D:propstat>
<D:prop>
<D:creationdate>2017-09-13T19:54:27Z</D:creationdate>
<D:displayname></D:displayname>
<D:getcontentlanguage/>
<D:getcontentlength>182</D:getcontentlength>
<D:getcontenttype/>
<D:getetag/>
<D:getlastmodified>Wed, 13 Sep 2017 19:54:27 GMT</D:getlastmodified>
<D:lockdiscovery/>
<D:resourcetype><D:collection/></D:resourcetype>
<D:source/>
<D:supportedlock/>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
...
... (
... truncated: it lists all the entries, which is really long and not
... really on-topic.
... )
...
1.
https://github.com/arut/nginx-dav-ext-module