Ticket #3207: test_client.py.patch

File test_client.py.patch, 9.8 KB (added by camrdale, 8 years ago)

patch to test pipelining in the http client

  • web2/test/test_client.py

    old new  
    55Tests for HTTP client.
    66"""
    77
    8 from twisted.internet import protocol, defer
     8from twisted.internet import protocol, defer, reactor
    99
    1010from twisted.web2.client import http
    1111from twisted.web2 import http_headers
     
    3535
    3636
    3737class ClientTests(HTTPTests):
    38     def connect(self, logFile=None, maxPipeline=4,
     38    def connect(self, mgr = None, logFile=None, maxPipeline=4,
    3939                inputTimeOut=60000, betweenRequestsTimeOut=600000):
    4040        cxn = TestConnection()
    4141
    42         cxn.client = http.HTTPClientProtocol()
     42        cxn.client = http.HTTPClientProtocol(mgr)
    4343        cxn.client.inputTimeOut = inputTimeOut
    4444        cxn.server = TestServer()
    4545
     
    492492        d.addCallback(cb)
    493493        return d
    494494
     495class TestHTTPClientManager(object):
     496    """
     497    An HTTPClientManager for testing purposes. It calls a deferred
     498    whenever the client becomes Idle, Gone, or Pipelined.
     499    """
     500
     501    def __init__(self):
     502        self.df = defer.Deferred()
     503
     504    def clientBusy(self, proto):
     505        pass
     506
     507    def clientIdle(self, proto):
     508        df = self.df
     509        self.df = defer.Deferred()
     510        df.callback('idle')
     511
     512    def clientPipelining(self, proto):
     513        df = self.df
     514        self.df = defer.Deferred()
     515        df.callback('pipeline')
     516
     517    def clientGone(self, proto):
     518        df = self.df
     519        self.df = defer.Deferred()
     520        df.callback('gone')
     521
     522class TestHTTPClientPipelining(ClientTests):
     523    """
     524    Test that the http client works with pipelined requests.
     525    """
     526   
     527    timeout = 2
     528   
     529    def test_pipelining(self):
     530        """
     531        Submitting multiple requests using the manager.
     532        """
     533        requests = 5
     534        mgr = TestHTTPClientManager()
     535        cxn = self.connect(mgr = mgr, inputTimeOut=None)
     536        req = http.ClientRequest('GET', '/', None, None)
     537
     538        didIt = [0]
     539
     540        def sendData(resp, data):
     541            reactor.callLater(0, self.writeToClient, cxn, data)
     542            return resp
     543
     544        def sendNextResponse(resp, data):
     545            reactor.callLater(0, self.writeLines, cxn, data)
     546            return resp
     547
     548        def submitRequest(_):
     549            didIt[0] += 1
     550
     551            if didIt[0] == requests - 1:
     552                keepAlive='close'
     553            else:
     554                keepAlive='Keep-Alive'
     555
     556            cxn.server.data = ''
     557            df = mgr.df
     558
     559            d = cxn.client.submitRequest(req, closeAfter=(didIt[0] == requests))
     560            d.addCallback(sendData, 'foobar' + str(didIt[0]))
     561           
     562            if didIt[0] < requests:
     563                d.addCallback(sendNextResponse, ('HTTP/1.1 200 OK',
     564                                      'Connection: '+ keepAlive,
     565                                      'Content-Length: 7',
     566                                      '\r\n'))
     567                d.addCallback(self.checkResponse, 200, [], 7, 'foobar' + str(didIt[0]))
     568                df.addCallback(submitRequest)
     569           
     570            if didIt[0] == 1:
     571                self.writeLines(cxn, ('HTTP/1.1 200 OK',
     572                                      'Connection: '+ keepAlive,
     573                                      'Content-Length: 7',
     574                                      '\r\n'))
     575
     576            d = defer.DeferredList([d, df], fireOnOneErrback=True)
     577            if didIt[0] == requests:
     578                d.addCallback(lambda _: self.assertDone(cxn))
     579            return d
     580
     581        d = submitRequest(None)
     582
     583        return d
     584
     585    def test_firstRequestPipeline(self):
     586        """
     587        Verify that the client manager gets a call before the first
     588        request completes.
     589        """
     590        mgr = TestHTTPClientManager()
     591        cxn = self.connect(mgr = mgr, inputTimeOut=None)
     592        req = http.ClientRequest('GET', '/', None, None)
     593
     594        def gotData(data):
     595            self.assertEquals(data, '1234567890')
     596
     597        def sendResp(type):
     598            self.assertEquals(type, 'pipeline')
     599
     600        def gotResp(resp):
     601            self.assertEquals(resp.code, 200)
     602            self.assertHeaders(resp, [])
     603            self.assertEquals(resp.stream.length, 10)
     604
     605            self.writeToClient(cxn, '1234567890')
     606
     607            return defer.maybeDeferred(resp.stream.read).addCallback(gotData)
     608
     609        df = mgr.df.addCallback(sendResp)
     610        d = cxn.client.submitRequest(req, closeAfter = False).addCallback(gotResp)
     611
     612        self.assertReceived(cxn, 'GET / HTTP/1.1',
     613                            ['Connection: Keep-Alive'])
     614
     615        self.writeLines(cxn, ('HTTP/1.1 200 OK',
     616                              'Content-Length: 10',
     617                              '\r\n'))
     618
     619        return df
     620
     621    def test_pipelineClosed(self):
     622        """
     623        Submitting multiple requests but the server closes the connection
     624        while there are pipelined requests.
     625        """
     626        requests = 5
     627        stopAfter = 2
     628        mgr = TestHTTPClientManager()
     629        cxn = self.connect(mgr = mgr, inputTimeOut=None)
     630        req = http.ClientRequest('GET', '/', None, None)
     631
     632        didIt = [0]
     633
     634        def sendData(resp, data):
     635            reactor.callLater(0, self.writeToClient, cxn, data)
     636            return resp
     637
     638        def sendNextResponse(resp, data):
     639            reactor.callLater(0, self.writeLines, cxn, data)
     640            return resp
     641
     642        def submitRequest(_):
     643            didIt[0] += 1
     644
     645            if didIt[0] == requests - 1 or didIt[0] == stopAfter:
     646                keepAlive='close'
     647            else:
     648                keepAlive='Keep-Alive'
     649
     650            cxn.server.data = ''
     651            df = mgr.df
     652
     653            d = cxn.client.submitRequest(req, closeAfter=(didIt[0] == requests))
     654            d.addCallback(sendData, 'foobar' + str(didIt[0]))
     655           
     656            if didIt[0] < requests:
     657                if didIt[0] <= stopAfter:
     658                    d.addCallback(sendNextResponse, ('HTTP/1.1 200 OK',
     659                                          'Connection: '+ keepAlive,
     660                                          'Content-Length: 7',
     661                                          '\r\n'))
     662                    d.addCallback(self.checkResponse, 200, [], 7, 'foobar' + str(didIt[0]))
     663                else:
     664                    self.assertFailure(d, getattr(http, 'PipelineError', http.ProtocolError))
     665                d = df.addCallback(submitRequest)
     666            else:
     667                self.assertFailure(d, getattr(http, 'PipelineError', http.ProtocolError))
     668
     669            if didIt[0] == 1:
     670                self.writeLines(cxn, ('HTTP/1.1 200 OK',
     671                                      'Connection: '+ keepAlive,
     672                                      'Content-Length: 7',
     673                                      '\r\n'))
     674
     675            d = defer.DeferredList([d, df], fireOnOneErrback=True)
     676            if didIt[0] == requests:
     677                d.addCallback(lambda _: self.assertDone(cxn))
     678            return d
     679
     680        d = submitRequest(None)
     681
     682        return d
     683
     684    def test_pipelineStreamClosed(self):
     685        """
     686        Submitting multiple requests but the client closes the stream
     687        of a request while there are other requests pipelined.
     688        """
     689        requests = 5
     690        closeAfter = 3
     691        mgr = TestHTTPClientManager()
     692        cxn = self.connect(mgr = mgr, inputTimeOut=None)
     693        req = http.ClientRequest('GET', '/', None, None)
     694
     695        didIt = [0]
     696
     697        def sendData(resp, data):
     698            reactor.callLater(0, self.writeToClient, cxn, data)
     699            return resp
     700
     701        def sendNextResponse(resp, data):
     702            reactor.callLater(0, self.writeLines, cxn, data)
     703            return resp
     704
     705        def closeStream(resp, code, headers, length, data):
     706            def gotData(gotdata):
     707                self.assertEquals(gotdata, data)
     708            self.assertEquals(resp.code, code)
     709            self.assertHeaders(resp, headers)
     710            self.assertEquals(resp.stream.length, length)
     711            resp.stream.close()
     712
     713        def submitRequest(_):
     714            didIt[0] += 1
     715
     716            if didIt[0] == requests - 1:
     717                keepAlive='close'
     718            else:
     719                keepAlive='Keep-Alive'
     720
     721            cxn.server.data = ''
     722            df = mgr.df
     723
     724            d = cxn.client.submitRequest(req, closeAfter=(didIt[0] == requests))
     725            d.addCallback(sendData, 'foobar' + str(didIt[0]))
     726           
     727            if didIt[0] < requests:
     728                if didIt[0] <= closeAfter:
     729                    d.addCallback(sendNextResponse, ('HTTP/1.1 200 OK',
     730                                          'Connection: '+ keepAlive,
     731                                          'Content-Length: 7',
     732                                          '\r\n'))
     733                    if didIt[0] == closeAfter:
     734                        d.addCallback(closeStream, 200, [], 7, 'foobar' + str(didIt[0]))
     735                    else:
     736                        d.addCallback(self.checkResponse, 200, [], 7, 'foobar' + str(didIt[0]))
     737                else:
     738                    self.assertFailure(d, getattr(http, 'PipelineError', http.ProtocolError))
     739                d = df.addCallback(submitRequest)
     740            else:
     741                self.assertFailure(d, getattr(http, 'PipelineError', http.ProtocolError))
     742
     743            if didIt[0] == 1:
     744                self.writeLines(cxn, ('HTTP/1.1 200 OK',
     745                                      'Connection: '+ keepAlive,
     746                                      'Content-Length: 7',
     747                                      '\r\n'))
     748
     749            d = defer.DeferredList([d, df], fireOnOneErrback=True)
     750            if didIt[0] == requests:
     751                d.addCallback(lambda _: self.assertDone(cxn))
     752            return d
     753
     754        d = submitRequest(None)
     755
     756        return d