Как подружить nginx и встроенный веб клиент

от автора

В Microsoft Windows начиная с 2к встроен Веб Клиент, по сути это средство для монтирования сетевых дисков по протоколу WebDav, который ходит поверх HTTP/HTTPS.
Клиент писался явно под себя (связка с IIS) и от того работает весьма корявосвоеобразно.
Это единственный способ «малой» кровью примонтировать диск через интернет не настраивая VPN для проброса SAMBA протокола.
SAMB-у в принципе тоже без VPN можно пробросить, но это из области поиска приключений с последствиями.

Nginx в базовом функционале имеет не полную поддержку WebDav: PUT DELETE MKCOL COPY MOVE.
Расширить его ещё двумя: PROPFIND и OPTIONS можно с помощью плагина: dav-ext

Дальше будет описание как заставить виндовый WebDav клиент работать с Nginx колдуя только в конфиге.
+ пара мелких багов Nginx.

Для работы потребуется 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

Проблема 1

Майкрософт нарушает стандарты и свои обещания. (как обычно)

Перед тем как создать файл 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.
с точки зрения стандартов: tools.ietf.org/html/rfc2518#page-24
с точки зрения доков мс: msdn.microsoft.com/en-us/library/aa142960(v=exchg.65).aspx
У яндекса тоже в примерах xml: 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/INSURGENCIES AND COUNTERING INSURGENCIES.pdf - ...........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 клиенту винды.

Фиксим в конфиге (возможно это сведёт с ума остальные, порядочные WebDav клиенты, но лично мне нужен был только один не такой как все):

Фикс 1

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; }

Проблема 2


WebDav от мс очень хочет метод PROPPATCH, которого в Nginx и расширениях нет. Совсем нет.
Я рассматривал два варианта решения:
1. Написать плагин к Nginx или патч к dav-ext, короче Си код и пересборка Nginx.
2. Положится на кривость виндовой реализации WebDav и скормить статический ответ.

Запрос

PROPPATCH /Family/INSURGENCIES%20AND%20COUNTERING%20INSURGENCIES.pdf%20-%20%D0%AF%D1%80%D0%BB%D1%8B%D0%BA.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//!!!!/INSURGENCIES%20AND%20COUNTERING%20INSURGENCIES.pdf%20-%20%D0%AF%D1%80%D0%BB%D1%8B%D0%BA.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.
Минусом — не выставляется оригинальная дата создания, модификации и атрибуты. Я переживу.

Проблема 3

Создание папок.

См п1 🙂

Приходит запрос

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, смотреть там не на что.

Как должно быть описано:
У майкрософта: msdn.microsoft.com/en-us/library/aa142923(v=exchg.65).aspx
У яндекса: api.yandex.ru/disk/doc/dg/reference/mkcol.xml
И даже в RFC: tools.ietf.org/html/rfc2518#page-33

Во всех примерах URL оканчивается слешем.
Но только не в запросе WebDav клиента от мс.

Вариантов опять было два:
1. Поправить файл: 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 плагин.

Проблема 4

Удаление папок.
Ноги тут те же что и в п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

Не трудно заметить, что nginx не добавляет в ответы:
Content-Type: text/xml
и хотя в данном случае это не создаёт проблем, но всё же это не правильно.

Это уже третий раз когда гибкость nginx позволила мне получить результат не используя языки программирования, Игорь — молодец!
Прошлые разы я сделал кешируюший прокси с фильтрацией по урл и UPnP/DLNA сервер.

Конфиг nginx для WebDav

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гб передавать не получится. (Скорее всего из за кривой реализации, которая файлы при открытии скачивает в память/на диск чтобы получить более менее стандартный файловый дискриптор, это мои домыслы.)

Вот здесь собраны все настройки с описанием: 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

ссылка на оригинал статьи http://habrahabr.ru/post/232883/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *