mercurial/httpclient/tests/simple_http_test.py
branchstable
changeset 17225 a06e2681dd17
parent 17222 98823bd0d697
parent 17224 23b247234454
child 17226 436cc9d017c6
equal deleted inserted replaced
17222:98823bd0d697 17225:a06e2681dd17
     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 socket
       
    30 import unittest
       
    31 
       
    32 import http
       
    33 
       
    34 # relative import to ease embedding the library
       
    35 import util
       
    36 
       
    37 
       
    38 class SimpleHttpTest(util.HttpTestBase, unittest.TestCase):
       
    39 
       
    40     def _run_simple_test(self, host, server_data, expected_req, expected_data):
       
    41         con = http.HTTPConnection(host)
       
    42         con._connect()
       
    43         con.sock.data = server_data
       
    44         con.request('GET', '/')
       
    45 
       
    46         self.assertStringEqual(expected_req, con.sock.sent)
       
    47         self.assertEqual(expected_data, con.getresponse().read())
       
    48 
       
    49     def test_broken_data_obj(self):
       
    50         con = http.HTTPConnection('1.2.3.4:80')
       
    51         con._connect()
       
    52         self.assertRaises(http.BadRequestData,
       
    53                           con.request, 'POST', '/', body=1)
       
    54 
       
    55     def test_no_keepalive_http_1_0(self):
       
    56         expected_request_one = """GET /remote/.hg/requires HTTP/1.1
       
    57 Host: localhost:9999
       
    58 range: bytes=0-
       
    59 accept-encoding: identity
       
    60 accept: application/mercurial-0.1
       
    61 user-agent: mercurial/proto-1.0
       
    62 
       
    63 """.replace('\n', '\r\n')
       
    64         expected_response_headers = """HTTP/1.0 200 OK
       
    65 Server: SimpleHTTP/0.6 Python/2.6.1
       
    66 Date: Sun, 01 May 2011 13:56:57 GMT
       
    67 Content-type: application/octet-stream
       
    68 Content-Length: 33
       
    69 Last-Modified: Sun, 01 May 2011 13:56:56 GMT
       
    70 
       
    71 """.replace('\n', '\r\n')
       
    72         expected_response_body = """revlogv1
       
    73 store
       
    74 fncache
       
    75 dotencode
       
    76 """
       
    77         con = http.HTTPConnection('localhost:9999')
       
    78         con._connect()
       
    79         con.sock.data = [expected_response_headers, expected_response_body]
       
    80         con.request('GET', '/remote/.hg/requires',
       
    81                     headers={'accept-encoding': 'identity',
       
    82                              'range': 'bytes=0-',
       
    83                              'accept': 'application/mercurial-0.1',
       
    84                              'user-agent': 'mercurial/proto-1.0',
       
    85                              })
       
    86         self.assertStringEqual(expected_request_one, con.sock.sent)
       
    87         self.assertEqual(con.sock.closed, False)
       
    88         self.assertNotEqual(con.sock.data, [])
       
    89         self.assert_(con.busy())
       
    90         resp = con.getresponse()
       
    91         self.assertStringEqual(resp.read(), expected_response_body)
       
    92         self.failIf(con.busy())
       
    93         self.assertEqual(con.sock, None)
       
    94         self.assertEqual(resp.sock.data, [])
       
    95         self.assert_(resp.sock.closed)
       
    96 
       
    97     def test_multiline_header(self):
       
    98         con = http.HTTPConnection('1.2.3.4:80')
       
    99         con._connect()
       
   100         con.sock.data = ['HTTP/1.1 200 OK\r\n',
       
   101                          'Server: BogusServer 1.0\r\n',
       
   102                          'Multiline: Value\r\n',
       
   103                          '  Rest of value\r\n',
       
   104                          'Content-Length: 10\r\n',
       
   105                          '\r\n'
       
   106                          '1234567890'
       
   107                          ]
       
   108         con.request('GET', '/')
       
   109 
       
   110         expected_req = ('GET / HTTP/1.1\r\n'
       
   111                         'Host: 1.2.3.4\r\n'
       
   112                         'accept-encoding: identity\r\n\r\n')
       
   113 
       
   114         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   115         self.assertEqual(expected_req, con.sock.sent)
       
   116         resp = con.getresponse()
       
   117         self.assertEqual('1234567890', resp.read())
       
   118         self.assertEqual(['Value\n Rest of value'],
       
   119                          resp.headers.getheaders('multiline'))
       
   120         # Socket should not be closed
       
   121         self.assertEqual(resp.sock.closed, False)
       
   122         self.assertEqual(con.sock.closed, False)
       
   123 
       
   124     def testSimpleRequest(self):
       
   125         con = http.HTTPConnection('1.2.3.4:80')
       
   126         con._connect()
       
   127         con.sock.data = ['HTTP/1.1 200 OK\r\n',
       
   128                          'Server: BogusServer 1.0\r\n',
       
   129                          'MultiHeader: Value\r\n'
       
   130                          'MultiHeader: Other Value\r\n'
       
   131                          'MultiHeader: One More!\r\n'
       
   132                          'Content-Length: 10\r\n',
       
   133                          '\r\n'
       
   134                          '1234567890'
       
   135                          ]
       
   136         con.request('GET', '/')
       
   137 
       
   138         expected_req = ('GET / HTTP/1.1\r\n'
       
   139                         'Host: 1.2.3.4\r\n'
       
   140                         'accept-encoding: identity\r\n\r\n')
       
   141 
       
   142         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   143         self.assertEqual(expected_req, con.sock.sent)
       
   144         resp = con.getresponse()
       
   145         self.assertEqual('1234567890', resp.read())
       
   146         self.assertEqual(['Value', 'Other Value', 'One More!'],
       
   147                          resp.headers.getheaders('multiheader'))
       
   148         self.assertEqual(['BogusServer 1.0'],
       
   149                          resp.headers.getheaders('server'))
       
   150 
       
   151     def testHeaderlessResponse(self):
       
   152         con = http.HTTPConnection('1.2.3.4', use_ssl=False)
       
   153         con._connect()
       
   154         con.sock.data = ['HTTP/1.1 200 OK\r\n',
       
   155                          '\r\n'
       
   156                          '1234567890'
       
   157                          ]
       
   158         con.request('GET', '/')
       
   159 
       
   160         expected_req = ('GET / HTTP/1.1\r\n'
       
   161                         'Host: 1.2.3.4\r\n'
       
   162                         'accept-encoding: identity\r\n\r\n')
       
   163 
       
   164         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   165         self.assertEqual(expected_req, con.sock.sent)
       
   166         resp = con.getresponse()
       
   167         self.assertEqual('1234567890', resp.read())
       
   168         self.assertEqual({}, dict(resp.headers))
       
   169         self.assertEqual(resp.status, 200)
       
   170 
       
   171     def testReadline(self):
       
   172         con = http.HTTPConnection('1.2.3.4')
       
   173         con._connect()
       
   174         # make sure it trickles in one byte at a time
       
   175         # so that we touch all the cases in readline
       
   176         con.sock.data = list(''.join(
       
   177             ['HTTP/1.1 200 OK\r\n',
       
   178              'Server: BogusServer 1.0\r\n',
       
   179              'Connection: Close\r\n',
       
   180              '\r\n'
       
   181              '1\n2\nabcdefg\n4\n5']))
       
   182 
       
   183         expected_req = ('GET / HTTP/1.1\r\n'
       
   184                         'Host: 1.2.3.4\r\n'
       
   185                         'accept-encoding: identity\r\n\r\n')
       
   186 
       
   187         con.request('GET', '/')
       
   188         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   189         self.assertEqual(expected_req, con.sock.sent)
       
   190         r = con.getresponse()
       
   191         for expected in ['1\n', '2\n', 'abcdefg\n', '4\n', '5']:
       
   192             actual = r.readline()
       
   193             self.assertEqual(expected, actual,
       
   194                              'Expected %r, got %r' % (expected, actual))
       
   195 
       
   196     def testIPv6(self):
       
   197         self._run_simple_test('[::1]:8221',
       
   198                         ['HTTP/1.1 200 OK\r\n',
       
   199                          'Server: BogusServer 1.0\r\n',
       
   200                          'Content-Length: 10',
       
   201                          '\r\n\r\n'
       
   202                          '1234567890'],
       
   203                         ('GET / HTTP/1.1\r\n'
       
   204                          'Host: [::1]:8221\r\n'
       
   205                          'accept-encoding: identity\r\n\r\n'),
       
   206                         '1234567890')
       
   207         self._run_simple_test('::2',
       
   208                         ['HTTP/1.1 200 OK\r\n',
       
   209                          'Server: BogusServer 1.0\r\n',
       
   210                          'Content-Length: 10',
       
   211                          '\r\n\r\n'
       
   212                          '1234567890'],
       
   213                         ('GET / HTTP/1.1\r\n'
       
   214                          'Host: ::2\r\n'
       
   215                          'accept-encoding: identity\r\n\r\n'),
       
   216                         '1234567890')
       
   217         self._run_simple_test('[::3]:443',
       
   218                         ['HTTP/1.1 200 OK\r\n',
       
   219                          'Server: BogusServer 1.0\r\n',
       
   220                          'Content-Length: 10',
       
   221                          '\r\n\r\n'
       
   222                          '1234567890'],
       
   223                         ('GET / HTTP/1.1\r\n'
       
   224                          'Host: ::3\r\n'
       
   225                          'accept-encoding: identity\r\n\r\n'),
       
   226                         '1234567890')
       
   227 
       
   228     def testEarlyContinueResponse(self):
       
   229         con = http.HTTPConnection('1.2.3.4:80')
       
   230         con._connect()
       
   231         sock = con.sock
       
   232         sock.data = ['HTTP/1.1 403 Forbidden\r\n',
       
   233                          'Server: BogusServer 1.0\r\n',
       
   234                          'Content-Length: 18',
       
   235                          '\r\n\r\n'
       
   236                          "You can't do that."]
       
   237         expected_req = self.doPost(con, expect_body=False)
       
   238         self.assertEqual(('1.2.3.4', 80), sock.sa)
       
   239         self.assertStringEqual(expected_req, sock.sent)
       
   240         self.assertEqual("You can't do that.", con.getresponse().read())
       
   241         self.assertEqual(sock.closed, True)
       
   242 
       
   243     def testDeniedAfterContinueTimeoutExpires(self):
       
   244         con = http.HTTPConnection('1.2.3.4:80')
       
   245         con._connect()
       
   246         sock = con.sock
       
   247         sock.data = ['HTTP/1.1 403 Forbidden\r\n',
       
   248                      'Server: BogusServer 1.0\r\n',
       
   249                      'Content-Length: 18\r\n',
       
   250                      'Connection: close',
       
   251                      '\r\n\r\n'
       
   252                      "You can't do that."]
       
   253         sock.read_wait_sentinel = 'Dear server, send response!'
       
   254         sock.close_on_empty = True
       
   255         # send enough data out that we'll chunk it into multiple
       
   256         # blocks and the socket will close before we can send the
       
   257         # whole request.
       
   258         post_body = ('This is some POST data\n' * 1024 * 32 +
       
   259                      'Dear server, send response!\n' +
       
   260                      'This is some POST data\n' * 1024 * 32)
       
   261         expected_req = self.doPost(con, expect_body=False,
       
   262                                    body_to_send=post_body)
       
   263         self.assertEqual(('1.2.3.4', 80), sock.sa)
       
   264         self.assert_('POST data\n' in sock.sent)
       
   265         self.assert_('Dear server, send response!\n' in sock.sent)
       
   266         # We expect not all of our data was sent.
       
   267         self.assertNotEqual(sock.sent, expected_req)
       
   268         self.assertEqual("You can't do that.", con.getresponse().read())
       
   269         self.assertEqual(sock.closed, True)
       
   270 
       
   271     def testPostData(self):
       
   272         con = http.HTTPConnection('1.2.3.4:80')
       
   273         con._connect()
       
   274         sock = con.sock
       
   275         sock.read_wait_sentinel = 'POST data'
       
   276         sock.early_data = ['HTTP/1.1 100 Co', 'ntinue\r\n\r\n']
       
   277         sock.data = ['HTTP/1.1 200 OK\r\n',
       
   278                      'Server: BogusServer 1.0\r\n',
       
   279                      'Content-Length: 16',
       
   280                      '\r\n\r\n',
       
   281                      "You can do that."]
       
   282         expected_req = self.doPost(con, expect_body=True)
       
   283         self.assertEqual(('1.2.3.4', 80), sock.sa)
       
   284         self.assertEqual(expected_req, sock.sent)
       
   285         self.assertEqual("You can do that.", con.getresponse().read())
       
   286         self.assertEqual(sock.closed, False)
       
   287 
       
   288     def testServerWithoutContinue(self):
       
   289         con = http.HTTPConnection('1.2.3.4:80')
       
   290         con._connect()
       
   291         sock = con.sock
       
   292         sock.read_wait_sentinel = 'POST data'
       
   293         sock.data = ['HTTP/1.1 200 OK\r\n',
       
   294                      'Server: BogusServer 1.0\r\n',
       
   295                      'Content-Length: 16',
       
   296                      '\r\n\r\n',
       
   297                      "You can do that."]
       
   298         expected_req = self.doPost(con, expect_body=True)
       
   299         self.assertEqual(('1.2.3.4', 80), sock.sa)
       
   300         self.assertEqual(expected_req, sock.sent)
       
   301         self.assertEqual("You can do that.", con.getresponse().read())
       
   302         self.assertEqual(sock.closed, False)
       
   303 
       
   304     def testServerWithSlowContinue(self):
       
   305         con = http.HTTPConnection('1.2.3.4:80')
       
   306         con._connect()
       
   307         sock = con.sock
       
   308         sock.read_wait_sentinel = 'POST data'
       
   309         sock.data = ['HTTP/1.1 100 ', 'Continue\r\n\r\n',
       
   310                      'HTTP/1.1 200 OK\r\n',
       
   311                      'Server: BogusServer 1.0\r\n',
       
   312                      'Content-Length: 16',
       
   313                      '\r\n\r\n',
       
   314                      "You can do that."]
       
   315         expected_req = self.doPost(con, expect_body=True)
       
   316         self.assertEqual(('1.2.3.4', 80), sock.sa)
       
   317         self.assertEqual(expected_req, sock.sent)
       
   318         resp = con.getresponse()
       
   319         self.assertEqual("You can do that.", resp.read())
       
   320         self.assertEqual(200, resp.status)
       
   321         self.assertEqual(sock.closed, False)
       
   322 
       
   323     def testSlowConnection(self):
       
   324         con = http.HTTPConnection('1.2.3.4:80')
       
   325         con._connect()
       
   326         # simulate one byte arriving at a time, to check for various
       
   327         # corner cases
       
   328         con.sock.data = list('HTTP/1.1 200 OK\r\n'
       
   329                              'Server: BogusServer 1.0\r\n'
       
   330                              'Content-Length: 10'
       
   331                              '\r\n\r\n'
       
   332                              '1234567890')
       
   333         con.request('GET', '/')
       
   334 
       
   335         expected_req = ('GET / HTTP/1.1\r\n'
       
   336                         'Host: 1.2.3.4\r\n'
       
   337                         'accept-encoding: identity\r\n\r\n')
       
   338 
       
   339         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   340         self.assertEqual(expected_req, con.sock.sent)
       
   341         self.assertEqual('1234567890', con.getresponse().read())
       
   342 
       
   343     def testTimeout(self):
       
   344         con = http.HTTPConnection('1.2.3.4:80')
       
   345         con._connect()
       
   346         con.sock.data = []
       
   347         con.request('GET', '/')
       
   348         self.assertRaises(http.HTTPTimeoutException,
       
   349                           con.getresponse)
       
   350 
       
   351         expected_req = ('GET / HTTP/1.1\r\n'
       
   352                         'Host: 1.2.3.4\r\n'
       
   353                         'accept-encoding: identity\r\n\r\n')
       
   354 
       
   355         self.assertEqual(('1.2.3.4', 80), con.sock.sa)
       
   356         self.assertEqual(expected_req, con.sock.sent)
       
   357 
       
   358     def test_conn_keep_alive_but_server_close_anyway(self):
       
   359         sockets = []
       
   360         def closingsocket(*args, **kwargs):
       
   361             s = util.MockSocket(*args, **kwargs)
       
   362             sockets.append(s)
       
   363             s.data = ['HTTP/1.1 200 OK\r\n',
       
   364                       'Server: BogusServer 1.0\r\n',
       
   365                       'Connection: Keep-Alive\r\n',
       
   366                       'Content-Length: 16',
       
   367                       '\r\n\r\n',
       
   368                       'You can do that.']
       
   369             s.close_on_empty = True
       
   370             return s
       
   371 
       
   372         socket.socket = closingsocket
       
   373         con = http.HTTPConnection('1.2.3.4:80')
       
   374         con._connect()
       
   375         con.request('GET', '/')
       
   376         r1 = con.getresponse()
       
   377         r1.read()
       
   378         self.assertFalse(con.sock.closed)
       
   379         self.assert_(con.sock.remote_closed)
       
   380         con.request('GET', '/')
       
   381         self.assertEqual(2, len(sockets))
       
   382 
       
   383     def test_server_closes_before_end_of_body(self):
       
   384         con = http.HTTPConnection('1.2.3.4:80')
       
   385         con._connect()
       
   386         s = con.sock
       
   387         s.data = ['HTTP/1.1 200 OK\r\n',
       
   388                   'Server: BogusServer 1.0\r\n',
       
   389                   'Connection: Keep-Alive\r\n',
       
   390                   'Content-Length: 16',
       
   391                   '\r\n\r\n',
       
   392                   'You can '] # Note: this is shorter than content-length
       
   393         s.close_on_empty = True
       
   394         con.request('GET', '/')
       
   395         r1 = con.getresponse()
       
   396         self.assertRaises(http.HTTPRemoteClosedError, r1.read)
       
   397 
       
   398     def test_no_response_raises_response_not_ready(self):
       
   399         con = http.HTTPConnection('foo')
       
   400         self.assertRaises(http.httplib.ResponseNotReady, con.getresponse)
       
   401 # no-check-code