Я работаю над клиентом Google Cloud Storage.NET библиотека. Существует три функции (между .NET, моим клиентом библиотека и служба хранения), которые объединяются в неприятный способ:
-
При загрузке файлов (объектов в облачном хранилище Google терминология), сервер включает хэш сохраненных данных. мой клиентский код затем проверяет этот хэш на данные, которые он скачали.
-
Отдельной функцией облачного хранилища Google является то, что пользователь может установите Content-Encoding объекта и включите его как заголовок при загрузке, когда запрос содержит совпадение Accept-Encoding. (На данный момент пусть игнорирует поведение, когда запрос не включает это...)
-
HttpClientHandler
может распаковать содержимое gzip (или deflate) автоматически и прозрачно.
Когда все три из них объединены, у нас возникают проблемы. Здесь короткая, но полная программа, демонстрирующая это, но без использования моего клиентская библиотека (и доступ к общедоступному файлу):
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
string url = "https://www.googleapis.com/download/storage/v1/b/"
+ "storage-library-test-bucket/o/gzipped-text.txt?alt=media";
var handler = new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.GZip
};
var client = new HttpClient(handler);
var response = await client.GetAsync(url);
byte[] content = await response.Content.ReadAsByteArrayAsync();
string text = Encoding.UTF8.GetString(content);
Console.WriteLine($"Content: {text}");
var hashHeader = response.Headers.GetValues("X-Goog-Hash").FirstOrDefault();
Console.WriteLine($"Hash header: {hashHeader}");
using (var md5 = MD5.Create())
{
var md5Hash = md5.ComputeHash(content);
var md5HashBase64 = Convert.ToBase64String(md5Hash);
Console.WriteLine($"MD5 of content: {md5HashBase64}");
}
}
}
Файл проекта .NET Core:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
</Project>
Вывод:
Content: hello world
Hash header: crc32c=T1s5RQ==,md5=xhF4M6pNFRDQnvaRRNVnkA==
MD5 of content: XrY7u+Ae7tCTyyK7j1rNww==
Как вы можете видеть, MD5 содержимого не совпадает с MD5
часть заголовка X-Goog-Hash
. (В моей клиентской библиотеке я использую crc32c
хэш, но это показывает то же поведение.)
Это не ошибка в HttpClientHandler
- ожидалось, но боль
когда я хочу проверить хэш. В принципе, мне нужно
до и после декомпрессии. И я не могу найти никакого способа
от этого.
Чтобы немного прояснить мои требования, я знаю, как предотвратить декомпрессию в HttpClient
и вместо этого распаковать после этого при чтении из потока, но мне нужно сделать это, не изменяя код, который использует результирующий HttpResponseMessage
из HttpClient
. (Там много кода, который обрабатывает ответы, и я хочу только внести изменения в одно центральное место.)
У меня есть план, который я прототипировал и который работает, насколько я знаю найденный до сих пор, но немного уродлив. Это предполагает создание трехслойного обработчик:
-
HttpClientHandler
с отключенной автоматической декомпрессией. - Новый обработчик, который заменяет поток контента новым подклассом
Stream
который делегирует исходный поток контента, но хэширует данные по мере их чтения. - Обработчик декомпрессии, основанный на коде Microsoft
DecompressionHandler
.
Пока это работает, у него есть недостатки:
- Лицензирование с открытым исходным кодом: проверка того, что мне нужно сделать, чтобы создать новый файл в моем репо на основе лицензии MIT Код Microsoft
- Эффективное форматирование кода MS, что означает, что я должен, вероятно, регулярно проверяйте, обнаружены ли в нем какие-либо ошибки.
- В коде Microsoft используются внутренние элементы сборки, поэтому не переносится как можно чище.
Если Microsoft сделала DecompressionHandler
общедоступной, это поможет
лот - но это, вероятно, будет в более длительный срок, чем мне нужно.
Я ищу альтернативный подход, если это возможно -
то, что я пропустил, позволяет мне добираться до контента до
декомпрессия. Я не хочу изобретать HttpClient
- ответ
часто зацикливается, например, и я не хочу, чтобы
эта сторона вещей. Это довольно конкретная точка перехвата, которая
Я ищу.