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
做了什么?
答案 0 :(得分:3)
因此,此代码段的原始来源是我的github repositories之一,其中包含扭曲的HTTP代理服务器协议以支持CONNECT方法。
简短的回答是,它将self
(下游协议实例 - 客户端和此扭曲服务器之间)分配给上游连接协议实例的成员(到远程https服务器) ,以便从上游连接收到的任何数据都可以很容易地写回下游客户端的传输。
我对代码有更长的解释,但它有助于对HTTP代理协议有一个基本的了解,所以我会尝试设置一些上下文,但如果你已经知道如何,可以随意跳过这很有效。
除了作为与Web服务器通信的请求/响应传输之外,HTTP协议还可用于通过" forward"进行通信。代理 - 到其他服务器 - 通过协议的一些小扩展。
可以通过直接与服务器建立TCP连接来进行正常的HTTP请求,例如,由www.example.com:80指定。此HTTP请求如下所示:
> GET /foo HTTP/1.1
< HTTP/1.1 200 OK
如果您需要通过与HTTP代理进行对话,您将改为与 代理服务器(例如localhost:8080)建立TCP连接,然后发送该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请求。然后它会将响应主体流回客户端。将浏览器配置为使用特定代理将导致它隐式重写所有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。如果代理支持它,则此方法将连接放入&#34;隧道&#34;或传递模式。这些请求看起来像这样:
> CONNECT www.example.com:443 http/1.1
< HTTP/1.1 200 CONNECT OK
在此模式下,代理仅代表客户端处理DNS,TCP,(TLS)和HTTP的所有舞蹈,而代理只执行前两个DNS和TCP。如果代理成功建立到example.com IP的TCP连接,则它将开始转发客户端发送到服务器的所有字节,并将从服务器接收的所有字节原样转发回客户端。这允许客户端和服务器执行后续的TLS握手和HTTP请求,而不会意识到(或关心)中间存在代理服务器。
按原样,twisted.web.proxy
模块不实现CONNECT方法。对于使用此方法的请求,它将返回HTTP 501 Not Implemented
错误,导致任何连接的浏览器无法通过https加载资源。
twisted-connect-proxy尝试通过从twisted.web.proxy
继承现有的扭曲类来填补这个空白,但是实现了对CONNECT方法的支持。
代理可能有点毛茸茸,但诀窍是要记住,对于每个请求,有2个连接在起作用:&#34;下游&#34;连接到扭曲的Web代理服务器的客户端(例如,浏览器)与&#34;上游&#34;之间的连接。连接,从扭曲的Web代理服务器到远程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
子类,为HTTP服务器实现HTTP请求处理程序。我们将其子类化为一个名为Protocol
的新ConnectProxy
。
requestFactory = ConnectProxyRequest
connectedRemote = None
父类twisted.web.proxy.Proxy
是http请求处理程序协议twisted.web.http.HTTPChannel
的真正基本子类。它通常将requestFactory
属性定义为twisted.web.proxy.ProxyRequest
类。但由于该请求处理程序不支持CONNECT方法,因此我们使用稍后在文件中定义的自己的子类,称为ConnectProxyRequest
。一个更好的名字可能是ConnectOrGetProxyRequest
,但是很好。
connectedRemote = None
只是将此服务器协议的所有实例上的成员访问权限默认为None的简便方法。对于针对此协议实例发出的CONNECT请求,将在成功的上游连接上为此变量分配ConnectProxyClient
协议(稍后在源中定义)的实例。 ConnectProxyClient
负责通过此协议实例将dataReceived
从上游连接(使用远程服务器)转发回连接到此服务器的下游客户端。
def requestDone(self, request):
requestDone
由HTTPChannel
的内容调用,此时此服务器的所有HTTP响应(标头和正文)都已发送到下游客户端。对于GET请求,这结束了来自上游HTTP服务器的响应。对于CONNECT请求,这导致上游TCP连接握手成功或失败。
if request.method == 'CONNECT' and self.connectedRemote is not None:
self.connectedRemote.connectedClient = self
每the RFC,当(代理)服务器响应CONNECT完成其200成功响应/正文时,连接切换到直通模式。
在成功的下游(到远程服务器)连接上,self.connectedRemote
将被设置为该连接的传递协议,而不是None。在这种情况下,我们为此协议提供参考,以便它可以轻松地将其数据发送回上游客户端(例如,浏览器)。
如果上游连接失败,或者这是我们完成流式传输响应主体的GET代理请求,我们会陷入else:
案例
else:
Proxy.requestDone(self, request)
这会调用父类的requestDone
。这对于HTTP服务器正确实现HTTP以正确关闭或维护持久性&#34; keepalive&#34;而言非常重要。与下游客户的连接。
答案 1 :(得分:0)
技术上:
if-condition检查名称self.connectedRemote
是否有某个对象。在此对象中,属性connectedClient
设置为self
。
它做了什么?
从这段代码中很难说,因为我不知道想要的对象connectedRemote
是什么。我推测,在成功完成CONNECT
请求后,客户端(connectedClient
)连接到远程服务器(connectedRemote
)设置为self
(此代理实例)。
如果确实如此,我有点臭,因为connectedRemote
对象应该对此负责。