| | 495 | class 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 | |
| | 522 | class 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 |