我正在使用Google Cloud Storage .NET客户端库。有三种功能(.NET,我的客户端库和存储服务之间)以不愉快的方式结合在一起:
下载文件(Google Cloud Storage术语中的对象)时,服务器会包含已存储数据的哈希值。然后,我的客户代码会根据下载的数据来验证该哈希。
Google云端存储的另一个功能是,用户可以设置对象的Content-Encoding,并且当请求包含匹配的Accept-Encoding时,该对象将作为下载的标头包含在内。(目前,让我们忽略请求中不包含的行为...)
HttpClientHandler
可以自动透明地解压缩gzip(或压缩)内容。
当所有这三个结合在一起时,我们就会遇到麻烦。这是一个简短但完整的程序,演示了这一点,但是没有使用我的客户端库(并且没有访问可公开访问的文件):
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与X-Goog-Hash
标头的MD5部分不同。(在我的客户端库中,我使用的是crc32c哈希,但这显示了相同的行为。)
这不是错误,HttpClientHandler
这是预料之中的,但是当我想验证哈希值时却很痛苦。基本上,我需要在减压之前和之后的内容。而且我找不到任何办法。
在一定程度上澄清我的要求,我知道如何防止减压在HttpClient
从流中读取的时候,而是事后解压缩-但我需要能够做到这一点不改变任何使用所产生的代码HttpResponseMessage
从HttpClient
。(有很多处理响应的代码,我只想在一个中心位置进行更改。)
我有一个计划,该计划已经原型化,并且可以按照我到目前为止发现的方式工作,但是有点难看。它涉及创建三层处理程序:
HttpClientHandler
禁用自动减压。- 一个新的处理程序,用一个新的
Stream
子类替换内容流,该子类委派给原始内容流,但在读取数据时对其进行哈希处理。 - 基于Microsoft
DecompressionHandler
代码的仅解压处理程序。
尽管这样做有效,但具有以下缺点:
- 开源许可:根据MIT许可的Microsoft代码,准确检查我需要做些什么才能在我的存储库中创建一个新文件
- 有效地分叉了MS代码,这意味着我可能应该定期检查以查看是否发现了任何错误。
- Microsoft代码使用程序集的内部成员,因此它的移植效果不尽如人意。
如果微软DecompressionHandler
公开上市,那将有很大帮助-但这可能比我需要的时间更长。
我正在寻找的是一种可能的替代方法-我错过了一些让我在解压之前就掌握内容的方法。我不想重塑HttpClient
-例如,响应通常是分块的,我也不想涉足这一方面。这是我正在寻找的一个非常具体的拦截点。