##// END OF EJS Templates
url: generalise HTTPS proxy handling to accomodate Python changes...
Henrik Stuart -
r9852:917cf6bb default
parent child Browse files
Show More
@@ -262,108 +262,11 b' class httpconnection(keepalive.HTTPConne'
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:
@@ -378,30 +281,141 b' class httpconnection(keepalive.HTTPConne'
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):
@@ -417,6 +431,15 b' if has_https:'
417 send = _gen_sendfile(BetterHTTPS)
431 send = _gen_sendfile(BetterHTTPS)
418 getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection)
432 getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection)
419
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)
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)
@@ -424,6 +447,10 b' if has_https:'
424 self.ui = ui
447 self.ui = ui
425 self.pwmgr = passwordmgr(self.ui)
448 self.pwmgr = passwordmgr(self.ui)
426
449
450 def _start_transaction(self, h, req):
451 _generic_start_transaction(self, h, req)
452 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
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)
General Comments 0
You need to be logged in to leave comments. Login now