HTTP规范:没有数据传输的PUT,因为服务器已知数据哈希

gue*_*tli 4 hash webdav put rfc deduplication

HTTP/WebDav规范是否允许此客户端 - 服务器对话框?

  1. client:我想将数据PUT到/user1/foo.mkv,它有这个哈希值:HASH
  2. 服务器:好的,PUT成功了,你不需要发送数据,因为我已经知道了这个哈希值的数据.

注意:此PUT是初始上传.这不是更新.

如果可以,则可以实现更快的文件同步.

使用案例:WebDAV服务器为每个用户托管一个目录.最喜欢的视频foo.mkv由多个用户上传.在此示例中,收藏的视频已存储在此位置:/user2/myfoo.mkv.第二次和以后的上传不需要发送任何数据,因为服务器已经知道内容.这会减少很多网络负载.

前提条件:

  • 客户端和服务器需要事先就哈希算法达成一致.
  • 服务器需要存储已知文件的哈希值.

在自定义客户端和服务器中实现它非常容易.但这不是我想要的.

我的问题:是否有允许这样的对话的RFC或其他标准?

如果还没有标准,那么如何实现这个梦想呢?

安全考虑

通过上面的对话框,它将能够访问已知哈希的内容.恶意客户端的示例知道存在哈希值为的文件1234567....他可以执行上述两个步骤,之后客户端可以使用GET下载数据.

解决此问题的方法是扩展对话框:

  1. 客户端:我想要PUT具有此哈希值的数据:HASH
  2. 服务器:好的,PUT会成功,但为了确保你有数据,请把字节N发给我直到M.我需要这个以确保你有哈希值数据.
  3. 客户端:字节数N到数据的M是 abcde...
  4. 服务器:好的,你的字节匹配我的.我信任你.上传成功,您不再需要发送数据.

怎么做到这一点?

由于似乎还没有规范,问题的这一部分仍然存在:

如何实现梦想成真?

use*_*130 7

根据你的描述,似乎应该使用ETag.

它专门用于将标记(通常是MD5哈希,但可以是任何内容)与资源的内容(和/或位置)相关联,以便稍后可以判断资源是否已更改.

ETag支持PUT请求,并且通常If-Match标头一起使用以进行乐观并发控制.

然而,你的使用情况稍有不同,你正试图防止一个PUT到具有相同内容的资源,而If-Match头被用来将允许PUT到资源与相同的内容.

在您的情况下,您可以改为使用If-None-Match标题:

"If-None-Match:*"的含义是,如果由原始服务器(或通过缓存,可能使用Vary机制,参见第14.44节)选择的表示存在,则不得执行该方法,并且应该执行如果表示不存在.此功能旨在用于防止PUT操作之间的比赛.


WebDAV也支持Etags,但它的使用方式可能取决于实现:

