Какую кодировку ожидает xsendfile?

Похоже, что при поиске проблем с xsendfile в Google выдается ряд противоречивых/устаревших обращений.

В интересах полного раскрытия информации я рассказываю о бета-версии xsendfile 1.0, задокументированной по адресу https://tn123.org/mod_xsendfile/beta/ (этот сайт не часто появляется в результатах поиска, только тот, у которого нет /beta). Я использую его с apache 2.4 и php 5.4.34 как в Linux, так и в Windows. Помимо того, что это последняя версия, мне нужно использовать бета-версию, потому что только на бета-сайте есть двоичные файлы Windows, созданные с помощью VC9 для apache 2.4.

Я сделал ошибку, прочитав документацию, где в описании значения имени файла в шапке написано:

Предполагается, что значение (имя файла), указанное в заголовке, закодировано в URL-адресе, т. е. будет выполнено неэкранирование/декодирование URL-адреса. См. XSendFileUnescape. Если вам случится хранить файлы, используя имена файлов, уже закодированные в URL-адресе, вы должны «двойно» кодировать имена... %20 -> %2520

И описание XSendFIleUnescape говорит:

Отключение XSendFileUnescape восстановит поведение до 1.0 с использованием необработанного значения заголовка, вместо того, чтобы сначала пытаться unescape/url-decode.

В документации об относительных путях ясно указано, что имя файла в заголовке X-SendFile должно быть полным путем. Поэтому я тщательно прогнал свои пути через функцию urlencode php.

Конечным результатом для меня неизменно была внутренняя ошибка сервера (код состояния 500) как в Linux, так и в Windows. Когда у меня была директива XSendFilePath в контексте server config, что, согласно документации, разрешено, я не получил ничего более конкретного в своем журнале ошибок. Но когда я (в конце концов) переместил эту директиву в контекст Directory, я получил это в своем журнале ошибок:

(404)Unknown error: [client 127.0.0.1:20742] xsendfile: bad file name encoding

В конце концов, в отчаянии, я сказал «к черту документацию» и удалил urlencode в имени пути. И вдруг он заработал отлично (и Windows, и Linux)!!!

У меня нет путей с символами, отличными от ASCII, так что все готово. Но мне интересно, какая кодировка должна применяться, чтобы разрешить работу символов, отличных от ASCII. Если вы погуглите xsendfile: bad file name encoding, вы найдете следующий исходный код по адресу https://github.com/nmaier/mod_xsendfile/blob/master/mod_xsendfile.c, где эта строка сообщения создается путем выбора истинной ветви:

   rv = ap_unescape_url(file);
      if (rv != OK) {

Но я не могу найти хорошее описание или исходный код для ap_unescape_url(). Если исходный код на github не устарел, эта функция возражает против простого %-кодирования, которое выполняет функция PHP urlencode(). В качестве дикой догадки я попытался вызвать ap_escape_url(), но он не определен в PHP. Таким образом, остается вопрос о том, какая кодировка предполагается применяться к параметру пути в заголовке X-SendFile??

Еще одно наблюдение/вопрос Описание XSendFile, использующего «внутренние компоненты apache» для отправки файла, может натолкнуть вас на мысль, что он создаст заголовок Content-Type из расширения имени файла, используя mod_mime. Но на самом деле это не так, и примеры показывают явный вызов header() для Content-Type. Итак, мое продолжение: каков «правильный» способ построить этот заголовок из имени пути, передаваемого в X-SendFile, чтобы он гарантированно соответствовал тому, что сделал бы mod_mime, если бы мы не использовали X-SendFile? Лучшее, что я мог придумать, это следующий код, использующий расширение PHP fileinfo, но, насколько я знаю, нет особых причин ожидать, что он действительно будет соответствовать тому, что делает apache, когда ему дается URL-адрес для имени файла.

    $finfo = new finfo(FILEINFO_MIME);
    $mime_info = $finfo->file($pathname);
    if (! strlen($mime_info)) {
        $mime_info = 'application/octet-stream; charset=binary';
    }
    $basename = basename($pathname);
    $encoded = "$pathname";
    header("Content-Type: $mime_info");
    header("Content-Disposition: attachment; filename=\"$basename\"");
    header("X-SendFile: $encoded");

person sootsnoot    schedule 05.11.2014    source источник


Ответы (1)


Я не собираюсь принимать это как ответ на вопрос, потому что я до сих пор не нашел «ту» функцию кодирования, которая, как ожидается, будет использоваться. Но я нашел эту древнюю документацию по Apache API: http://pedrowa.weba.sk/docs/ApiDoc/apidoc_ap_unescape_url.html. Он документирует возвращаемое значение ap_unescape_url() как:

Возвращает 0 в случае успеха, BAD_REQUEST, если найдена неправильная управляющая последовательность, или NOT_FOUND, если найден %2F (/).

Но, конечно, в PHP и urlencode(), и rawurlencode() кодируют '/' как %2F.

Совершенно очевидно, что любое полное имя пути, используемое в заголовке XSendFile, НЕ должно кодироваться с использованием любой из этих функций!!

Мое предположение относительно «лучшего» решения для меня:

$encoded = str_replace('%2F', '/', rawurlencode($pathname));

Должен признаться, я поражен тем, что в документации XSendFile об этом не упоминается. И еще больше я поражен, что вопрос не получил здесь ответа. Должен ли я был разместить это на другом сайте Stack Exchange?

person sootsnoot    schedule 13.11.2014