title: Implementing a reconnecting http client in twisted
body: <p>I am implementing a library module to serve as client for couchdb change notifications. I want to have the library in "continuous" mode, that is, the connection must remain open forever, or at least it must reconnect if the connection has been closed, so that couchdb has a channel to notify of any new changes happening in the database. I will then process those notifications to generate certain events (this is not yet implemented).</p>
  
  <p>The approach that I have chosen is to use the ReconnectingClientFactory (which does automatic re-connection as per an elaborated algorithm) as basis for my Protocol Factory. Whenever the connection has been established, the buildProtocol method is called. In this method I create the protocol instance, and fire a callLater (immediately) to signal that the connection is ready. In the cdConnected function, I send the request and add a callback to process the received data (cbReceived).</p>
  
  <p>The code does reconnection as expected, but I am having two different problems:</p>
  
  <ul>
  <li>the request is failing (no data is sent over the tcp connection), but I do not know why.</li>
  <li>an error is generated, even though the connection is closed cleanly.</li>
  </ul>
  
  <p>Maybe somebody has an idea of what I am doing wrong?</p>
  
  <p>Thanks!</p>
  
  <p>(edit: the "Connection was closed cleanly." error is being printed by myself, so it can be ignored)</p>
  
  <p>Here is the code:</p>
  
  <pre><code>from   twisted.internet              import defer
  from   twisted.internet.protocol     import ReconnectingClientFactory
  from   twisted.web._newclient        import HTTP11ClientProtocol
  from   twisted.web._newclient        import Request
  from   twisted.web.client            import _parse
  
  class MyReconnectingClientFactory(ReconnectingClientFactory):
  
      def __init__(self, reactor, cbConnected):
          self.reactor      = reactor
          self.cbConnected  = cbConnected
  
      def startedConnecting(self, connector):
          print 'Started to connect ...'
  
      def buildProtocol(self, addr):
          print 'Resetting reconnection delay'
          self.resetDelay()
          proto = HTTP11ClientProtocol()
          self.reactor.callLater(0, self.cbConnected, proto)
          return proto
  
      def clientConnectionLost(self, connector, reason):
          print 'Lost connection.  Reason:', reason
          ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
  
      def clientConnectionFailed(self, connector, reason):
          print 'Connection failed. Reason:', reason
          ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
  
  def cbReceived(response):
      print response
  
  def printError(failure):
      print "printError &gt; %s" % (str(failure))
  
  def cbConnected(proto):
      print "Sending request ..."
      req = Request(method, path, headers, bodyProducer)
      d = proto.request(req)
      d.addCallback(cbReceived).addErrback(printError)
      return d
  
  from twisted.internet import reactor
  
  uri='http://localhost:5984/cn/_changes?feed=continuous'
  method='GET'
  headers=None
  bodyProducer=None
  
  scheme, host, port, path = _parse(uri)
  factory = MyReconnectingClientFactory(reactor, cbConnected)
  reactor.connectTCP(host, port, factory)
  reactor.run()
  </code></pre>
  
  <p>And here is the output:</p>
  
  <pre><code>Started to connect ...
  Resetting reconnection delay
  Sending request ...
  printError &gt; [Failure instance: Traceback (failure with no frames): &lt;class 'twisted.web._newclient.RequestGenerationFailed'&gt;: [&lt;twisted.python.failure.Failure &lt;type 'exceptions.AttributeError'&gt;&gt;]
  ]
  Lost connection.  Reason: [Failure instance: Traceback (failure with no frames): &lt;class 'twisted.internet.error.ConnectionDone'&gt;: Connection was closed cleanly.
  ]
  Started to connect ...
  Resetting reconnection delay
  Sending request ...
  printError &gt; [Failure instance: Traceback (failure with no frames): &lt;class 'twisted.web._newclient.RequestGenerationFailed'&gt;: [&lt;twisted.python.failure.Failure &lt;type 'exceptions.AttributeError'&gt;&gt;]
  ]
  Lost connection.  Reason: [Failure instance: Traceback (failure with no frames): &lt;class 'twisted.internet.error.ConnectionDone'&gt;: Connection was closed cleanly.
  ]
  </code></pre>