mercurial/url.py
changeset 9852 917cf6bb6d0c
parent 9726 430e59ff3437
child 10263 25e572394f5c
equal deleted inserted replaced
9850:004bf1d6e6af 9852:917cf6bb6d0c
   260 
   260 
   261 class httpconnection(keepalive.HTTPConnection):
   261 class httpconnection(keepalive.HTTPConnection):
   262     # must be able to send big bundle as stream.
   262     # must be able to send big bundle as stream.
   263     send = _gen_sendfile(keepalive.HTTPConnection)
   263     send = _gen_sendfile(keepalive.HTTPConnection)
   264 
   264 
   265     def _proxytunnel(self):
       
   266         proxyheaders = dict(
       
   267                 [(x, self.headers[x]) for x in self.headers
       
   268                  if x.lower().startswith('proxy-')])
       
   269         self._set_hostport(self.host, self.port)
       
   270         self.send('CONNECT %s:%d HTTP/1.0\r\n' % (self.realhost, self.realport))
       
   271         for header in proxyheaders.iteritems():
       
   272             self.send('%s: %s\r\n' % header)
       
   273         self.send('\r\n')
       
   274 
       
   275         # majority of the following code is duplicated from
       
   276         # httplib.HTTPConnection as there are no adequate places to
       
   277         # override functions to provide the needed functionality
       
   278         res = self.response_class(self.sock,
       
   279                                   strict=self.strict,
       
   280                                   method=self._method)
       
   281 
       
   282         while True:
       
   283             version, status, reason = res._read_status()
       
   284             if status != httplib.CONTINUE:
       
   285                 break
       
   286             while True:
       
   287                 skip = res.fp.readline().strip()
       
   288                 if not skip:
       
   289                     break
       
   290         res.status = status
       
   291         res.reason = reason.strip()
       
   292 
       
   293         if res.status == 200:
       
   294             while True:
       
   295                 line = res.fp.readline()
       
   296                 if line == '\r\n':
       
   297                     break
       
   298             return True
       
   299 
       
   300         if version == 'HTTP/1.0':
       
   301             res.version = 10
       
   302         elif version.startswith('HTTP/1.'):
       
   303             res.version = 11
       
   304         elif version == 'HTTP/0.9':
       
   305             res.version = 9
       
   306         else:
       
   307             raise httplib.UnknownProtocol(version)
       
   308 
       
   309         if res.version == 9:
       
   310             res.length = None
       
   311             res.chunked = 0
       
   312             res.will_close = 1
       
   313             res.msg = httplib.HTTPMessage(cStringIO.StringIO())
       
   314             return False
       
   315 
       
   316         res.msg = httplib.HTTPMessage(res.fp)
       
   317         res.msg.fp = None
       
   318 
       
   319         # are we using the chunked-style of transfer encoding?
       
   320         trenc = res.msg.getheader('transfer-encoding')
       
   321         if trenc and trenc.lower() == "chunked":
       
   322             res.chunked = 1
       
   323             res.chunk_left = None
       
   324         else:
       
   325             res.chunked = 0
       
   326 
       
   327         # will the connection close at the end of the response?
       
   328         res.will_close = res._check_close()
       
   329 
       
   330         # do we have a Content-Length?
       
   331         # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
       
   332         length = res.msg.getheader('content-length')
       
   333         if length and not res.chunked:
       
   334             try:
       
   335                 res.length = int(length)
       
   336             except ValueError:
       
   337                 res.length = None
       
   338             else:
       
   339                 if res.length < 0:  # ignore nonsensical negative lengths
       
   340                     res.length = None
       
   341         else:
       
   342             res.length = None
       
   343 
       
   344         # does the body have a fixed length? (of zero)
       
   345         if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
       
   346             100 <= status < 200 or # 1xx codes
       
   347             res._method == 'HEAD'):
       
   348             res.length = 0
       
   349 
       
   350         # if the connection remains open, and we aren't using chunked, and
       
   351         # a content-length was not provided, then assume that the connection
       
   352         # WILL close.
       
   353         if (not res.will_close and
       
   354            not res.chunked and
       
   355            res.length is None):
       
   356             res.will_close = 1
       
   357 
       
   358         self.proxyres = res
       
   359 
       
   360         return False
       
   361 
       
   362     def connect(self):
   265     def connect(self):
   363         if has_https and self.realhost: # use CONNECT proxy
   266         if has_https and self.realhost: # use CONNECT proxy
   364             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   267             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   365             self.sock.connect((self.host, self.port))
   268             self.sock.connect((self.host, self.port))
   366             if self._proxytunnel():
   269             if _generic_proxytunnel(self):
   367                 # we do not support client x509 certificates
   270                 # we do not support client x509 certificates
   368                 self.sock = _ssl_wrap_socket(self.sock, None, None)
   271                 self.sock = _ssl_wrap_socket(self.sock, None, None)
   369         else:
   272         else:
   370             keepalive.HTTPConnection.connect(self)
   273             keepalive.HTTPConnection.connect(self)
   371 
   274 
   376                 self.close()
   279                 self.close()
   377             self.proxyres = None
   280             self.proxyres = None
   378             return proxyres
   281             return proxyres
   379         return keepalive.HTTPConnection.getresponse(self)
   282         return keepalive.HTTPConnection.getresponse(self)
   380 
   283 
       
   284 # general transaction handler to support different ways to handle
       
   285 # HTTPS proxying before and after Python 2.6.3.
       
   286 def _generic_start_transaction(handler, h, req):
       
   287     if hasattr(req, '_tunnel_host') and req._tunnel_host:
       
   288         tunnel_host = req._tunnel_host
       
   289         if tunnel_host[:7] not in ['http://', 'https:/']:
       
   290             tunnel_host = 'https://' + tunnel_host
       
   291         new_tunnel = True
       
   292     else:
       
   293         tunnel_host = req.get_selector()
       
   294         new_tunnel = False
       
   295 
       
   296     if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
       
   297         urlparts = urlparse.urlparse(tunnel_host)
       
   298         if new_tunnel or urlparts[0] == 'https': # only use CONNECT for HTTPS
       
   299             if ':' in urlparts[1]:
       
   300                 realhost, realport = urlparts[1].split(':')
       
   301                 realport = int(realport)
       
   302             else:
       
   303                 realhost = urlparts[1]
       
   304                 realport = 443
       
   305 
       
   306             h.realhost = realhost
       
   307             h.realport = realport
       
   308             h.headers = req.headers.copy()
       
   309             h.headers.update(handler.parent.addheaders)
       
   310             return
       
   311 
       
   312     h.realhost = None
       
   313     h.realport = None
       
   314     h.headers = None
       
   315 
       
   316 def _generic_proxytunnel(self):
       
   317     proxyheaders = dict(
       
   318             [(x, self.headers[x]) for x in self.headers
       
   319              if x.lower().startswith('proxy-')])
       
   320     self._set_hostport(self.host, self.port)
       
   321     self.send('CONNECT %s:%d HTTP/1.0\r\n' % (self.realhost, self.realport))
       
   322     for header in proxyheaders.iteritems():
       
   323         self.send('%s: %s\r\n' % header)
       
   324     self.send('\r\n')
       
   325 
       
   326     # majority of the following code is duplicated from
       
   327     # httplib.HTTPConnection as there are no adequate places to
       
   328     # override functions to provide the needed functionality
       
   329     res = self.response_class(self.sock,
       
   330                               strict=self.strict,
       
   331                               method=self._method)
       
   332 
       
   333     while True:
       
   334         version, status, reason = res._read_status()
       
   335         if status != httplib.CONTINUE:
       
   336             break
       
   337         while True:
       
   338             skip = res.fp.readline().strip()
       
   339             if not skip:
       
   340                 break
       
   341     res.status = status
       
   342     res.reason = reason.strip()
       
   343 
       
   344     if res.status == 200:
       
   345         while True:
       
   346             line = res.fp.readline()
       
   347             if line == '\r\n':
       
   348                 break
       
   349         return True
       
   350 
       
   351     if version == 'HTTP/1.0':
       
   352         res.version = 10
       
   353     elif version.startswith('HTTP/1.'):
       
   354         res.version = 11
       
   355     elif version == 'HTTP/0.9':
       
   356         res.version = 9
       
   357     else:
       
   358         raise httplib.UnknownProtocol(version)
       
   359 
       
   360     if res.version == 9:
       
   361         res.length = None
       
   362         res.chunked = 0
       
   363         res.will_close = 1
       
   364         res.msg = httplib.HTTPMessage(cStringIO.StringIO())
       
   365         return False
       
   366 
       
   367     res.msg = httplib.HTTPMessage(res.fp)
       
   368     res.msg.fp = None
       
   369 
       
   370     # are we using the chunked-style of transfer encoding?
       
   371     trenc = res.msg.getheader('transfer-encoding')
       
   372     if trenc and trenc.lower() == "chunked":
       
   373         res.chunked = 1
       
   374         res.chunk_left = None
       
   375     else:
       
   376         res.chunked = 0
       
   377 
       
   378     # will the connection close at the end of the response?
       
   379     res.will_close = res._check_close()
       
   380 
       
   381     # do we have a Content-Length?
       
   382     # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
       
   383     length = res.msg.getheader('content-length')
       
   384     if length and not res.chunked:
       
   385         try:
       
   386             res.length = int(length)
       
   387         except ValueError:
       
   388             res.length = None
       
   389         else:
       
   390             if res.length < 0:  # ignore nonsensical negative lengths
       
   391                 res.length = None
       
   392     else:
       
   393         res.length = None
       
   394 
       
   395     # does the body have a fixed length? (of zero)
       
   396     if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
       
   397         100 <= status < 200 or # 1xx codes
       
   398         res._method == 'HEAD'):
       
   399         res.length = 0
       
   400 
       
   401     # if the connection remains open, and we aren't using chunked, and
       
   402     # a content-length was not provided, then assume that the connection
       
   403     # WILL close.
       
   404     if (not res.will_close and
       
   405        not res.chunked and
       
   406        res.length is None):
       
   407         res.will_close = 1
       
   408 
       
   409     self.proxyres = res
       
   410 
       
   411     return False
       
   412 
   381 class httphandler(keepalive.HTTPHandler):
   413 class httphandler(keepalive.HTTPHandler):
   382     def http_open(self, req):
   414     def http_open(self, req):
   383         return self.do_open(httpconnection, req)
   415         return self.do_open(httpconnection, req)
   384 
   416 
   385     def _start_transaction(self, h, req):
   417     def _start_transaction(self, h, req):
   386         if req.get_selector() == req.get_full_url(): # has proxy
   418         _generic_start_transaction(self, h, req)
   387             urlparts = urlparse.urlparse(req.get_selector())
       
   388             if urlparts[0] == 'https': # only use CONNECT for HTTPS
       
   389                 if ':' in urlparts[1]:
       
   390                     realhost, realport = urlparts[1].split(':')
       
   391                     realport = int(realport)
       
   392                 else:
       
   393                     realhost = urlparts[1]
       
   394                     realport = 443
       
   395 
       
   396                 h.realhost = realhost
       
   397                 h.realport = realport
       
   398                 h.headers = req.headers.copy()
       
   399                 h.headers.update(self.parent.addheaders)
       
   400                 return keepalive.HTTPHandler._start_transaction(self, h, req)
       
   401 
       
   402         h.realhost = None
       
   403         h.realport = None
       
   404         h.headers = None
       
   405         return keepalive.HTTPHandler._start_transaction(self, h, req)
   419         return keepalive.HTTPHandler._start_transaction(self, h, req)
   406 
   420 
   407     def __del__(self):
   421     def __del__(self):
   408         self.close_all()
   422         self.close_all()
   409 
   423 
   414     class httpsconnection(BetterHTTPS):
   428     class httpsconnection(BetterHTTPS):
   415         response_class = keepalive.HTTPResponse
   429         response_class = keepalive.HTTPResponse
   416         # must be able to send big bundle as stream.
   430         # must be able to send big bundle as stream.
   417         send = _gen_sendfile(BetterHTTPS)
   431         send = _gen_sendfile(BetterHTTPS)
   418         getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection)
   432         getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection)
       
   433 
       
   434         def connect(self):
       
   435             if self.realhost: # use CONNECT proxy
       
   436                 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       
   437                 self.sock.connect((self.host, self.port))
       
   438                 if _generic_proxytunnel(self):
       
   439                     self.sock = _ssl_wrap_socket(self.sock, self.cert_file, self.key_file)
       
   440             else:
       
   441                 BetterHTTPS.connect(self)
   419 
   442 
   420     class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
   443     class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
   421         def __init__(self, ui):
   444         def __init__(self, ui):
   422             keepalive.KeepAliveHandler.__init__(self)
   445             keepalive.KeepAliveHandler.__init__(self)
   423             urllib2.HTTPSHandler.__init__(self)
   446             urllib2.HTTPSHandler.__init__(self)
   424             self.ui = ui
   447             self.ui = ui
   425             self.pwmgr = passwordmgr(self.ui)
   448             self.pwmgr = passwordmgr(self.ui)
       
   449 
       
   450         def _start_transaction(self, h, req):
       
   451             _generic_start_transaction(self, h, req)
       
   452             return keepalive.KeepAliveHandler._start_transaction(self, h, req)
   426 
   453 
   427         def https_open(self, req):
   454         def https_open(self, req):
   428             self.auth = self.pwmgr.readauthtoken(req.get_full_url())
   455             self.auth = self.pwmgr.readauthtoken(req.get_full_url())
   429             return self.do_open(self._makeconnection, req)
   456             return self.do_open(self._makeconnection, req)
   430 
   457