mercurial/url.py
changeset 8590 59acb9c7d90f
parent 8344 873429914ec5
child 8847 7951f385fcb7
equal deleted inserted replaced
8589:3edf133dcb5a 8590:59acb9c7d90f
     5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
     5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
     6 #
     6 #
     7 # This software may be used and distributed according to the terms of the
     7 # This software may be used and distributed according to the terms of the
     8 # GNU General Public License version 2, incorporated herein by reference.
     8 # GNU General Public License version 2, incorporated herein by reference.
     9 
     9 
    10 import urllib, urllib2, urlparse, httplib, os, re
    10 import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO
    11 from i18n import _
    11 from i18n import _
    12 import keepalive, util
    12 import keepalive, util
    13 
    13 
    14 def hidepassword(url):
    14 def hidepassword(url):
    15     '''hide user credential in a url string'''
    15     '''hide user credential in a url string'''
   243                 connection.send(self, chunk)
   243                 connection.send(self, chunk)
   244         else:
   244         else:
   245             connection.send(self, data)
   245             connection.send(self, data)
   246     return _sendfile
   246     return _sendfile
   247 
   247 
       
   248 has_https = hasattr(urllib2, 'HTTPSHandler')
       
   249 if has_https:
       
   250     try:
       
   251         # avoid using deprecated/broken FakeSocket in python 2.6
       
   252         import ssl
       
   253         _ssl_wrap_socket = ssl.wrap_socket
       
   254     except ImportError:
       
   255         def _ssl_wrap_socket(sock, key_file, cert_file):
       
   256             ssl = socket.ssl(sock, key_file, cert_file)
       
   257             return httplib.FakeSocket(sock, ssl)
       
   258 
   248 class httpconnection(keepalive.HTTPConnection):
   259 class httpconnection(keepalive.HTTPConnection):
   249     # must be able to send big bundle as stream.
   260     # must be able to send big bundle as stream.
   250     send = _gen_sendfile(keepalive.HTTPConnection)
   261     send = _gen_sendfile(keepalive.HTTPConnection)
   251 
   262 
       
   263     def _proxytunnel(self):
       
   264         proxyheaders = dict(
       
   265                 [(x, self.headers[x]) for x in self.headers
       
   266                  if x.lower().startswith('proxy-')])
       
   267         self._set_hostport(self.host, self.port)
       
   268         self.send('CONNECT %s:%d HTTP/1.0\r\n' % (self.realhost, self.realport))
       
   269         for header in proxyheaders.iteritems():
       
   270             self.send('%s: %s\r\n' % header)
       
   271         self.send('\r\n')
       
   272 
       
   273         # majority of the following code is duplicated from
       
   274         # httplib.HTTPConnection as there are no adequate places to
       
   275         # override functions to provide the needed functionality
       
   276         res = self.response_class(self.sock,
       
   277                                   strict=self.strict,
       
   278                                   method=self._method)
       
   279 
       
   280         while True:
       
   281             version, status, reason = res._read_status()
       
   282             if status != httplib.CONTINUE:
       
   283                 break
       
   284             while True:
       
   285                 skip = res.fp.readline().strip()
       
   286                 if not skip:
       
   287                     break
       
   288         res.status = status
       
   289         res.reason = reason.strip()
       
   290 
       
   291         if res.status == 200:
       
   292             while True:
       
   293                 line = res.fp.readline()
       
   294                 if line == '\r\n':
       
   295                     break
       
   296             return True
       
   297 
       
   298         if version == 'HTTP/1.0':
       
   299             res.version = 10
       
   300         elif version.startswith('HTTP/1.'):
       
   301             res.version = 11
       
   302         elif version == 'HTTP/0.9':
       
   303             res.version = 9
       
   304         else:
       
   305             raise httplib.UnknownProtocol(version)
       
   306 
       
   307         if res.version == 9:
       
   308             res.length = None
       
   309             res.chunked = 0
       
   310             res.will_close = 1
       
   311             res.msg = httplib.HTTPMessage(cStringIO.StringIO())
       
   312             return False
       
   313 
       
   314         res.msg = httplib.HTTPMessage(res.fp)
       
   315         res.msg.fp = None
       
   316 
       
   317         # are we using the chunked-style of transfer encoding?
       
   318         trenc = res.msg.getheader('transfer-encoding')
       
   319         if trenc and trenc.lower() == "chunked":
       
   320             res.chunked = 1
       
   321             res.chunk_left = None
       
   322         else:
       
   323             res.chunked = 0
       
   324 
       
   325         # will the connection close at the end of the response?
       
   326         res.will_close = res._check_close()
       
   327 
       
   328         # do we have a Content-Length?
       
   329         # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
       
   330         length = res.msg.getheader('content-length')
       
   331         if length and not res.chunked:
       
   332             try:
       
   333                 res.length = int(length)
       
   334             except ValueError:
       
   335                 res.length = None
       
   336             else:
       
   337                 if res.length < 0:  # ignore nonsensical negative lengths
       
   338                     res.length = None
       
   339         else:
       
   340             res.length = None
       
   341 
       
   342         # does the body have a fixed length? (of zero)
       
   343         if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
       
   344             100 <= status < 200 or # 1xx codes
       
   345             res._method == 'HEAD'):
       
   346             res.length = 0
       
   347 
       
   348         # if the connection remains open, and we aren't using chunked, and
       
   349         # a content-length was not provided, then assume that the connection
       
   350         # WILL close.
       
   351         if (not res.will_close and
       
   352            not res.chunked and
       
   353            res.length is None):
       
   354             res.will_close = 1
       
   355 
       
   356         self.proxyres = res
       
   357 
       
   358         return False
       
   359 
       
   360     def connect(self):
       
   361         if has_https and self.realhost: # use CONNECT proxy
       
   362             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       
   363             self.sock.connect((self.host, self.port))
       
   364             if self._proxytunnel():
       
   365                 # we do not support client x509 certificates
       
   366                 self.sock = _ssl_wrap_socket(self.sock, None, None)
       
   367         else:
       
   368             keepalive.HTTPConnection.connect(self)
       
   369 
       
   370     def getresponse(self):
       
   371         proxyres = getattr(self, 'proxyres', None)
       
   372         if proxyres:
       
   373             if proxyres.will_close:
       
   374                 self.close()
       
   375             self.proxyres = None
       
   376             return proxyres
       
   377         return keepalive.HTTPConnection.getresponse(self)
       
   378 
   252 class httphandler(keepalive.HTTPHandler):
   379 class httphandler(keepalive.HTTPHandler):
   253     def http_open(self, req):
   380     def http_open(self, req):
   254         return self.do_open(httpconnection, req)
   381         return self.do_open(httpconnection, req)
   255 
   382 
       
   383     def _start_transaction(self, h, req):
       
   384         if req.get_selector() == req.get_full_url(): # has proxy
       
   385             urlparts = urlparse.urlparse(req.get_selector())
       
   386             if urlparts[0] == 'https': # only use CONNECT for HTTPS
       
   387                 if ':' in urlparts[1]:
       
   388                     realhost, realport = urlparts[1].split(':')
       
   389                 else:
       
   390                     realhost = urlparts[1]
       
   391                     realport = 443
       
   392 
       
   393                 h.realhost = realhost
       
   394                 h.realport = realport
       
   395                 h.headers = req.headers.copy()
       
   396                 h.headers.update(self.parent.addheaders)
       
   397                 return keepalive.HTTPHandler._start_transaction(self, h, req)
       
   398 
       
   399         h.realhost = None
       
   400         h.realport = None
       
   401         h.headers = None
       
   402         return keepalive.HTTPHandler._start_transaction(self, h, req)
       
   403 
   256     def __del__(self):
   404     def __del__(self):
   257         self.close_all()
   405         self.close_all()
   258 
   406 
   259 has_https = hasattr(urllib2, 'HTTPSHandler')
       
   260 if has_https:
   407 if has_https:
   261     class httpsconnection(httplib.HTTPSConnection):
   408     class httpsconnection(httplib.HTTPSConnection):
   262         response_class = keepalive.HTTPResponse
   409         response_class = keepalive.HTTPResponse
   263         # must be able to send big bundle as stream.
   410         # must be able to send big bundle as stream.
   264         send = _gen_sendfile(httplib.HTTPSConnection)
   411         send = _gen_sendfile(httplib.HTTPSConnection)