mercurial/httpclient/tests/simple_http_test.py
changeset 14243 861f28212398
child 14293 9adbb5ef0964
equal deleted inserted replaced
14242:5ee1309f7edb 14243:861f28212398
       
     1 # Copyright 2010, Google Inc.
       
     2 # All rights reserved.
       
     3 #
       
     4 # Redistribution and use in source and binary forms, with or without
       
     5 # modification, are permitted provided that the following conditions are
       
     6 # met:
       
     7 #
       
     8 #     * Redistributions of source code must retain the above copyright
       
     9 # notice, this list of conditions and the following disclaimer.
       
    10 #     * Redistributions in binary form must reproduce the above
       
    11 # copyright notice, this list of conditions and the following disclaimer
       
    12 # in the documentation and/or other materials provided with the
       
    13 # distribution.
       
    14 #     * Neither the name of Google Inc. nor the names of its
       
    15 # contributors may be used to endorse or promote products derived from
       
    16 # this software without specific prior written permission.
       
    17 
       
    18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
    19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
    20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
    21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
       
    22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
       
    23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
       
    24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
       
    25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
       
    26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    29 import unittest
       
    30 
       
    31 import http
       
    32 
       
    33 # relative import to ease embedding the library
       
    34 import util
       
    35 
       
    36 
       
    37 class SimpleHttpTest(util.HttpTestBase, unittest.TestCase):
       
    38 
       
    39     def _run_simple_test(self, host, server_data, expected_req, expected_data):
       
    40         con = http.HTTPConnection(host)
       
    41         con._connect()
       
    42         con.sock.data = server_data
       
    43         con.request('GET', '/')
       
    44 
       
    45         self.assertStringEqual(expected_req, con.sock.sent)
       
    46         self.assertEqual(expected_data, con.getresponse().read())
       
    47 
       
    48     def test_broken_data_obj(self):
       
    49         con = http.HTTPConnection('1.2.3.4:80')
       
    50         con._connect()
       
    51         self.assertRaises(http.BadRequestData,
       
    52                           con.request, 'POST', '/', body=1)
       
    53 
       
    54     def test_no_keepalive_http_1_0(self):
       
    55         expected_request_one = """GET /remote/.hg/requires HTTP/1.1
       
    56 Host: localhost:9999
       
    57 range: bytes=0-
       
    58 accept-encoding: identity
       
    59 accept: application/mercurial-0.1
       
    60 user-agent: mercurial/proto-1.0
       
    61 
       
    62 """.replace('\n', '\r\n')
       
    63         expected_response_headers = """HTTP/1.0 200 OK
       
    64 Server: SimpleHTTP/0.6 Python/2.6.1
       
    65 Date: Sun, 01 May 2011 13:56:57 GMT
       
    66 Content-type: application/octet-stream
       
    67 Content-Length: 33
       
    68 Last-Modified: Sun, 01 May 2011 13:56:56 GMT
       
    69 
       
    70 """.replace('\n', '\r\n')
       
    71         expected_response_body = """revlogv1
       
    72 store
       
    73 fncache
       
    74 dotencode
       
    75 """
       
    76         con = http.HTTPConnection('localhost:9999')
       
    77         con._connect()
       
    78         con.sock.data = [expected_response_headers, expected_response_body]
       
    79         con.request('GET', '/remote/.hg/requires',
       
    80                     headers={'accept-encoding': 'identity',
       
    81                              'range': 'bytes=0-',
       
    82                              'accept': 'application/mercurial-0.1',
       
    83                              'user-agent': 'mercurial/proto-1.0',
       
    84                              })
       
    85         self.assertStringEqual(expected_request_one, con.sock.sent)
       
    86         self.assertEqual(con.sock.closed, False)
       
    87         self.assertNotEqual(con.sock.data, [])
       
    88         self.assert_(con.busy())
       
    89         resp = con.getresponse()
       
    90         self.assertStringEqual(resp.read(), expected_response_body)
       
    91         self.failIf(con.busy())
       
    92         self.assertEqual(con.sock, None)
       
    93         self.assertEqual(resp.sock.data, [])
       
    94         self.assert_(resp.sock.closed)
       
    95 
       
    96     def test_multiline_header(self):
       
    97         con = http.HTTPConnection('1.2.3.4:80')
       
    98         con._connect()
       
    99         con.sock.data = ['HTTP/1.1 200 OK\r\n',
       
   100                          'Server: BogusServer 1.0\r\n',
       
   101                          'Multiline: Value\r\n',
       
   102                          '  Rest of value\r\n',
       
   103                          'Content-Length: 10\r\n',
       
   104                          '\r\n'
       
   105                          '1234567890'
       
   106                          ]
       
   107         con.request('GET', '/')
       
   108 
       
   109         expected_req = ('GET / HTTP/1.1\r\n'
       
   110                         'Host: 1.2.3.4\r\n'
       
   111                         'accept-encoding: identity\r\n\r\n')
       
   112 
       
   113         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   114         self.assertEqual(expected_req, con.sock.sent)
       
   115         resp = con.getresponse()
       
   116         self.assertEqual('1234567890', resp.read())
       
   117         self.assertEqual(['Value\n Rest of value'],
       
   118                          resp.headers.getheaders('multiline'))
       
   119 
       
   120     def testSimpleRequest(self):
       
   121         con = http.HTTPConnection('1.2.3.4:80')
       
   122         con._connect()
       
   123         con.sock.data = ['HTTP/1.1 200 OK\r\n',
       
   124                          'Server: BogusServer 1.0\r\n',
       
   125                          'MultiHeader: Value\r\n'
       
   126                          'MultiHeader: Other Value\r\n'
       
   127                          'MultiHeader: One More!\r\n'
       
   128                          'Content-Length: 10\r\n',
       
   129                          '\r\n'
       
   130                          '1234567890'
       
   131                          ]
       
   132         con.request('GET', '/')
       
   133 
       
   134         expected_req = ('GET / HTTP/1.1\r\n'
       
   135                         'Host: 1.2.3.4\r\n'
       
   136                         'accept-encoding: identity\r\n\r\n')
       
   137 
       
   138         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   139         self.assertEqual(expected_req, con.sock.sent)
       
   140         resp = con.getresponse()
       
   141         self.assertEqual('1234567890', resp.read())
       
   142         self.assertEqual(['Value', 'Other Value', 'One More!'],
       
   143                          resp.headers.getheaders('multiheader'))
       
   144         self.assertEqual(['BogusServer 1.0'],
       
   145                          resp.headers.getheaders('server'))
       
   146 
       
   147     def testHeaderlessResponse(self):
       
   148         con = http.HTTPConnection('1.2.3.4', use_ssl=False)
       
   149         con._connect()
       
   150         con.sock.data = ['HTTP/1.1 200 OK\r\n',
       
   151                          '\r\n'
       
   152                          '1234567890'
       
   153                          ]
       
   154         con.request('GET', '/')
       
   155 
       
   156         expected_req = ('GET / HTTP/1.1\r\n'
       
   157                         'Host: 1.2.3.4\r\n'
       
   158                         'accept-encoding: identity\r\n\r\n')
       
   159 
       
   160         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   161         self.assertEqual(expected_req, con.sock.sent)
       
   162         resp = con.getresponse()
       
   163         self.assertEqual('1234567890', resp.read())
       
   164         self.assertEqual({}, dict(resp.headers))
       
   165         self.assertEqual(resp.status, 200)
       
   166 
       
   167     def testReadline(self):
       
   168         con = http.HTTPConnection('1.2.3.4')
       
   169         con._connect()
       
   170         # make sure it trickles in one byte at a time
       
   171         # so that we touch all the cases in readline
       
   172         con.sock.data = list(''.join(
       
   173             ['HTTP/1.1 200 OK\r\n',
       
   174              'Server: BogusServer 1.0\r\n',
       
   175              'Connection: Close\r\n',
       
   176              '\r\n'
       
   177              '1\n2\nabcdefg\n4\n5']))
       
   178 
       
   179         expected_req = ('GET / HTTP/1.1\r\n'
       
   180                         'Host: 1.2.3.4\r\n'
       
   181                         'accept-encoding: identity\r\n\r\n')
       
   182 
       
   183         con.request('GET', '/')
       
   184         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   185         self.assertEqual(expected_req, con.sock.sent)
       
   186         r = con.getresponse()
       
   187         for expected in ['1\n', '2\n', 'abcdefg\n', '4\n', '5']:
       
   188             actual = r.readline()
       
   189             self.assertEqual(expected, actual,
       
   190                              'Expected %r, got %r' % (expected, actual))
       
   191 
       
   192     def testIPv6(self):
       
   193         self._run_simple_test('[::1]:8221',
       
   194                         ['HTTP/1.1 200 OK\r\n',
       
   195                          'Server: BogusServer 1.0\r\n',
       
   196                          'Content-Length: 10',
       
   197                          '\r\n\r\n'
       
   198                          '1234567890'],
       
   199                         ('GET / HTTP/1.1\r\n'
       
   200                          'Host: [::1]:8221\r\n'
       
   201                          'accept-encoding: identity\r\n\r\n'),
       
   202                         '1234567890')
       
   203         self._run_simple_test('::2',
       
   204                         ['HTTP/1.1 200 OK\r\n',
       
   205                          'Server: BogusServer 1.0\r\n',
       
   206                          'Content-Length: 10',
       
   207                          '\r\n\r\n'
       
   208                          '1234567890'],
       
   209                         ('GET / HTTP/1.1\r\n'
       
   210                          'Host: ::2\r\n'
       
   211                          'accept-encoding: identity\r\n\r\n'),
       
   212                         '1234567890')
       
   213         self._run_simple_test('[::3]:443',
       
   214                         ['HTTP/1.1 200 OK\r\n',
       
   215                          'Server: BogusServer 1.0\r\n',
       
   216                          'Content-Length: 10',
       
   217                          '\r\n\r\n'
       
   218                          '1234567890'],
       
   219                         ('GET / HTTP/1.1\r\n'
       
   220                          'Host: ::3\r\n'
       
   221                          'accept-encoding: identity\r\n\r\n'),
       
   222                         '1234567890')
       
   223 
       
   224     def doPost(self, con, expect_body, body_to_send='This is some POST data'):
       
   225         con.request('POST', '/', body=body_to_send,
       
   226                     expect_continue=True)
       
   227         expected_req = ('POST / HTTP/1.1\r\n'
       
   228                         'Host: 1.2.3.4\r\n'
       
   229                         'content-length: %d\r\n'
       
   230                         'Expect: 100-Continue\r\n'
       
   231                         'accept-encoding: identity\r\n\r\n' %
       
   232                         len(body_to_send))
       
   233         if expect_body:
       
   234             expected_req += body_to_send
       
   235         return expected_req
       
   236 
       
   237     def testEarlyContinueResponse(self):
       
   238         con = http.HTTPConnection('1.2.3.4:80')
       
   239         con._connect()
       
   240         sock = con.sock
       
   241         sock.data = ['HTTP/1.1 403 Forbidden\r\n',
       
   242                          'Server: BogusServer 1.0\r\n',
       
   243                          'Content-Length: 18',
       
   244                          '\r\n\r\n'
       
   245                          "You can't do that."]
       
   246         expected_req = self.doPost(con, expect_body=False)
       
   247         self.assertEqual(('1.2.3.4', 80), sock.sa)
       
   248         self.assertStringEqual(expected_req, sock.sent)
       
   249         self.assertEqual("You can't do that.", con.getresponse().read())
       
   250         self.assertEqual(sock.closed, True)
       
   251 
       
   252     def testDeniedAfterContinueTimeoutExpires(self):
       
   253         con = http.HTTPConnection('1.2.3.4:80')
       
   254         con._connect()
       
   255         sock = con.sock
       
   256         sock.data = ['HTTP/1.1 403 Forbidden\r\n',
       
   257                      'Server: BogusServer 1.0\r\n',
       
   258                      'Content-Length: 18\r\n',
       
   259                      'Connection: close',
       
   260                      '\r\n\r\n'
       
   261                      "You can't do that."]
       
   262         sock.read_wait_sentinel = 'Dear server, send response!'
       
   263         sock.close_on_empty = True
       
   264         # send enough data out that we'll chunk it into multiple
       
   265         # blocks and the socket will close before we can send the
       
   266         # whole request.
       
   267         post_body = ('This is some POST data\n' * 1024 * 32 +
       
   268                      'Dear server, send response!\n' +
       
   269                      'This is some POST data\n' * 1024 * 32)
       
   270         expected_req = self.doPost(con, expect_body=False,
       
   271                                    body_to_send=post_body)
       
   272         self.assertEqual(('1.2.3.4', 80), sock.sa)
       
   273         self.assert_('POST data\n' in sock.sent)
       
   274         self.assert_('Dear server, send response!\n' in sock.sent)
       
   275         # We expect not all of our data was sent.
       
   276         self.assertNotEqual(sock.sent, expected_req)
       
   277         self.assertEqual("You can't do that.", con.getresponse().read())
       
   278         self.assertEqual(sock.closed, True)
       
   279 
       
   280     def testPostData(self):
       
   281         con = http.HTTPConnection('1.2.3.4:80')
       
   282         con._connect()
       
   283         sock = con.sock
       
   284         sock.read_wait_sentinel = 'POST data'
       
   285         sock.early_data = ['HTTP/1.1 100 Co', 'ntinue\r\n\r\n']
       
   286         sock.data = ['HTTP/1.1 200 OK\r\n',
       
   287                      'Server: BogusServer 1.0\r\n',
       
   288                      'Content-Length: 16',
       
   289                      '\r\n\r\n',
       
   290                      "You can do that."]
       
   291         expected_req = self.doPost(con, expect_body=True)
       
   292         self.assertEqual(('1.2.3.4', 80), sock.sa)
       
   293         self.assertEqual(expected_req, sock.sent)
       
   294         self.assertEqual("You can do that.", con.getresponse().read())
       
   295         self.assertEqual(sock.closed, False)
       
   296 
       
   297     def testServerWithoutContinue(self):
       
   298         con = http.HTTPConnection('1.2.3.4:80')
       
   299         con._connect()
       
   300         sock = con.sock
       
   301         sock.read_wait_sentinel = 'POST data'
       
   302         sock.data = ['HTTP/1.1 200 OK\r\n',
       
   303                      'Server: BogusServer 1.0\r\n',
       
   304                      'Content-Length: 16',
       
   305                      '\r\n\r\n',
       
   306                      "You can do that."]
       
   307         expected_req = self.doPost(con, expect_body=True)
       
   308         self.assertEqual(('1.2.3.4', 80), sock.sa)
       
   309         self.assertEqual(expected_req, sock.sent)
       
   310         self.assertEqual("You can do that.", con.getresponse().read())
       
   311         self.assertEqual(sock.closed, False)
       
   312 
       
   313     def testServerWithSlowContinue(self):
       
   314         con = http.HTTPConnection('1.2.3.4:80')
       
   315         con._connect()
       
   316         sock = con.sock
       
   317         sock.read_wait_sentinel = 'POST data'
       
   318         sock.data = ['HTTP/1.1 100 ', 'Continue\r\n\r\n',
       
   319                      'HTTP/1.1 200 OK\r\n',
       
   320                      'Server: BogusServer 1.0\r\n',
       
   321                      'Content-Length: 16',
       
   322                      '\r\n\r\n',
       
   323                      "You can do that."]
       
   324         expected_req = self.doPost(con, expect_body=True)
       
   325         self.assertEqual(('1.2.3.4', 80), sock.sa)
       
   326         self.assertEqual(expected_req, sock.sent)
       
   327         resp = con.getresponse()
       
   328         self.assertEqual("You can do that.", resp.read())
       
   329         self.assertEqual(200, resp.status)
       
   330         self.assertEqual(sock.closed, False)
       
   331 
       
   332     def testSlowConnection(self):
       
   333         con = http.HTTPConnection('1.2.3.4:80')
       
   334         con._connect()
       
   335         # simulate one byte arriving at a time, to check for various
       
   336         # corner cases
       
   337         con.sock.data = list('HTTP/1.1 200 OK\r\n'
       
   338                              'Server: BogusServer 1.0\r\n'
       
   339                              'Content-Length: 10'
       
   340                              '\r\n\r\n'
       
   341                              '1234567890')
       
   342         con.request('GET', '/')
       
   343 
       
   344         expected_req = ('GET / HTTP/1.1\r\n'
       
   345                         'Host: 1.2.3.4\r\n'
       
   346                         'accept-encoding: identity\r\n\r\n')
       
   347 
       
   348         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   349         self.assertEqual(expected_req, con.sock.sent)
       
   350         self.assertEqual('1234567890', con.getresponse().read())
       
   351 
       
   352     def testTimeout(self):
       
   353         con = http.HTTPConnection('1.2.3.4:80')
       
   354         con._connect()
       
   355         con.sock.data = []
       
   356         con.request('GET', '/')
       
   357         self.assertRaises(http.HTTPTimeoutException,
       
   358                           con.getresponse)
       
   359 
       
   360         expected_req = ('GET / HTTP/1.1\r\n'
       
   361                         'Host: 1.2.3.4\r\n'
       
   362                         'accept-encoding: identity\r\n\r\n')
       
   363 
       
   364         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   365         self.assertEqual(expected_req, con.sock.sent)
       
   366 # no-check-code