请注意,PUT响应中ETag的含义在本文档或RFC 2616中没有明确定义(即,ETag是否意味着资源是八位字节,相当于PUT请求的主体,或者是否服务器可能在存储时对文档的格式或内容进行了微小的更改.这是一个HTTP问题,不仅仅是WebDAV问题.


如果您正在实现自己的客户端,我会做这样的事情:

  1. 客户端向资源发送HEAD请求检查 ETag
    • 如果客户端看到它与已有的匹配,请不要发送任何其他内容
    • 如果不匹配,则发送带有If-None-Matches标头的PUT请求

UPDATE

从您更新的问题,现在看来很清楚,当收到PUT请求时,您希望在接受请求之前检查服务器上的所有资源是否缺少相同的内容.这意味着还要检查位于与PUT请求的目标指定位置不同的位置的资源.

AFAIK,没有专门处理此案例的现有规范.但是,ETag机制(和HTTP协议)被设计为通用且足够灵活,可以处理许多情况,这就是其中之一.

当然,这只是意味着您无法利用标准的HTTP服务器逻辑 - 您需要自定义客户端和服务器端的代码.

假设

在进入可能的实现之前,需要做一些假设.

  1. 如上所述,您需要控制服务器和客户端
  2. 需要同意基于内容生成ETag的算法.这可以是MD5,SHA1,SHA2-256,SHA3,它们组合的串联等等.我只是提到算法输出为ETag,但是你如何做到这取决于你.

可能的实现

如果简单的情况对您不起作用,这些已经从最简单到复杂的顺序排序.

可能的实施1

这假设您的服务器实现允许您在收到整个请求之前读取请求标头并进行响应.

  1. 客户端计算要上载的文件/资源​​的ETag.
  2. 客户端使用If-None-Match包含ETag 的标头向服务器发送PUT请求(位置无关紧要)并继续正常发送正文.
  3. 服务器检查是否已存在具有ETag的资源.
  4. 服务器:
    • 如果ETag已经存在,立即返回412响应代码.(可选)终止连接以阻止客户端继续发送资源(注意:HTTP规范不建议这样做,但未明确禁止.请参阅下面的注释1).是的,浪费了一点带宽,但您不必等待整个请求完成.
    • 如果ETag不存在,请等待请求正常完成.
  5. 客户:
    • 如果收到412响应,则将其解释为资源已存在且需要中止请求 - 停止发送数据.

可能的实施2

这稍微复杂一点,但更好地遵守HTTP规范.此外,如果您的服务器体系结构不允许您在收到整个请求之前读取标头,那么这可能会起作用.

  1. 客户端计算要上载的文件/资源​​的ETag.
  2. 客户端使用If-None-Match包含ETag和Expect: 100-continue标头的标头向服务器发送PUT请求(位置无关紧要).此时尚未发送请求正文.
  3. 服务器检查是否已存在具有ETag的资源.
  4. 服务器:
    • 如果ETag已存在,则返回412响应.
    • 如果ETag不存在,则发送100响应并等待请求正常完成.
  5. 客户:
    • 如果收到412响应,则将其解释为资源已存在且请求因此中止.
    • 如果收到100响应,继续正常发送正文

可能的实施3

此实现可能需要最多的工作,但应与所有主要库/架构广泛兼容.但是,另一个客户端在两个请求之间上传具有相同内容的文件的风险很小.

  1. 客户端计算要上载的文件/资源​​的ETag.
  2. 客户端发送在HEAD请求(无体)到服务器/check-etag/<etag>,其中<etag>是的ETag.这将检查ETag是否已存在于服务器上.
  3. /check-etag/*检查服务器代码以查看具有该ETag的资源是否已存在.
  4. 服务器:
    • 如果ETag已存在,则返回200响应.
    • 如果ETag不存在,则发送404响应.
  5. 客户:
    • 如果收到200响应,则将其解释为资源已存在且不继续进行PUT请求.
    • 如果收到404响应,请跟进正常的PUT请求到目的地.

注意事项

虽然实施取决于您,但有以下几点需要考虑:

  1. 添加或更新资源时,ETag和位置应存储在数据库中以便快速检索.每当上载资源时,服务器重新计算每个资源的散列都是不必要的低效率.ETag和位置字段上还应该有一个索引,以便快速检索.
  2. 如果两个客户端同时上传具有相同ETag的资源,您可能希望在第一个完成后立即中止第二个资源.
  3. 对ETag使用散列意味着存在冲突的可能性(其中两个资源具有相同的散列),但在实践中,如果使用良好的散列,则可能性非常小.请注意,已知MD5对故意碰撞攻击很弱.如果你是偏执狂,你可以连接多个哈希值,使碰撞的机会小得多.
  4. 关于你的"安全考虑",我仍然不知道如何知道哈希将导致检索资源.服务器只会,并且应该只告诉您是否存在特定的ETag.在不泄露位置的情况下,客户端无法检索文件.即使客户端知道位置,服务器也应该实施其他安全控制,如身份验证和授权,以限制访问.仅仅将资源位置用作限制访问的方式只是默默无闻的安全性,特别是从您提到的内容来看,路径似乎遵循用户名的模式.

笔记

  1. RFC 2616表明不应该这样做:

如果源服务器收到的请求不包含具有"100-continue"期望的Expect请求标头字段,则该请求包括请求主体,并且服务器在从读取整个请求主体之前使用最终状态代码进行响应.传输连接,然后服务器不应关闭传输连接,直到它已读取整个请求,或直到客户端关闭连接.否则,客户端可能无法可靠地接收响应消息.

此外,请勿在不发送任何状态代码的情况下关闭服务器端的连接,因为客户端很可能会重试该请求:

如果HTTP/1.1客户端发送包含请求主体的请求,但不包含具有"100-continue"期望的Expect请求标头字段,并且客户端未直接连接到HTTP/1.1源服务器,如果客户端在从服务器接收任何状态之前看到连接关闭,则客户端应该重试该请求.