Почему ASP.NET заменяет заголовок Content-Length заголовком Transfer-Encoding при ручной очистке ответа? - PullRequest
17 голосов
/ 21 декабря 2011

В нашем веб-приложении (ASP.NET Web Forms) есть страница, на которой пользователям будет отображаться недавно созданный файл PDF.Поскольку PDF-файл иногда бывает довольно большим, мы реализовали «потоковый» подход для отправки его клиентскому браузеру порциями.

Несмотря на отправку данных порциями, мы знаем полный размерфайл до его отправки, поэтому мы правильно установили заголовок Content-Length.Это работает в нашей производственной среде некоторое время (и продолжает работать в нашей тестовой среде с практически идентичной конфигурацией) до сегодняшнего дня.Описанная проблема заключалась в том, что Chrome пытался открыть файл PDF, но зависал с застрявшей анимацией «Загрузка».

Поскольку в нашей тестовой среде все еще работало нормально, я смог использовать Firebug, чтобы посмотретьв заголовках ответа, которые возвращались в обеих средах.В тестовой среде я видел правильный заголовок «Content-Length», а в производстве его заменили на Transfer-Encoding: chunked header.Chrome это не нравится, поэтому зависание.

Я читал некоторые статьи и посты о том, как заголовок Transfer-Encoding может отображаться, если заголовок Content-Length не указан, но мыуказав заголовок Content-Length и все по-прежнему работает при выполнении одного и того же кода для одного и того же файла PDF на тестовом сервере.

И на тестовом, и на рабочем серверах работает IIS 7.5, и на обоих включены динамическое и статическое сжатие.

Вот код, о котором идет речь:

var fileInfo = new FileInfo(fileToSendDown);
Response.ClearHeaders();
Response.ContentType = "application/pdf";            
Response.AddHeader("Content-Disposition", "filename=test.pdf");
Response.AddHeader("Content-Length", fileInfo.Length.ToString());
var buffer = new byte[1024];
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    int read;
    while ((read = fs.Read(buffer, 0, 1024)) > 0)
    {
        if (!response.IsClientConnected) break;
        Response.OutputStream.Write(buffer, 0, read);
        Response.Flush();
    }
}

Мне повезло увидеть такое же поведение на моей локальной рабочей станции, поэтому с помощью отладчика я смог увидеть, что «Transfer-Кодировка: chunked заголовок устанавливается на 2-й проход через цикл while во время вызова Flush.В этот момент ответ имеет как заголовок Content-Length, так и заголовок Transfer-Encoding, но каким-то образом, когда ответ достигает браузера, Firebug показывает только заголовок Transfer-Encoding.

UPDATE

Я думаю, что я проследил это до использования комбинации отправки данных в виде «кусков» И присоединения «Filter» к объекту HttpResponse (мы использовали фильтр для отслеживания размерасостояние просмотра отправляется на каждую страницу).У нас нет смысла использовать HTTP-фильтр при отправке PDF в браузер, поэтому очистка фильтра здесь решила нашу проблему.Я решил заглянуть немного глубже просто из любопытства и обновил этот вопрос, если кто-нибудь еще когда-нибудь столкнется с этой проблемой в будущем.

У меня есть простое приложение на AppHarbor, которое воспроизводит проблему:http://transferencodingtest.apphb.com/. Если вы отметите оба параметра «Использовать фильтр?»и "Отправить кусками?"В боксах вы должны увидеть заголовок 'Transfer-Encoding: Chunked' (используя инструменты разработчика Chrome, Firebug, Fiddler и т. д.).Если ни один из флажков не установлен, вы получите правильный заголовок длины содержимого.Базовый код работает на github, поэтому вы можете видеть, что происходит за кулисами:

https://github.com/appakz/TransferEncodingTest

Обратите внимание, что для локального воспроизведения необходимо настроить локальный веб-сайт в IIS 7.5.(7 тоже может сработать, я не пробовал).Сервер разработки ASP .NET, поставляемый с Visual Studio, НЕ воспроизводит проблему.

Я добавил некоторые дополнительные сведения в сообщение в блоге здесь: Заголовок «Content-Length» заменен на «Transfer-»Кодировка: Chunked 'в ASP .NET

Ответы [ 3 ]

5 голосов
/ 22 декабря 2011

Из статьи в MSDN кажется, что вы можете отключить кодирование по частям:

appcmd set config /section:asp /enableChunkedEncoding:False

enter image description here

Но это упоминается в Настройки ASP, поэтому он может не применяться к ответу, созданному из обработчика ASP.NET.

4 голосов
/ 22 декабря 2011

После вызова Response.Flush() тело ответа находится в процессе отправки клиенту, поэтому дополнительные заголовки не могут быть добавлены к ответу.Я считаю очень маловероятным, что второй вызов Response.Flush() добавляет в это время заголовок Transfer-Encoding.

Вы говорите, что у вас включено сжатие.Это почти всегда требует частичного ответа.Поэтому имеет смысл, что если сервер знает Content-Length до сжатия, он может заменить этот заголовок заголовком Transfer-Encoding и разделить ответ на части.Однако даже с включенным сжатием на сервере клиент должен явно указать поддержку сжатия в своем заголовке запроса Accept-Encoding, иначе сервер не сможет сжать ответ.Вы проверяли это в своих тестах?

В заключение, поскольку вы звоните Response.Flush() вручную, попробуйте установить Response.Buffer = True и Response.BufferOutput = False.Очевидно, они имеют противоречивое влияние на то, как работает Response.Flush().См. Комментарий внизу этой страницы и этой страницы .

0 голосов
/ 06 января 2016

У меня была похожая проблема, когда я писал большой CSV (файл не существует, я пишу строку за строкой, перебирая коллекцию в памяти и генерируя строку), вызывая Response.Write в потоке Response с BufferOutput, установленным в false, но решение было изменить

Reponse.ContentType = 'text/csv' до Reponse.ContentType = 'application/octet-stream'

Когда тип контента не был установлен на application / octet-stream, была добавлена ​​куча других заголовков ответа, таких как Content-Encoding - gzip

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...