Ticket #3207: test_client.py.patch

File test_client.py.patch, 9.8 KB (added by camrdale, 7 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