Twisted HTTP Proxy 中 Channel 被意外设置为 None 的问题与解决方案

70 阅读2分钟

在开发一个 HTTP 代理时,我遇到了一个问题:当服务器连接断开时,HTTP 通道的 Channel 属性会被意外地设置为 None,导致后续的操作引发错误。在深入调查后,我发现这是因为在调用 loseConnection() 方法关闭连接之前,Channel 属性就被设置为了 None。这导致了代码中对 Channel 属性的访问报错,提示 None 对象没有 transport 属性。

下面是异常信息:

Traceback (most recent call last):
  File "C:\Program Files\Python 2.6.2\lib\site-packages\wx-2.8-msw-unicode\wx_core.py", line 7303, in MainLoop
    return _core_.PyApp_MainLoop(*args, **kwargs)
  File "C:\Program Files\Python 2.6.2\lib\site-packages\wx-2.8-msw-unicode\wx_core.py", line 14640, in <lambda>
    lambda event: event.callable(*event.args, **event.kw) )
  File "C:\Program Files\Python 2.6.2\lib\site-packages\twisted\internet_threadedselect.py", line 243, in _interleave
    getattr(self, '_process_' + msg)(*args)
  File "C:\Program Files\Python 2.6.2\lib\site-packages\twisted\internet_threadedselect.py", line 209, in _process_Notify
    _logrun(selectable, _drdw, selectable, method, dct)
--- <exception caught here> ---
  File "C:\Program Files\Python 2.6.2\lib\site-packages\twisted\python\log.py", line 84, in callWithLogger
    return callWithContext({"system": lp}, func, *args, **kw)
  File "C:\Program Files\Python 2.6.2\lib\site-packages\twisted\python\log.py", line 69, in callWithContext
    return context.call({ILogContext: newCtx}, func, *args, **kw)
  File "C:\Program Files\Python 2.6.2\lib\site-packages\twisted\python\context.py", line 59, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File "C:\Program Files\Python 2.6.2\lib\site-packages\twisted\python\context.py", line 37, in callWithContext
    return func(*args,**kw)
  File "C:\Program Files\Python 2.6.2\lib\site-packages\twisted\internet_threadedselect.py", line 303, in _doReadOrWrite
    self._disconnectSelectable(selectable, why, method == "doRead")
  File "C:\Program Files\Python 2.6.2\lib\site-packages\twisted\internet\posixbase.py", line 253, in _disconnectSelectable
    selectable.connectionLost(f)
  File "C:\Program Files\Python 2.6.2\lib\site-packages\twisted\internet\tcp.py", line 677, in connectionLost
    Connection.connectionLost(self, reason)
  File "C:\Program Files\Python 2.6.2\lib\site-packages\twisted\internet\tcp.py", line 519, in connectionLost
    protocol.connectionLost(reason)
  File "C:\Program Files\Python 2.6.2\lib\site-packages\twisted\web\http.py", line 489, in connectionLost
    self.handleResponseEnd()
  File "C:\Documents and Settings\Admin\My Documents\Mercurial\sharky\ProxyServer.py", line 103, in handleResponseEnd
    p.channel.transport.loseConnection()
exceptions.AttributeError: 'NoneType' object has no attribute 'transport'

2、解决方案

要解决这个问题,我们需要在调用 loseConnection() 方法关闭连接之前,先检查 Channel 属性是否为 None。如果为 None,则无需再次调用 loseConnection() 方法,因为连接已经断开。

经过分析,我发现问题出在以下代码中:

    for p in self.producers:
        p.channel.transport.loseConnection()

在循环中,我尝试关闭所有生产者 (producer) 的连接。然而,在某些情况下,p.channel 可能已经被设置为 None 了,导致 p.channel.transport.loseConnection() 调用失败。

为了解决这个问题,我修改了代码,增加了对 p.channel 的检查:

    for p in self.producers:
        if p.channel:
            p.channel.transport.loseConnection()

现在,只有当 p.channel 不为 None 时,才会调用 loseConnection() 方法关闭连接。这样可以确保不会在连接已经断开的情况下再次调用 loseConnection() 方法,从而避免了错误的发生。

修正后的代码如下:

self.connectionDone = True
print self.producers
for p in self.producers:
    print p
    print p.channel
    print dir(p)
    if p.channel:
        p.channel.transport.loseConnection()

self.transport.loseConnection()

经过修改后,程序能够正确关闭所有连接,不再出现错误。