mercurial/keepalive.py
changeset 28883 032c4c2f802a
parent 28278 b1b22185c764
child 29341 0d83ad967bf8
equal deleted inserted replaced
28882:800ec7c048b0 28883:032c4c2f802a
    26 """An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive.
    26 """An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive.
    27 
    27 
    28 >>> import urllib2
    28 >>> import urllib2
    29 >>> from keepalive import HTTPHandler
    29 >>> from keepalive import HTTPHandler
    30 >>> keepalive_handler = HTTPHandler()
    30 >>> keepalive_handler = HTTPHandler()
    31 >>> opener = urllib2.build_opener(keepalive_handler)
    31 >>> opener = urlreq.buildopener(keepalive_handler)
    32 >>> urllib2.install_opener(opener)
    32 >>> urlreq.installopener(opener)
    33 >>>
    33 >>>
    34 >>> fo = urllib2.urlopen('http://www.python.org')
    34 >>> fo = urlreq.urlopen('http://www.python.org')
    35 
    35 
    36 If a connection to a given host is requested, and all of the existing
    36 If a connection to a given host is requested, and all of the existing
    37 connections are still in use, another connection will be opened.  If
    37 connections are still in use, another connection will be opened.  If
    38 the handler tries to use an existing connection but it fails in some
    38 the handler tries to use an existing connection but it fails in some
    39 way, it will be closed and removed from the pool.
    39 way, it will be closed and removed from the pool.
   112 import errno
   112 import errno
   113 import httplib
   113 import httplib
   114 import socket
   114 import socket
   115 import sys
   115 import sys
   116 import thread
   116 import thread
   117 import urllib2
   117 
       
   118 from . import (
       
   119     util,
       
   120 )
       
   121 
       
   122 urlerr = util.urlerr
       
   123 urlreq = util.urlreq
   118 
   124 
   119 DEBUG = None
   125 DEBUG = None
   120 
   126 
   121 if sys.version_info < (2, 4):
   127 if sys.version_info < (2, 4):
   122     HANDLE_ERRORS = 1
   128     HANDLE_ERRORS = 1
   225         return self.do_open(HTTPConnection, req)
   231         return self.do_open(HTTPConnection, req)
   226 
   232 
   227     def do_open(self, http_class, req):
   233     def do_open(self, http_class, req):
   228         host = req.get_host()
   234         host = req.get_host()
   229         if not host:
   235         if not host:
   230             raise urllib2.URLError('no host given')
   236             raise urlerr.urlerror('no host given')
   231 
   237 
   232         try:
   238         try:
   233             h = self._cm.get_ready_conn(host)
   239             h = self._cm.get_ready_conn(host)
   234             while h:
   240             while h:
   235                 r = self._reuse_connection(h, req, host)
   241                 r = self._reuse_connection(h, req, host)
   252                                host, id(h))
   258                                host, id(h))
   253                 self._cm.add(host, h, 0)
   259                 self._cm.add(host, h, 0)
   254                 self._start_transaction(h, req)
   260                 self._start_transaction(h, req)
   255                 r = h.getresponse()
   261                 r = h.getresponse()
   256         except (socket.error, httplib.HTTPException) as err:
   262         except (socket.error, httplib.HTTPException) as err:
   257             raise urllib2.URLError(err)
   263             raise urlerr.urlerror(err)
   258 
   264 
   259         # if not a persistent connection, don't try to reuse it
   265         # if not a persistent connection, don't try to reuse it
   260         if r.will_close:
   266         if r.will_close:
   261             self._cm.remove(h)
   267             self._cm.remove(h)
   262 
   268 
   344                 if 'content-length' not in headers:
   350                 if 'content-length' not in headers:
   345                     h.putheader('Content-length', '%d' % len(data))
   351                     h.putheader('Content-length', '%d' % len(data))
   346             else:
   352             else:
   347                 h.putrequest('GET', req.get_selector(), **skipheaders)
   353                 h.putrequest('GET', req.get_selector(), **skipheaders)
   348         except socket.error as err:
   354         except socket.error as err:
   349             raise urllib2.URLError(err)
   355             raise urlerr.urlerror(err)
   350         for k, v in headers.items():
   356         for k, v in headers.items():
   351             h.putheader(k, v)
   357             h.putheader(k, v)
   352         h.endheaders()
   358         h.endheaders()
   353         if req.has_data():
   359         if req.has_data():
   354             h.send(data)
   360             h.send(data)
   355 
   361 
   356 class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler):
   362 class HTTPHandler(KeepAliveHandler, urlreq.httphandler):
   357     pass
   363     pass
   358 
   364 
   359 class HTTPResponse(httplib.HTTPResponse):
   365 class HTTPResponse(httplib.HTTPResponse):
   360     # we need to subclass HTTPResponse in order to
   366     # we need to subclass HTTPResponse in order to
   361     # 1) add readline() and readlines() methods
   367     # 1) add readline() and readlines() methods
   591 
   597 
   592 def error_handler(url):
   598 def error_handler(url):
   593     global HANDLE_ERRORS
   599     global HANDLE_ERRORS
   594     orig = HANDLE_ERRORS
   600     orig = HANDLE_ERRORS
   595     keepalive_handler = HTTPHandler()
   601     keepalive_handler = HTTPHandler()
   596     opener = urllib2.build_opener(keepalive_handler)
   602     opener = urlreq.buildopener(keepalive_handler)
   597     urllib2.install_opener(opener)
   603     urlreq.installopener(opener)
   598     pos = {0: 'off', 1: 'on'}
   604     pos = {0: 'off', 1: 'on'}
   599     for i in (0, 1):
   605     for i in (0, 1):
   600         print("  fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i))
   606         print("  fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i))
   601         HANDLE_ERRORS = i
   607         HANDLE_ERRORS = i
   602         try:
   608         try:
   603             fo = urllib2.urlopen(url)
   609             fo = urlreq.urlopen(url)
   604             fo.read()
   610             fo.read()
   605             fo.close()
   611             fo.close()
   606             try:
   612             try:
   607                 status, reason = fo.status, fo.reason
   613                 status, reason = fo.status, fo.reason
   608             except AttributeError:
   614             except AttributeError:
   621     from . import util
   627     from . import util
   622     md5 = util.md5
   628     md5 = util.md5
   623     format = '%25s: %s'
   629     format = '%25s: %s'
   624 
   630 
   625     # first fetch the file with the normal http handler
   631     # first fetch the file with the normal http handler
   626     opener = urllib2.build_opener()
   632     opener = urlreq.buildopener()
   627     urllib2.install_opener(opener)
   633     urlreq.installopener(opener)
   628     fo = urllib2.urlopen(url)
   634     fo = urlreq.urlopen(url)
   629     foo = fo.read()
   635     foo = fo.read()
   630     fo.close()
   636     fo.close()
   631     m = md5(foo)
   637     m = md5(foo)
   632     print(format % ('normal urllib', m.hexdigest()))
   638     print(format % ('normal urllib', m.hexdigest()))
   633 
   639 
   634     # now install the keepalive handler and try again
   640     # now install the keepalive handler and try again
   635     opener = urllib2.build_opener(HTTPHandler())
   641     opener = urlreq.buildopener(HTTPHandler())
   636     urllib2.install_opener(opener)
   642     urlreq.installopener(opener)
   637 
   643 
   638     fo = urllib2.urlopen(url)
   644     fo = urlreq.urlopen(url)
   639     foo = fo.read()
   645     foo = fo.read()
   640     fo.close()
   646     fo.close()
   641     m = md5(foo)
   647     m = md5(foo)
   642     print(format % ('keepalive read', m.hexdigest()))
   648     print(format % ('keepalive read', m.hexdigest()))
   643 
   649 
   644     fo = urllib2.urlopen(url)
   650     fo = urlreq.urlopen(url)
   645     foo = ''
   651     foo = ''
   646     while True:
   652     while True:
   647         f = fo.readline()
   653         f = fo.readline()
   648         if f:
   654         if f:
   649             foo = foo + f
   655             foo = foo + f
   655 def comp(N, url):
   661 def comp(N, url):
   656     print('  making %i connections to:\n  %s' % (N, url))
   662     print('  making %i connections to:\n  %s' % (N, url))
   657 
   663 
   658     sys.stdout.write('  first using the normal urllib handlers')
   664     sys.stdout.write('  first using the normal urllib handlers')
   659     # first use normal opener
   665     # first use normal opener
   660     opener = urllib2.build_opener()
   666     opener = urlreq.buildopener()
   661     urllib2.install_opener(opener)
   667     urlreq.installopener(opener)
   662     t1 = fetch(N, url)
   668     t1 = fetch(N, url)
   663     print('  TIME: %.3f s' % t1)
   669     print('  TIME: %.3f s' % t1)
   664 
   670 
   665     sys.stdout.write('  now using the keepalive handler       ')
   671     sys.stdout.write('  now using the keepalive handler       ')
   666     # now install the keepalive handler and try again
   672     # now install the keepalive handler and try again
   667     opener = urllib2.build_opener(HTTPHandler())
   673     opener = urlreq.buildopener(HTTPHandler())
   668     urllib2.install_opener(opener)
   674     urlreq.installopener(opener)
   669     t2 = fetch(N, url)
   675     t2 = fetch(N, url)
   670     print('  TIME: %.3f s' % t2)
   676     print('  TIME: %.3f s' % t2)
   671     print('  improvement factor: %.2f' % (t1 / t2))
   677     print('  improvement factor: %.2f' % (t1 / t2))
   672 
   678 
   673 def fetch(N, url, delay=0):
   679 def fetch(N, url, delay=0):
   675     lens = []
   681     lens = []
   676     starttime = time.time()
   682     starttime = time.time()
   677     for i in range(N):
   683     for i in range(N):
   678         if delay and i > 0:
   684         if delay and i > 0:
   679             time.sleep(delay)
   685             time.sleep(delay)
   680         fo = urllib2.urlopen(url)
   686         fo = urlreq.urlopen(url)
   681         foo = fo.read()
   687         foo = fo.read()
   682         fo.close()
   688         fo.close()
   683         lens.append(len(foo))
   689         lens.append(len(foo))
   684     diff = time.time() - starttime
   690     diff = time.time() - starttime
   685 
   691 
   698         def debug(self, msg, *args):
   704         def debug(self, msg, *args):
   699             print(msg % args)
   705             print(msg % args)
   700         info = warning = error = debug
   706         info = warning = error = debug
   701     DEBUG = FakeLogger()
   707     DEBUG = FakeLogger()
   702     print("  fetching the file to establish a connection")
   708     print("  fetching the file to establish a connection")
   703     fo = urllib2.urlopen(url)
   709     fo = urlreq.urlopen(url)
   704     data1 = fo.read()
   710     data1 = fo.read()
   705     fo.close()
   711     fo.close()
   706 
   712 
   707     i = 20
   713     i = 20
   708     print("  waiting %i seconds for the server to close the connection" % i)
   714     print("  waiting %i seconds for the server to close the connection" % i)
   712         time.sleep(1)
   718         time.sleep(1)
   713         i -= 1
   719         i -= 1
   714     sys.stderr.write('\r')
   720     sys.stderr.write('\r')
   715 
   721 
   716     print("  fetching the file a second time")
   722     print("  fetching the file a second time")
   717     fo = urllib2.urlopen(url)
   723     fo = urlreq.urlopen(url)
   718     data2 = fo.read()
   724     data2 = fo.read()
   719     fo.close()
   725     fo.close()
   720 
   726 
   721     if data1 == data2:
   727     if data1 == data2:
   722         print('  data are identical')
   728         print('  data are identical')