Ticket #6104: get-page-cancel.diff

File get-page-cancel.diff, 6.0 KB (added by luks, 4 years ago)
  • twisted/web/client.py

    === modified file 'twisted/web/client.py'
     
    311311    def __init__(self, url, method='GET', postdata=None, headers=None,
    312312                 agent="Twisted PageGetter", timeout=0, cookies=None,
    313313                 followRedirect=True, redirectLimit=20,
    314                  afterFoundGet=False):
     314                 afterFoundGet=False, canceller=None):
    315315        self.followRedirect = followRedirect
    316316        self.redirectLimit = redirectLimit
    317317        self._redirectCount = 0
     
    336336
    337337        self.waiting = 1
    338338        self._disconnectedDeferred = defer.Deferred()
    339         self.deferred = defer.Deferred()
     339        self.deferred = defer.Deferred(canceller)
    340340        # Make sure the first callback on the result Deferred pauses the
    341341        # callback chain until the request connection is closed.
    342342        self.deferred.addBoth(self._waitForDisconnect)
     
    408408        result has yet been provided to the result Deferred, provide the
    409409        connection failure reason as an error result.
    410410        """
    411         if self.waiting:
    412             self.waiting = 0
    413             # If the connection attempt failed, there is nothing more to
    414             # disconnect, so just fire that Deferred now.
    415             self._disconnectedDeferred.callback(None)
    416             self.deferred.errback(reason)
     411        self.noPage(reason)
     412        # If the connection attempt failed, there is nothing more to
     413        # disconnect, so just fire that Deferred now.
     414        self._disconnectedDeferred.callback(None)
    417415
    418416
    419417
     
    427425                 method='GET', postdata=None, headers=None,
    428426                 agent="Twisted client", supportPartial=0,
    429427                 timeout=0, cookies=None, followRedirect=1,
    430                  redirectLimit=20, afterFoundGet=False):
     428                 redirectLimit=20, afterFoundGet=False,
     429                 canceller=None):
    431430        self.requestedPartial = 0
    432431        if isinstance(fileOrName, types.StringTypes):
    433432            self.fileName = fileOrName
     
    445444            self, url, method=method, postdata=postdata, headers=headers,
    446445            agent=agent, timeout=timeout, cookies=cookies,
    447446            followRedirect=followRedirect, redirectLimit=redirectLimit,
    448             afterFoundGet=afterFoundGet)
     447            afterFoundGet=afterFoundGet, canceller=canceller)
    449448
    450449
    451450    def gotHeaders(self, headers):
     
    596595
    597596    @return: The factory created by C{factoryFactory}
    598597    """
     598    def cancel(d):
     599        factory.noPage(defer.CancelledError())
     600        connector.disconnect()
    599601    scheme, host, port, path = _parse(url)
    600     factory = factoryFactory(url, *args, **kwargs)
     602    factory = factoryFactory(url, canceller=cancel, *args, **kwargs)
    601603    if scheme == 'https':
    602604        from twisted.internet import ssl
    603605        if contextFactory is None:
    604606            contextFactory = ssl.ClientContextFactory()
    605         reactor.connectSSL(host, port, factory, contextFactory)
     607        connector = reactor.connectSSL(host, port, factory, contextFactory)
    606608    else:
    607         reactor.connectTCP(host, port, factory)
     609        connector = reactor.connectTCP(host, port, factory)
    608610    return factory
    609611
    610612
     
    615617    Download a page. Return a deferred, which will callback with a
    616618    page (as a string) or errback with a description of the error.
    617619
     620    If the deferred is cancelled before the request completes, the
     621    connection is closed and the deferred will fire with a
     622    L{defer.CancelledError}.
     623
    618624    See L{HTTPClientFactory} to see what extra arguments can be passed.
    619625    """
    620626    return _makeGetterFactory(
     
    628634    """
    629635    Download a web page to a file.
    630636
     637    Download a page. Return a deferred, which will callback with None
     638    or errback with a description of the error.
     639
     640    If the deferred is cancelled before the request completes, the
     641    connection is closed and the deferred will fire with a
     642    L{defer.CancelledError}.
     643
    631644    @param file: path to file on filesystem, or file-like object.
    632645
    633     See HTTPDownloader to see what extra args can be passed.
     646    See L{HTTPDownloader} to see what extra args can be passed.
    634647    """
    635648    factoryFactory = lambda url, *a, **kw: HTTPDownloader(url, file, *a, **kw)
    636649    return _makeGetterFactory(
  • twisted/web/test/test_webclient.py

    === modified file 'twisted/web/test/test_webclient.py'
     
    507507            defer.TimeoutError)
    508508
    509509
     510    def test_getPageCancelImmediately(self):
     511        """
     512        Call L{getPage} and cancel it before it has chance to try to
     513        connect to the server. The L{Deferred} returned by L{getPage}
     514        fails with L{defer.CancelledError}.
     515        """
     516        d = client.getPage(self.getURL("wait"), timeout=10)
     517        d.cancel()
     518        return self.assertFailure(d, defer.CancelledError)
     519
     520
     521    def test_getPageCancelLater(self):
     522        """
     523        Call L{getPage} and cancel it after it has connected to the server,
     524        but before it was able to deliver the results. The L{Deferred}
     525        returned by L{getPage} fails with L{defer.CancelledError}.
     526        """
     527        d = client.getPage(self.getURL("wait"), timeout=10)
     528        reactor.callLater(0.01, d.cancel)
     529        return self.assertFailure(d, defer.CancelledError)
     530
     531
     532    def test_getPageCancelAfter(self):
     533        """
     534        Call L{getPage} and attempt to cancel it after it has delivered
     535        the results. The cancellation is ignored and the L{Deferred}
     536        returned by L{getPage} is called back with the page contents.
     537        """
     538        d = client.getPage(self.getURL("file"))
     539        def cancelRequest(content):
     540            d.cancel()
     541            return content
     542        d.addCallback(cancelRequest)
     543        d.addCallback(self.assertEqual, "0123456789")
     544        return d
     545
     546
    510547    def testDownloadPage(self):
    511548        downloads = []
    512549        downloadData = [("file", self.mktemp(), "0123456789"),