【发布时间】:2020-02-18 12:29:40
【问题描述】:
我正在处理Google Cloud Storage .NET client library。共有三个功能(在.NET、我的客户端 库和存储服务)组合在一个 不愉快的方式:
下载文件时(Google Cloud Storage 中的对象) 术语),服务器包括存储数据的哈希值。我的 客户端代码然后根据它的数据验证该哈希 已下载。
Google 云存储的一个单独功能是用户可以 设置对象的 Content-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 中的解压缩,而是在从流中读取之后解压缩 - 但我需要能够在不更改任何使用生成的@987654330 的代码的情况下做到这一点@来自HttpClient。 (处理响应的代码很多,我只想在一个中心位置进行更改。)
我有一个计划,我已经制作了原型,并且可以尽我所能 到目前为止发现,但有点难看。它涉及创建一个三层 处理程序:
-
HttpClientHandler禁用自动解压。 - 用新的
Stream子类替换内容流的新处理程序 它委托给原始内容流,但在读取数据时对其进行哈希处理。 - 仅解压缩处理程序,基于 Microsoft
DecompressionHandler代码。
虽然这可行,但它有以下缺点:
- 开源许可:准确检查我需要做的事情 根据 MIT 许可在我的仓库中创建一个新文件 微软代码
- 有效地分叉 MS 代码,这意味着我可能应该 定期检查是否发现任何错误
- Microsoft 代码使用程序集的内部成员,因此它 没有尽可能干净地移植。
如果微软将DecompressionHandler 公开,那将有助于
很多 - 但这可能比我需要的时间更长。
如果可能的话,我正在寻找一种替代方法 -
我错过了一些让我之前了解内容的东西
减压。我不想重新发明HttpClient - 回复
例如,经常被分块,我不想进入
事情的那一面。这是一个非常具体的拦截点
我在找。
【问题讨论】:
-
在我看来,这里的压缩部分,就存储方面而言,有点像这样“我确实有一个未压缩的文件,但如果我可以将它压缩存储,那就太好了让浏览器的解压部分自动解压”。如果是这样,那么存储/提供解压缩内容的哈希是否有意义?听起来这只是服务器空间和cpu优化,避免服务器端的压缩步骤。我在这里想念什么?很多客户端库不会因此而出现完全相同的问题吗?
-
@LasseVågsætherKarlsen:如果响应可以同时包含压缩数据的散列和未压缩数据(您不希望客户端必须解压缩它),那就太好了只是为了散列,如果他们想让它保持压缩,否则我怀疑我是否能够通过这种变化。是的,其他一些客户端库可能确实有同样的问题 - 但我正在与谷歌官方的维护者保持联系,他们正在检查它:)
-
@LasseVågsætherKarlsen:如果您仍在从 GCS 获取数据,那不是
HttpClientHandler这样做 - 那是 GCS。如果您请求一个 Content-Encoding 为 gzip 的文件,但您没有指定 Accept-Encoding: gzip,它会为您解压缩,提供没有 Content-Encoding 标头的解压缩内容。 (并且仍然包括压缩文件的哈希值。我知道,这是有问题的......我不想进入 all 这个问题中可能存在的怪癖,但如果你认为我应该提到这一点。) -
简而言之,这个哈希似乎被设计为无法验证,这对我来说听起来毫无意义。
-
@zaitsman:我通常比源代码更相信我在网络上看到的内容 :) 我一直在 .NET Core 上运行我的大部分测试,但在 Windows 上 - 这就是绝对可以禁用压缩。
标签: c# .net-core google-cloud-storage md5 dotnet-httpclient