🔗 重构转发链
目前 Squid 在我们可以请求的协议之间存在混淆
- FTP
- HTTP
- HTTPS
- WAIS
- URN
- GOPHER
- CACHEOBJ
以及我们拥有服务器实现的协议
- HTTPS
- HTTP
- ICP
- HTCP
以及我们拥有客户端实现的协议
- HTTP
- HTTPS
- URN
- GOPHER
- FTP
有一个 补丁 用于分离服务器实现 - HTTPS, HTTP, ICP, HTCP。这可能需要更多工作才能真正完善,并计划在 3.1 中发布。
在将我们可以请求的协议分离成一组干净的类、使其模块化方面已经做了一些工作,但尚未完成 - 而且在分离我们实现客户端的协议以及请求对象与实际将其移交给外部服务器之间的连接之前,可能无法完成。
此提案试图提供一个一致的 API 来实现这一点,这将允许
- 干净地实现 HTTP 和 HTTPS 对等体
- 在 Squid 中实现额外的对等体功能,例如 RTSP,或通过 SSH 对等体进行隧道等。
- 处理额外的 URL 方案,而不会干扰核心。
🔗 设计
Squid 内处理请求有三种主要方式
- 内部生成的数据
- 通过对等体的线路协议移交给缓存对等体
- 移交给请求线路协议的实现
每个请求的 scheme 字段是一个相当好的驱动此操作的键:scheme->startFetch() 将请求处理移交给模块想要做的任何事情。模块将负责在此点正确地确保错误等发生。
内部处理的 URL 方案将自行实现 startFetch。然而,大多数方案不是内部的,而是 ForwardableURLScheme 的子类。ForwardableURLScheme 在其 startFetch 方法中,创建一个 FwdState 并启动现有的 FwdState 进程。
也就是说,一组对等体被选中,基于对等体的配置数据 - ACL、netdb,以及我们关于对等体的数据 - 缓存摘要等,并且可能通过线路上到缓存的 ICP 查询。
我们将稍微修改此查找。首先,我们将为 URLScheme 添加一个方法 - request->scheme 的类型 - 称为 'protocolClientAvailable'。此方法仅当 Squid 具有该协议的原生实现时才返回 true。如果返回 false,我们将要求它通过对等体转发。(例如,考虑 WAIS,我们不原生实现它。我们只能通过实现 WAIS 的对等体来满足 WAIS。)
此时我们有一组要尝试的对等体。对于每个对等体(如果允许,包括直接访问的那个)
- 我们尝试获取一个缓存的 ProtocolClient 对象。这是当前 pconn 机制的泛化。缓存查找必须考虑到诸如“仅将此 ProtocolClient 提供给同一个经过身份验证的用户”之类的要求 - 以支持连接固定需求。
- 如果我们无法获得缓存的 ProtocolClient,我们将使用 TCP 启动与对等体的连接。
- 连接完成后,我们调用 peer->protocolClientFactory(),这是一个创建 ProtocolClient 的工厂。我们将 FwdState 作为 refcount-lock 的对象,这样它就可以在 ProtocolClient 准备好使用时回调。(查看 HTTPS 示例以了解为什么它是异步的)。通过调用对等体的 protocolClientFactory 方法,我们允许直接对等体拥有与与我们的缓存对等体(它们都是 HTTP/HTTPS)通信的线路协议不同的线路协议实现。因此,例如,FTP 的直接对等体 protocolClientFactory 将构造一个 FTPProtocolClient。
-
一旦我们有了 ProtocolClient,我们要么将其放入池中(如果请求已被中止),要么将其移交给它 -
protocolClient.handleRequest(request, aForwardState)。此调用将立即创建一个 ProtocolClientRequest - 即 FTPClientRequest,或 GopherClientRequest - 其中包含满足此请求所需的状态。此 ProtocolClientRequest 将由 ProtocolClient 拥有,并对 ForwardState 具有 refcount 引用,以便它可以在以下情况通知 ForwardState: - 有一个正在交付给客户端的响应 - ProtocolClientRequest 未能获取响应,并且请求应该被重新转发 - ProtocolClientRequest 未能获取响应,并且应该通知客户端。这三种状态并不意味着请求的*完成*,而是它们在 ForwardState 内改变状态。ForwardState 将持有 ProtocolClientRequest 的 RefCountReference,以便它可以请求中止该请求。我们在此处创建一个新的 ProtocolClientRequest 以允许最终实现流水线 - 在 ProtocolClient 上多次调用 handleRequest 将会流水线,到该客户端的能力,这些请求。ProtocolClient 将拥有协议的状态机,ProtocolClientRequest 将拥有单个请求的数据的接收器和发送器。它必须是客户端特定的,因为 URL 与所需的线路级别编码无关。
- 如果请求被我们的客户端中止,ForwardState 对象的 abort() 方法将被调用。这应该调用 ProtocolClientRequest 的 abort(),而不是 ProtocolClient 的 abort()。例如,在流水线场景中,一个流水线的 HttpClientRequest 可能尚未序列化,因此对其调用 abort() 可能会将其从 ProtocolClient 的队列中删除。当 ProtocolClientRequest 被中止时,就客户端而言,有两种可能的状态:
- 客户端已收到某些数据。在这种情况下,客户端发起了中止,并将执行任何必要的清理。
- 客户端尚未收到数据。在这种情况下,无需清理。
因此,当 ProtocolClientRequest 被中止时,它将以适当的方式从 ProtocolClient 请求队列中删除自身,并删除其对 forward state 对象的引用 - 它将不再调用该对象。这将减少 forward state 对象的引用计数,并可能允许其释放。ForwardState 对象现在将删除其对 ProtocolClientRequest 的引用,允许 ProtocolClientRequest 释放(如果它尚未被 ProtocolClient 拥有,这在清理过程中可能会发生)。我们可能需要在 ForwardState::abort 中进行自引用,以确保它在自身生命周期内不会删除自身。
🔗 示例
🔗 CONNECT
ConnectURLScheme 将是 ForwardableURLScheme 的子类。Connect 将对 protocolClientAvailable() 调用返回 true。connect 的 protocolClientFactory 将直接进入隧道模式,并告知 forward state 它正在向客户端发送数据,并且无法重新转发。没有 CONNECT 对等体类型。
🔗 HTTP
HTTPUrlScheme 将是 ForwardableURLScheme 的子类。HTTPUrlScheme 将对 protocolClientAvailable() 调用返回 true。http 的 protocolClientFactory 将是 HTTPClientFactory 的实例。这将返回一个 HTTPClient,它大致相当于今天的 HTTPServerState,但只有线路级别方面。
将有一个 HTTP Peer 子类。其 protocolClientFactory 属性默认情况下将与 HTTPUrlScheme 的相同。
🔗 HTTPS
HTTPSUrlScheme 将是 ForwardableURLSche,e 的子类。HTTPUrlSchceme 将对 protocolClientAvailable() 调用返回 true。HTTPSUrlScheme 将仅在启用 SSL 支持时编译。protocolClientFactory 将是 SSLClientFactory,并使用 HTTPClientFactory 实例进行参数化。SSLClientFactory 接受一个套接字并执行 SSL 握手,之后它们调用它们被参数化的工厂 - 因此序列是
aSSLClientFactory(fd, ProtocolClientRequest *requestor)- 创建 SSLClientEndpoint(fd)
- 创建 SSLConnectionRequest(this, requestor)
- 调用 endpointer->connect(theSSLConnectionRequest)
- 进行握手
- 握手完成时调用 theSSLConnectionRequest->connected(),或者如果握手失败则调用 ->failed()。
- 发生错误时,我们创建一个全局实例 - FailedSSLProtocolClient,它将生成错误。
- 连接成功后,我们调用
this->nestedProtocolClientFactory(theSSLClientEndpoint->fd, theSSLConnectionRequest)。这将创建嵌套的 ProtocolClient - 即,对于 HTTPS,这将创建实际连接到 SSL 客户端的 HTTPClient。 - SSLClientEndpoint 被赋予 HTTPClient 来拥有引用,就像 Socket 拥有 SSLClientEndpoint 一样。
- SSLClientEndpoint 然后将原始请求者与 HTTPClient 联系起来。
不会有 HTTPSPeer 子类。相反,HTTPS 对等体会是 HTTPPeer 的实例,其 protocolClientFactory 设置为 SSLClientFactory 的实例,并使用 HTTPClientFactory 进行参数化。
这意味着只有一条代码路径会为客户端创建 HTTPS 包装器。这也意味着我们可以对任何协议执行 Gophers 或其他此类 TLS 操作。
🔗 Gopher
GopherURLScheme 将是 ForwardableURLScheme 的子类。Connect 将对 protocolClientAvailable() 调用返回 true。gopher 的 protocolClientFactory 将返回一个 GopherClient 对象。没有 Gopher 对等体类型,因为我们的 gopher 实现无法转发其他协议。
分类: WantedFeature
导航:站点搜索,站点页面,类别,🔼 向上