我原计划在不同的加载器上发布更多的帖子,你可以使用这个架构构建,但是为了“完成”这个系列,我决定放弃一个帖子每个加载器,而是强调要点 其中一些。
OpenID
我们已经了解了如何实现通过 OAuth 2 流授权请求的加载程序,但在其之上存在一个抽象,称为 OpenID。 使用 OAuth 加载程序,我们需要指定登录 url、刷新令牌的 url 等内容。 OpenID 允许身份提供者通过发送包含所有这些 url(以及协议的其他特性)的清单来抽象化这一点。
如果我们想自己实现 OpenID,我们需要在我们的状态机中有一个初步状态来首先获取这个清单,然后将其用作后续状态逻辑的基础。 或者,我们可以将 OpenID 的现有实现(例如官方实现)包装在自定义 HTTPLoader 子类中,并允许该库执行复杂的逻辑。 在这种情况下,我们的 HTTPLoader 子类将充当库提供的 API 与 HTTP 加载程序链所需的 API 之间的适配器。
缓存
从概念上讲,缓存加载器应该相对简单易懂。 当一个请求进入这个加载器时,它会检查请求(可能还有一个 HTTPRequestOption 来指示是否允许缓存)并查看它是否与任何已保存的响应(在内存中、磁盘上等)相匹配。 如果存在这样的响应,则加载程序返回该响应,而不是将请求进一步发送到链中。
如果响应不存在,它会继续执行典型的请求,但也会插入另一个完成处理程序,以便它可以捕获响应,并且(如果满足正确的条件)将其持久化以用于未来的请求。
重复备份
重复备份类似于缓存,因为当一个请求进来时,加载器会查看它是否与一个已经在进行中的请求相似。 如果是,则新请求被搁置,当原始请求得到响应时,该响应被复制到第二个请求。
重定向
有几种方法可以处理重定向请求。
默认情况下,URLSession 将遵循重定向,除非您特别覆盖 URLSessionTaskDelegate 上的 willPerformHTTPRedirection 委托方法。 因此,您可以这样做,然后有条件地允许根据您创建的特定 HTTPRequestOption 对请求进行重定向。
或者,您可以无条件地拒绝 URLSession 级别的重定向,然后有一个单独的 RedirectionFollowingLoader 接收传入请求,复制它们,并将重复项发送到链中。 当副本返回时,加载器检查响应并查看它是否是重定向响应。 如果是,那么它会为重定向构造一个新请求,并将其发回。
一旦加载程序返回非重定向响应,它就会使用该响应作为对原始请求的响应并将其发回。 您将需要一些逻辑来检测重定向循环并打破它们,但这里的关键思想是发送请求的副本,以便您有机会在决定如何处理之前检查响应。
证书固定
原则上,证书固定应该看起来像任何其他 HTTPLoader:一个请求进来,在它被发送到下一个请求之前,目标服务器的证书根据作为 HTTPRequestOption 附加到请求的证书进行验证。
实际上,这有点困难,因为证书仅在 URLSessionLoader 中协商到远程服务器的连接时可用。 因此,此处的操作过程是不使用单独的 CertificatePinningLoader,而是向 HTTPRequest 提供 CertificateValidator 值,如果加载程序需要执行某些证书验证(类似于 Alamofire 的 ServerTrustEvaluating 协议),则可以使用该值。
然后我们的 URLSessionLoader 需要更新为使用委托,并实现委托方法来处理 URLAuthenticationChallenge,然后在收到 .serverTrust 质询时为请求查询该选项。
点对点
点对点加载器很有趣,因为它源于这样一种认识,即 HTTPLoader 的合同没有说明响应来自哪个设备。 我们已经看到了加载程序的示例,这些加载程序将返回虚假响应(用于模拟)或重用响应(缓存和重复数据删除)。 P2P 加载器可以决定将请求发送到另一台设备,并允许该设备提供响应。
这可以通过多种技术来实现,从 MultipeerConnectivity 到蓝牙或直接套接字连接。 这里的可能性非常大。
敏锐的观察者还会意识到我们早先创建的 URLSessionLoader 属于此类。 这是一个加载程序,可以将生成请求的责任“卸载”到另一个设备。 它恰好是一个同时也是 HTTP 服务器的设备,但我们的加载堆栈并不需要直接知道这一点。
流式响应
该框架不能很好地工作的一个领域是流式响应。 这很明显:我们已经围绕这样的期望构建了一切,即离散且有限的请求具有离散且有限的响应。 流媒体打破了这种期望。 我们可以在上传中做一个流式主体,因为发送该流的过程是发送我们单个离散请求的一部分。
我们可以处理一些类型的流媒体主体,例如文件下载。 对于这些,我们想提供一个 OutputStream(或类似的)来表示“把你得到的任何字节放回这里”; 默认情况下,这可能是内存中数据值的流。 这将允许我们将响应直接流式传输到文件,而不是通过内存中的数据值。
对于实时视频流,我们可以提供一个 OutputStream 将数据通过管道传输到 AVSession 中。 但是,为了使这项工作有效,我们将明确放弃“单一请求,单一响应”的一些语义。 我们还需要非常小心我们如何实现请求复制(例如重定向加载程序需要)。
结论
我们可以用这个框架做很多事情。 有些事情有些复杂,需要解决/使用系统 API 的特定实现细节(例如证书固定、流式响应等)。 总的来说,这种将网络建模为“发送请求并最终获得响应”的方法使我们能够构建极其灵活、可组合和可定制的网络堆栈。
在下一篇(也可能是最后一篇)博文中,我们将缩小范围以查看我们创建的框架的高级概述,并了解它如何适应其他 Swift 技术。