Несколько вещей, которые я обнаружил, вызывают проблемы с прокруткой собственного сжатия.
Во-первых. Некоторая ситуация приводит к полному изменению способа обработки ответа (Server.Transfer, модуль HTTP, откладывающий на другой модуль HTTP), может очистить заголовки, но сохранить поток. Fiddler быстро скажет вам, если это так. Одна возможность состоит в том, что это происходит, когда вы переходите к своему ответу об ошибке, и в случае FF происходит ошибка. Принудительное распаковывание потока самостоятельно должно помочь в диагностике.
И наоборот, последовательность событий могла привести к удвоению заголовков и / или сжатия, поэтому вы в конечном итоге отправляете gzip gzip и тому подобное. Что еще хуже, фильтр мог быть частично изменен в ответе.
В-третьих. Простое добавление DeflateStream или GZipStream в качестве фильтра некорректно обрабатывает случай, когда используется чанкованное кодирование (буферизация отключена, вызывается HttpResponse.Flush () или отправляется ответ, превышающий максимально допустимый размер буфера). Следующий класс потока обрабатывает этот случай правильно (исправление Flush()
исправляет, дополнительные общедоступные свойства, которые я нашел полезными при работе с описанными выше случаями).
public enum CompressionType
{
Deflate,
GZip
}
public sealed class WebCompressionFilter : Stream
{
private readonly Stream _compSink;
private readonly Stream _finalSink;
public WebCompressionFilter(Stream stm, CompressionType comp)
{
switch(comp)
{
case CompressionType.Deflate:
_compSink = new DeflateStream((_finalSink = stm), CompressionMode.Compress);
break;
case CompressionType.GZip:
_compSink = new GZipStream((_finalSink = stm), CompressionMode.Compress);
break;
default:
throw new ArgumentException();
}
}
public Stream Sink
{
get
{
return _finalSink;
}
}
public CompressionType CompressionType
{
get
{
return _compSink is DeflateStream ? CompressionType.Deflate : CompressionType.GZip;
}
}
public override bool CanRead
{
get
{
return false;
}
}
public override bool CanSeek
{
get
{
return false;
}
}
public override bool CanWrite
{
get
{
return true;
}
}
public override long Length
{
get
{
throw new NotSupportedException();
}
}
public override long Position
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}
public override void Flush()
{
//We do not flush the compression stream. At best this does nothing, at worse it
//loses a few bytes. We do however flush the underlying stream to send bytes down the
//wire.
_finalSink.Flush();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
_compSink.Write(buffer, offset, count);
}
public override void WriteByte(byte value)
{
_compSink.WriteByte(value);
}
public override void Close()
{
_compSink.Close();
_finalSink.Close();
base.Close();
}
protected override void Dispose(bool disposing)
{
if(disposing)
{
_compSink.Dispose();
_finalSink.Dispose();
}
base.Dispose(disposing);
}
}
Четвертое. С помощью кодировки содержимого (а не кодировки передачи) HTTP считает, что вы фактически отправляете другую сущность, а не другую кодировку. (Transfer-encoding считает, что вы просто используете кодировку, чтобы использовать меньшую полосу пропускания, чего мы обычно и хотим, но, увы, поддержка Transfer-encoding не так распространена, поэтому мы используем вместо этого Content-Encoding) , Таким образом, вы должны убедиться, что электронные теги (если они есть) различны в разных кодировках (добавьте G для gzip и D для значения по умолчанию до того, как последний «символ» сделает свое дело, просто не повторяйте мод Ошибка -gzip, заключающаяся в том, что он ставится после символа "*).
Пятый. В связи с этим вы должны отправить соответствующий заголовок Vary, учитывая, что вы можете варьировать в зависимости от кодировки содержимого. Выполнение этого правильно означает отправку Vary: Accept-Encoding, чтобы указать, что то, что вы отправляете, будет зависеть от значения этого заголовка. Поскольку это вызывает проблемы с IE (к счастью, следующая версия будет иметь некоторое улучшение , согласно MS), некоторые люди вместо этого отправляют Vary: User-Agent (на том основании, что большинство пользовательских агентов либо принимают сжатые кодировки содержимого) или нет, а не запрашивать иногда, а не другие). Обратите внимание, что вам нужно установить заголовок Vary, когда вы подготовлены для сжатия, даже в тех случаях, когда вы этого не делаете.
Шестое. Даже если вы все делаете идеально, то что-то в кеше, находящемся на ранних этапах разработки, может помешать этому, поскольку вы только что изменили правила кеширования после того, как оно было кешировано. Очистите кеш.
Если ни один из этих способов не подходит, по крайней мере посмотрите на то, что вы видите в таком инструменте, как Fiddler, и на то, что вы увидите, если вы вручную распакуете поток, отправленный в FF, это определенно должно помочь.
Между прочим, ваш код выше предпочитает GZip над Deflate, независимо от предпочтений клиента. Если бы я собирался игнорировать указанный клиентом порядок предпочтений, я бы сделал это наоборот. Поскольку GZip построен на Deflate, GZip всегда немного больше, чем Deflate. Эта разница незначительна, но, что более важно, некоторым реализациям потребуется намного больше времени ЦП для работы с данными g-zip, чем для дефляции данных, и это зависит от архитектуры, а также от программного обеспечения (так что тестирование выполняется только на одной машине). не говорит вам достаточно, чтобы судить, применимо ли это), поэтому для клиента, работающего в своем браузере на младшей машине, заметная разница между gzip и deflate может быть больше, чем просто загрузка нескольких дополнительных октетов, которые отправит gzip.