有人可以解释一下这段扭曲的作用吗?

Can somebody explain what this piece of twisted does?

 class ConnectProxy(Proxy):

    requestFactory = ConnectProxyRequest
    connectedRemote = None

   def requestDone(self, request):   
       if request.method == 'CONNECT' and self.connectedRemote is not None:  
           self.connectedRemote.connectedClient = self
       else:
           Proxy.requestDone(self, request)

self.connectedRemote.connectedClient = self 是做什么的?

技术上:

if 条件检查名称 self.connectedRemote 处是否存在某个对象。此对象的属性 connectedClient 设置为 self

它有什么作用?

从这段代码很难判断,因为我不知道 connectedRemote 打算 是什么对象。我推测在成功完成 CONNECT 请求后, 客户端 (connectedClient) 连接到 远程 服务器 ( connectedRemote) 设置为 self(此代理实例)。

如果确实如此,我认为这有点臭,因为 connectedRemote 对象应该对此负责。

所以这个代码片段的原始来源是我的 github repositories 之一,它包装了扭曲的 HTTP 代理服务器协议以也支持 CONNECT 方法。

简短的回答是,这将 self(下游协议实例——在客户端和这个扭曲的服务器之间)分配给上游连接协议实例的成员(到远程 https 服务器),所以从上游连接接收到的任何数据都可以轻松写回下游客户端的传输。

我对代码有更长的解释,但这有助于对 HTTP 代理协议有一个基本的了解,所以我会尝试设置一些上下文,但如果您已经知道如何操作,请随时跳过有效。

HTTP 代理 GET 和 CONNECT 简要概述

除了作为与 Web 服务器通信的 request/response 传输之外,HTTP 协议还可以用于通过“转发”代理与其他服务器通信 - 通过对协议的一些小扩展。

可以通过直接与服务器建立TCP连接来发出正常的HTTP请求,例如www.example.com:80指定的服务器。这个 HTTP 请求看起来像这样:

> GET /foo HTTP/1.1
< HTTP/1.1 200 OK

如果您需要通过 HTTP 代理进行对话,您将改为与那个 代理服务器建立TCP 连接(例如,localhost:8080),并向该 HTTP 服务器发送以下 specially formatted HTTP request:

> GET http://www.example.com/foo http/1.1
< HTTP/1.1 200 OK

代理将为 www.example.com 进行 DNS 查找,建立 TCP 连接,并代表您发送 HTTP 请求。然后它将响应 body 流式传输回您的客户端。将您的浏览器配置为使用特定代理将导致它隐式重写所有 HTTP url 以通过该代理服务器。

将此与问题联系起来:Twisted 发布了一个 Proxy protocol 来理解这些 HTTP 代理 GET 请求。对于使用 HTTP 的常规浏览器,这工作得很好,没有问题。

现在,有了 TLS,我们想阻止代理嗅探我们本应受到保护的流量。这就是为什么浏览器,而不是像这样通过代理发送 https 请求:

> GET https://www.example.com/foo http/1.1
< HTTP/1.1 200 OK

改为使用CONNECT HTTP proxy extension method。如果代理支持,此方法会将连接置于“隧道”或 pass-through 模式。这些请求看起来像这样:

> CONNECT www.example.com:443 http/1.1
< HTTP/1.1 200 CONNECT OK

在此模式下,代理不会代表客户端处理所有 DNS、TCP、(TLS) 和 HTTP,代理仅处理前两个,即 DNS 和 TCP。如果代理成功建立到 example.com IP 的 TCP 连接,它将开始转发客户端发送到服务器的所有字节,并将从服务器接收到的所有字节转发回客户端 as-is。这允许客户端和服务器执行后续的 TLS 握手和 HTTP 请求,而无需意识到(或关心)存在代理服务器 in-between.

As-is,twisted.web.proxy 模块 实现 CONNECT 方法。对于使用此方法的请求,它将改为 return HTTP 501 Not Implemented 错误,导致任何连接的浏览器无法通过 https 加载资产。

twisted-connect-proxy 试图通过从 twisted.web.proxy 继承 class 现有的扭曲 classes 来填补这一空白,但实施对 CONNECT 方法的支持。

代码说明

代理可能有点复杂,但诀窍是要记住,对于每个请求,都有 2 个连接在起作用:客户端(例如浏览器)之间的“下游”连接连接到扭曲的网络代理服务器,以及从扭曲的网络代理服务器到远程 HTTP(s) 服务器的“上游”连接。

这里又是有问题的代码:

class ConnectProxy(Proxy):

    requestFactory = ConnectProxyRequest
    connectedRemote = None

    def requestDone(self, request):   
        if request.method == 'CONNECT' and self.connectedRemote is not None:  
            self.connectedRemote.connectedClient = self
        else:
            Proxy.requestDone(self, request)

我们将逐节进行:

class ConnectProxy(Proxy):

Proxy 这里是 twisted.web.proxy.Proxy,一个 twisted.internet.Protocol subclass 为 HTTP 服务器实现 HTTP 请求处理程序。我们把它subclass 成一个新的Protocol,叫做ConnectProxy.

requestFactory = ConnectProxyRequest
connectedRemote = None

parent class、twisted.web.proxy.Proxy 是 http 请求处理程序协议 twisted.web.http.HTTPChannel 的一个真正基本的子 class。它通常将 requestFactory 属性定义为 twisted.web.proxy.ProxyRequest class。但由于该请求处理程序不支持 CONNECT 方法,我们使用我们自己的 subclass,稍后在文件中定义,称为 ConnectProxyRequest。更好的名字可能是 ConnectOrGetProxyRequest,但是哦好吧。

connectedRemote = None 只是一种 shorthand 方式,可以让成员对该服务器协议的所有实例的访问权限默认为 None。对于针对此协议实例发出的 CONNECT 请求,此变量将在成功的 upstream 连接上分配 ConnectProxyClient 协议(稍后在源代码中定义)的实例。 ConnectProxyClient 负责通过此协议实例将 dataReceived 从上游连接(与远程服务器)转发回连接到此服务器的下游客户端。

def requestDone(self, request):   

requestDoneHTTPChannel 的内部调用,当来自该服务的所有 HTTP 响应(headers 和 body)r 已经发送到下游客户端。对于 GET 请求,这结束了来自上游 HTTP 服务器的响应。对于 CONNECT 请求,这决定了上游 TCP 连接握手成功或失败。

if request.method == 'CONNECT' and self.connectedRemote is not None:  
    self.connectedRemote.connectedClient = self

根据 the RFC,当(代理)服务器完成其 200 次成功 response/body 以响应 CONNECT 时,连接切换到 pass-through 模式。

在成功的 downstream(到远程服务器)连接中,self.connectedRemote 将被设置为该连接的 pass-through 协议,而不是 None。在那种情况下,我们给它一个对该协议的引用,以便它可以轻松地将其数据发送回上游客户端(例如,浏览器)。

如果上游连接失败,或者这是一个 GET 代理请求,我们已完成流式传输响应 body,我们属于 else: 情况

else:
    Proxy.requestDone(self, request)

这调用了 parent class 的 requestDone。这对于 HTTP 服务器在正确关闭或维护与下游客户端的持久“保持活动”连接方面正确实施 HTTP 很重要。