This is an old revision of the document!
В Microsoft Windows начиная с 2к встроен Веб Клиент, по сути это средство для монтирования сетевых дисков по протоколу WebDav, который ходит поверх HTTP/HTTPS.
Клиент писался явно под себя (связка с IIS) и от того работает весьма корявосвоеобразно.
Это единственный способ “малой” кровью примонтировать диск через интернет не настраивая VPN для проброса SAMBA протокола.
SAMB-у в принципе тоже без VPN можно пробросить, но это из области поиска приключений с последствиями.
Nginx в базовом функционале имеет не полную поддержку WebDav: PUT DELETE MKCOL COPY MOVE.
Расширить его ещё двумя: PROPFIND и OPTIONS можно с помощью плагина: dav-ext
Для работы потребуется Nginx собранный с поддержкой WebDav, dav-ext модулем и rewrite.
[x] HTTP_DAV Enable http_webdav module [x] HTTP_DAV_EXT 3rd party webdav_ext module [x] HTTP_REWRITE Enable http_rewrite module
Перед тем как создать файл WebDav клиент проверяет наличие файла посылая запрос:
PROPFIND /!!!!/test.lnk HTTP/1.1 Connection: Keep-Alive User-Agent: Microsoft-WebDAV-MiniRedir/6.1.7601 Depth: 0 translate: f Content-Length: 0 Host: 172.16.0.254:8089
В ответ IIS ему выдаёт:
HTTP/1.1 404 Resource Not Found Content-Length: 1635 Content-Type: text/html Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET Date: Sun, 10 Aug 2014 20:06:08 GMT <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> ...
Только это не правильно, должен быть код 207 и xml в котором описан элемент и код для него, те 404 должно быть в xml.
с точки зрения стандартов: http://tools.ietf.org/html/rfc2518#page-24
с точки зрения доков мс: http://msdn.microsoft.com/en-us/library/aa142960(v=exchg.65).aspx
У яндекса тоже в примерах xml: http://api.yandex.ru/disk/doc/dg/reference/propfind_property-request.xml
Ответ Nginx:
HTTP/1.1 207 Multi-Status Server: nginx/1.7.4 Date: Sun, 10 Aug 2014 21:17:34 GMT Transfer-Encoding: chunked Connection: keep-alive Keep-Alive: timeout=60 47 <?xml version="1.0" encoding="utf-8" ?> <D:multistatus xmlns:D="DAV:"> cc <D:response> <D:href>/Family/test.lnk</D:href> <D:propstat> <D:prop> </D:prop> <D:status>HTTP/1.1 404 Not Found</D:status> </D:propstat> </D:response> 11 </D:multistatus> 0
Такой “неожиданный” ответ сносит голову WebDav клиенту винды.
Фикс 1: в конфиге (возможно это сведёт с ума остальные, порядочные WebDav клиенты, но лично мне нужен был только один не такой как все):
error_page 599 = @propfind_handler; if ($request_method = PROPFIND) { return 599; } location @propfind_handler { internal; open_file_cache off; if (!-e $webdav_root/$uri) { # Microsoft specific handle. return 404; } root $webdav_root; dav_ext_methods PROPFIND; }
WebDav от мс очень хочет метод PROPPATCH, которого в Nginx и расширениях нет. Совсем нет.
Я рассматривал два варианта решения:
1. Написать плагин к Nginx или патч к dav-ext, короче Си код и пересборка Nginx.
2. Положится на кривость виндовой реализации WebDav и скормить статический ответ.
Запрос:
PROPPATCH /Family/test.lnk HTTP/1.1 Cache-Control: no-cache Connection: Keep-Alive Pragma: no-cache Content-Type: text/xml; charset="utf-8" User-Agent: Microsoft-WebDAV-MiniRedir/6.1.7601 translate: f Content-Length: 443 Host: xxx.xxx.net <?xml version="1.0" encoding="utf-8" ?><D:propertyupdate xmlns:D="DAV:" xmlns:Z="urn:schemas-microsoft-com:"><D:set><D:prop><Z:Win32CreationTime>Sun, 10 Aug 2014 21:30:21 GMT</Z:Win32CreationTime><Z:Win32LastAccessTime>Sun, 10 Aug 2014 21:30:22 GMT</Z:Win32LastAccessTime><Z:Win32LastModifiedTime>Sun, 10 Aug 2014 21:30:22 GMT</Z:Win32LastModifiedTime><Z:Win32FileAttributes>00000020</Z:Win32FileAttributes></D:prop></D:set></D:propertyupdate>
Ответ IIS:
HTTP/1.1 207 Multi-Status Date: Sun, 10 Aug 2014 12:24:47 GMT Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET Content-Type: text/xml Transfer-Encoding: chunked <?xml version="1.0"?><a:multistatus xmlns:a="DAV:" xmlns:b="urn:schemas-microsoft-com:"><a:response><a:href>http://172.16.0.254:8088//!!!!/test.lnk</a:href><a:propstat><a:status>HTTP/1.1 200 OK</a:status><a:prop><b:Win32CreationTime/><b:Win32LastAccessTime/><b:Win32LastModifiedTime/><b:Win32FileAttributes/></a:prop></a:propstat></a:response></a:multistatus> 0
“Пропритериарщина”, - подумал я. Вот кому такое вообще надо? И как эти все атрибуты писать в разных ОС и разных ФС?…
Фикс 2
if ($request_method = PROPPATCH) { # Unsupported, allways return OK. add_header Content-Type 'text/xml'; return 207 '<?xml version="1.0"?><a:multistatus xmlns:a="DAV:"><a:response><a:propstat><a:status>HTTP/1.1 200 OK</a:status></a:propstat></a:response></a:multistatus>'; }
Из ответа IIS я выкинул всё что мне не понравилось и виндовый WebDav это проглотил.
Теперь одиночные и группы файлов без проблем можно копировать на сетевой диск примонтированный по WebDav.
Минусом - не выставляется оригинальная дата создания, модификации и атрибуты.
Приходит запрос:
MKCOL /Family/MR3020 HTTP/1.1 Connection: Keep-Alive User-Agent: Microsoft-WebDAV-MiniRedir/6.1.7601 translate: f Content-Length: 0 Host: xxx.xxx.net
Nginx на него отвечает (немного странно выбирая код, на мой взгляд, но в принципе правильно):
HTTP/1.1 409 Conflict Server: nginx/1.7.4 Date: Sun, 10 Aug 2014 21:43:15 GMT Content-Type: text/html Content-Length: 166 Connection: keep-alive Keep-Alive: timeout=60 <html> <head><title>409 Conflict</title></head> <body bgcolor="white"> <center><h1>409 Conflict</h1></center> <hr><center>nginx/1.7.4</center> </body> </html>
IIS отвечает: “HTTP/1.1 201 Created”, - смотреть там не на что.
Как должно быть описано:
У майкрософта: http://msdn.microsoft.com/en-us/library/aa142923(v=exchg.65).aspx
У яндекса: http://api.yandex.ru/disk/doc/dg/reference/mkcol.xml
И даже в RFC: http://tools.ietf.org/html/rfc2518#page-33
Во всех примерах URL оканчивается слешем.
Но только не в запросе WebDav клиента от мс.
Вариантов опять было два:
1. Поправить файл: http://lxr.nginx.org/source/src/http/modules/ngx_http_dav_module.c строчки 484 - 493, там как раз проверка наличия слеша и его отрезание.
2. Пофиксить через конфиг.
Вариант 1 я оставляю на усмотрение програмеров nginx, может по стандарту оно и должно ругаться.
Фикс 3:
if ($request_method = MKCOL) { # Microsoft specific handle: add trailing slash. rewrite ^(.*[^/])$ $1/; }
Вот для этого пустяка и потребовался REWRITE плагин.
Ноги тут те же что и в п3: отсутствие слеша на конце урла.
Однако я столкнулся с тем, что nginx тоже ведёт себя несколько странно.
1. Если слеш на конце отсутствует, то nginx считает что его просят удалить файл, и получает ошибку: 21: Is a directory при попытке удалить.
2. Если слеш добавить то папку он почему то так и не удаляет. Подозреваю тут где то баг самого nginx.
Фикс 4:
error_page 598 = @delete_handler; if ($request_method = DELETE) { return 598; } location @delete_handler { internal; open_file_cache off; if (-d $webdav_root/$uri) { # Add trailing slash to dirs. rewrite ^(.*[^/])$ $1/; } root $webdav_root; dav_methods DELETE; }
Если кратко, то переносим обработку DELETE в отдельный локейшин (процедуру), дальше проверяем, если удаляется папка то добавляем слеш.
Не трудно заметить, что nginx не добавляет в ответы:
Content-Type: text/xml
и хотя в данном случае это не создаёт проблем, но всё же это не правильно.
WebDav для Nginx:
# WebDAV set $webdav_root "/mnt/WebDav_folder"; location ^~ /Family { root $webdav_root; error_page 599 = @propfind_handler; error_page 598 = @delete_handler; chunked_transfer_encoding on; open_file_cache off; client_max_body_size 50m; add_header Allow 'OPTIONS, GET, HEAD, DELETE, PUT, COPY, MOVE, PROPFIND'; if ($request_method = PROPFIND) { return 599; } if ($request_method = PROPPATCH) { # Unsupported, allways return OK. add_header Content-Type 'text/xml'; return 207 '<?xml version="1.0"?><a:multistatus xmlns:a="DAV:"><a:response><a:propstat><a:status>HTTP/1.1 200 OK</a:status></a:propstat></a:response></a:multistatus>'; } if ($request_method = MKCOL) { # Microsoft specific handle: add trailing slash. rewrite ^(.*[^/])$ $1/; } if ($request_method = DELETE) { return 598; } dav_methods PUT MKCOL COPY MOVE; # dav_ext_methods OPTIONS; create_full_put_path on; min_delete_depth 0; dav_access user:rw group:rw all:rw; } location @propfind_handler { internal; open_file_cache off; if (!-e $webdav_root/$uri) { # Microsoft specific handle. return 404; } root $webdav_root; dav_ext_methods PROPFIND; } location @delete_handler { internal; open_file_cache off; if (-d $webdav_root/$uri) { # Add trailing slash to dirs. rewrite ^(.*[^/])$ $1/; } root $webdav_root; dav_methods DELETE; }
open_file_cache off; - нужно потому что мы меняем файлы, если кеширование включено то можно будет долго гадать почему не видно файлы которые мы только что закинули на сервер.
Он как капризный ребёнок, из коробки ему подавай SSL и никакой Basic аутентификации, файлы не больше 50 мегабайт, в папках не более 500-1000 файлов.
Увы, но даже после всех проделанных настроек файлы более 4гб передавать не получится. (Скорее всего из за кривой реализации, которая файлы при открытии скачивает в память/на диск чтобы получить более менее стандартный файловый дискриптор, это мои домыслы.)
Вот здесь собраны все настройки с описанием: http://blogs.msdn.com/b/robert_mcmurray/archive/2008/01/17/webdav-redirector-registry-settings.aspx
На данный момент мои настройки WebClient выглядят так:
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WebClient\Parameters] "SupportLocking"=dword:00000000 "InternetServerTimeoutInSec"=dword:0000001e "ServiceDllUnloadOnStop"=dword:00000001 "ServerNotFoundCacheLifeTimeInSec"=dword:0000000a "ClientDebug"=dword:00000000 "FileSizeLimitInBytes"=dword:ffffffff "SendReceiveTimeoutInSec"=dword:0000003c "LocalServerTimeoutInSec"=dword:0000000f "FileAttributesLimitInBytes"=dword:00989680 "AcceptOfficeAndTahoeServers"=dword:00000001 "ServiceDebug"=dword:00000000 "BasicAuthLevel"=dword:00000002