Почему ASP.NET заменяет заголовок Content-Length заголовком Transfer-Encoding при ручном удалении ответа? - программирование
Подтвердить что ты не робот

Почему ASP.NET заменяет заголовок Content-Length заголовком Transfer-Encoding при ручном удалении ответа?

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

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

Поскольку все еще работало нормально в нашей тестовой среде, я смог использовать Firebug, чтобы взглянуть на заголовки ответов, которые возвращались в обеих средах. В тестовой среде я видел правильный заголовок Content-Length, в то время как в производстве, который был заменен заголовком Transfer-Encoding: chunked. 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-Encoding: chunked" устанавливается на втором проходе через цикл while во время вызова 'Промывать'. В этот момент в ответе есть заголовок Content-Length и заголовок Transfer-Encoding, но каким-то образом к моменту достижения ответа в браузере Firebug показывает только заголовок Transfer-Encoding.

UPDATE

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

У меня есть приложение на AppHarbor, которое воспроизводит проблему: http://transferencodingtest.apphb.com/. Если вы установите флажок "Использовать фильтр"? и "Отправить в куски"? вы должны увидеть заголовок "перекодирование: chunked" (используя инструменты Chrome dev, Firebug, Fiddler, что угодно). Если ни один из полей не установлен, вы получите правильный заголовок длины содержимого. Основной код находится на github, чтобы вы могли видеть, что происходит за кулисами:

https://github.com/appakz/TransferEncodingTest

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

Я добавил некоторые подробности в сообщение в блоге: Заголовок Content-Length заменен на "Передача-кодирование: Chunked" в ASP. NET

4b9b3361

Ответ 1

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

appcmd set config /section:asp /enableChunkedEncoding:False

enter image description here

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

Ответ 2

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

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

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

Ответ 3

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

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

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