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

в 7:32, , рубрики: nginx, webclient, webdav, windows, windows Веб Клиенn, системное администрирование

В 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

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

Перед тем как создать файл 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

Как подружить nginx и встроенный веб клиент
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

Создание папок.
Как подружить nginx и встроенный веб клиент
См п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_MACHINESYSTEMCurrentControlSetservicesWebClientParameters]
"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

Автор: Ivan_83

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js