##// END OF EJS Templates
url: merge BetterHTTPS with httpsconnection to get some proxy https validation
Mads Kiilerich -
r13424:08f9c587 default
parent child Browse files
Show More
@@ -1,767 +1,758 b''
1 1 # url.py - HTTP handling for mercurial
2 2 #
3 3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 import urllib, urllib2, urlparse, httplib, os, re, socket, cStringIO
11 11 import __builtin__
12 12 from i18n import _
13 13 import keepalive, util
14 14
15 15 def _urlunparse(scheme, netloc, path, params, query, fragment, url):
16 16 '''Handle cases where urlunparse(urlparse(x://)) doesn't preserve the "//"'''
17 17 result = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
18 18 if (scheme and
19 19 result.startswith(scheme + ':') and
20 20 not result.startswith(scheme + '://') and
21 21 url.startswith(scheme + '://')
22 22 ):
23 23 result = scheme + '://' + result[len(scheme + ':'):]
24 24 return result
25 25
26 26 def hidepassword(url):
27 27 '''hide user credential in a url string'''
28 28 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
29 29 netloc = re.sub('([^:]*):([^@]*)@(.*)', r'\1:***@\3', netloc)
30 30 return _urlunparse(scheme, netloc, path, params, query, fragment, url)
31 31
32 32 def removeauth(url):
33 33 '''remove all authentication information from a url string'''
34 34 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
35 35 netloc = netloc[netloc.find('@')+1:]
36 36 return _urlunparse(scheme, netloc, path, params, query, fragment, url)
37 37
38 38 def netlocsplit(netloc):
39 39 '''split [user[:passwd]@]host[:port] into 4-tuple.'''
40 40
41 41 a = netloc.find('@')
42 42 if a == -1:
43 43 user, passwd = None, None
44 44 else:
45 45 userpass, netloc = netloc[:a], netloc[a + 1:]
46 46 c = userpass.find(':')
47 47 if c == -1:
48 48 user, passwd = urllib.unquote(userpass), None
49 49 else:
50 50 user = urllib.unquote(userpass[:c])
51 51 passwd = urllib.unquote(userpass[c + 1:])
52 52 c = netloc.find(':')
53 53 if c == -1:
54 54 host, port = netloc, None
55 55 else:
56 56 host, port = netloc[:c], netloc[c + 1:]
57 57 return host, port, user, passwd
58 58
59 59 def netlocunsplit(host, port, user=None, passwd=None):
60 60 '''turn host, port, user, passwd into [user[:passwd]@]host[:port].'''
61 61 if port:
62 62 hostport = host + ':' + port
63 63 else:
64 64 hostport = host
65 65 if user:
66 66 quote = lambda s: urllib.quote(s, safe='')
67 67 if passwd:
68 68 userpass = quote(user) + ':' + quote(passwd)
69 69 else:
70 70 userpass = quote(user)
71 71 return userpass + '@' + hostport
72 72 return hostport
73 73
74 74 def readauthforuri(ui, uri):
75 75 # Read configuration
76 76 config = dict()
77 77 for key, val in ui.configitems('auth'):
78 78 if '.' not in key:
79 79 ui.warn(_("ignoring invalid [auth] key '%s'\n") % key)
80 80 continue
81 81 group, setting = key.rsplit('.', 1)
82 82 gdict = config.setdefault(group, dict())
83 83 if setting in ('username', 'cert', 'key'):
84 84 val = util.expandpath(val)
85 85 gdict[setting] = val
86 86
87 87 # Find the best match
88 88 scheme, hostpath = uri.split('://', 1)
89 89 bestlen = 0
90 90 bestauth = None
91 91 for group, auth in config.iteritems():
92 92 prefix = auth.get('prefix')
93 93 if not prefix:
94 94 continue
95 95 p = prefix.split('://', 1)
96 96 if len(p) > 1:
97 97 schemes, prefix = [p[0]], p[1]
98 98 else:
99 99 schemes = (auth.get('schemes') or 'https').split()
100 100 if (prefix == '*' or hostpath.startswith(prefix)) and \
101 101 len(prefix) > bestlen and scheme in schemes:
102 102 bestlen = len(prefix)
103 103 bestauth = group, auth
104 104 return bestauth
105 105
106 106 _safe = ('abcdefghijklmnopqrstuvwxyz'
107 107 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
108 108 '0123456789' '_.-/')
109 109 _safeset = None
110 110 _hex = None
111 111 def quotepath(path):
112 112 '''quote the path part of a URL
113 113
114 114 This is similar to urllib.quote, but it also tries to avoid
115 115 quoting things twice (inspired by wget):
116 116
117 117 >>> quotepath('abc def')
118 118 'abc%20def'
119 119 >>> quotepath('abc%20def')
120 120 'abc%20def'
121 121 >>> quotepath('abc%20 def')
122 122 'abc%20%20def'
123 123 >>> quotepath('abc def%20')
124 124 'abc%20def%20'
125 125 >>> quotepath('abc def%2')
126 126 'abc%20def%252'
127 127 >>> quotepath('abc def%')
128 128 'abc%20def%25'
129 129 '''
130 130 global _safeset, _hex
131 131 if _safeset is None:
132 132 _safeset = set(_safe)
133 133 _hex = set('abcdefABCDEF0123456789')
134 134 l = list(path)
135 135 for i in xrange(len(l)):
136 136 c = l[i]
137 137 if (c == '%' and i + 2 < len(l) and
138 138 l[i + 1] in _hex and l[i + 2] in _hex):
139 139 pass
140 140 elif c not in _safeset:
141 141 l[i] = '%%%02X' % ord(c)
142 142 return ''.join(l)
143 143
144 144 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
145 145 def __init__(self, ui):
146 146 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
147 147 self.ui = ui
148 148
149 149 def find_user_password(self, realm, authuri):
150 150 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
151 151 self, realm, authuri)
152 152 user, passwd = authinfo
153 153 if user and passwd:
154 154 self._writedebug(user, passwd)
155 155 return (user, passwd)
156 156
157 157 if not user:
158 158 res = readauthforuri(self.ui, authuri)
159 159 if res:
160 160 group, auth = res
161 161 user, passwd = auth.get('username'), auth.get('password')
162 162 self.ui.debug("using auth.%s.* for authentication\n" % group)
163 163 if not user or not passwd:
164 164 if not self.ui.interactive():
165 165 raise util.Abort(_('http authorization required'))
166 166
167 167 self.ui.write(_("http authorization required\n"))
168 168 self.ui.write(_("realm: %s\n") % realm)
169 169 if user:
170 170 self.ui.write(_("user: %s\n") % user)
171 171 else:
172 172 user = self.ui.prompt(_("user:"), default=None)
173 173
174 174 if not passwd:
175 175 passwd = self.ui.getpass()
176 176
177 177 self.add_password(realm, authuri, user, passwd)
178 178 self._writedebug(user, passwd)
179 179 return (user, passwd)
180 180
181 181 def _writedebug(self, user, passwd):
182 182 msg = _('http auth: user %s, password %s\n')
183 183 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
184 184
185 185 class proxyhandler(urllib2.ProxyHandler):
186 186 def __init__(self, ui):
187 187 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
188 188 # XXX proxyauthinfo = None
189 189
190 190 if proxyurl:
191 191 # proxy can be proper url or host[:port]
192 192 if not (proxyurl.startswith('http:') or
193 193 proxyurl.startswith('https:')):
194 194 proxyurl = 'http://' + proxyurl + '/'
195 195 snpqf = urlparse.urlsplit(proxyurl)
196 196 proxyscheme, proxynetloc, proxypath, proxyquery, proxyfrag = snpqf
197 197 hpup = netlocsplit(proxynetloc)
198 198
199 199 proxyhost, proxyport, proxyuser, proxypasswd = hpup
200 200 if not proxyuser:
201 201 proxyuser = ui.config("http_proxy", "user")
202 202 proxypasswd = ui.config("http_proxy", "passwd")
203 203
204 204 # see if we should use a proxy for this url
205 205 no_list = ["localhost", "127.0.0.1"]
206 206 no_list.extend([p.lower() for
207 207 p in ui.configlist("http_proxy", "no")])
208 208 no_list.extend([p.strip().lower() for
209 209 p in os.getenv("no_proxy", '').split(',')
210 210 if p.strip()])
211 211 # "http_proxy.always" config is for running tests on localhost
212 212 if ui.configbool("http_proxy", "always"):
213 213 self.no_list = []
214 214 else:
215 215 self.no_list = no_list
216 216
217 217 proxyurl = urlparse.urlunsplit((
218 218 proxyscheme, netlocunsplit(proxyhost, proxyport,
219 219 proxyuser, proxypasswd or ''),
220 220 proxypath, proxyquery, proxyfrag))
221 221 proxies = {'http': proxyurl, 'https': proxyurl}
222 222 ui.debug('proxying through http://%s:%s\n' %
223 223 (proxyhost, proxyport))
224 224 else:
225 225 proxies = {}
226 226
227 227 # urllib2 takes proxy values from the environment and those
228 228 # will take precedence if found, so drop them
229 229 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
230 230 try:
231 231 if env in os.environ:
232 232 del os.environ[env]
233 233 except OSError:
234 234 pass
235 235
236 236 urllib2.ProxyHandler.__init__(self, proxies)
237 237 self.ui = ui
238 238
239 239 def proxy_open(self, req, proxy, type_):
240 240 host = req.get_host().split(':')[0]
241 241 if host in self.no_list:
242 242 return None
243 243
244 244 # work around a bug in Python < 2.4.2
245 245 # (it leaves a "\n" at the end of Proxy-authorization headers)
246 246 baseclass = req.__class__
247 247 class _request(baseclass):
248 248 def add_header(self, key, val):
249 249 if key.lower() == 'proxy-authorization':
250 250 val = val.strip()
251 251 return baseclass.add_header(self, key, val)
252 252 req.__class__ = _request
253 253
254 254 return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_)
255 255
256 256 class httpsendfile(object):
257 257 """This is a wrapper around the objects returned by python's "open".
258 258
259 259 Its purpose is to send file-like objects via HTTP and, to do so, it
260 260 defines a __len__ attribute to feed the Content-Length header.
261 261 """
262 262
263 263 def __init__(self, ui, *args, **kwargs):
264 264 # We can't just "self._data = open(*args, **kwargs)" here because there
265 265 # is an "open" function defined in this module that shadows the global
266 266 # one
267 267 self.ui = ui
268 268 self._data = __builtin__.open(*args, **kwargs)
269 269 self.seek = self._data.seek
270 270 self.close = self._data.close
271 271 self.write = self._data.write
272 272 self._len = os.fstat(self._data.fileno()).st_size
273 273 self._pos = 0
274 274 self._total = len(self) / 1024 * 2
275 275
276 276 def read(self, *args, **kwargs):
277 277 try:
278 278 ret = self._data.read(*args, **kwargs)
279 279 except EOFError:
280 280 self.ui.progress(_('sending'), None)
281 281 self._pos += len(ret)
282 282 # We pass double the max for total because we currently have
283 283 # to send the bundle twice in the case of a server that
284 284 # requires authentication. Since we can't know until we try
285 285 # once whether authentication will be required, just lie to
286 286 # the user and maybe the push succeeds suddenly at 50%.
287 287 self.ui.progress(_('sending'), self._pos / 1024,
288 288 unit=_('kb'), total=self._total)
289 289 return ret
290 290
291 291 def __len__(self):
292 292 return self._len
293 293
294 294 def _gen_sendfile(orgsend):
295 295 def _sendfile(self, data):
296 296 # send a file
297 297 if isinstance(data, httpsendfile):
298 298 # if auth required, some data sent twice, so rewind here
299 299 data.seek(0)
300 300 for chunk in util.filechunkiter(data):
301 301 orgsend(self, chunk)
302 302 else:
303 303 orgsend(self, data)
304 304 return _sendfile
305 305
306 306 has_https = hasattr(urllib2, 'HTTPSHandler')
307 307 if has_https:
308 308 try:
309 309 # avoid using deprecated/broken FakeSocket in python 2.6
310 310 import ssl
311 311 _ssl_wrap_socket = ssl.wrap_socket
312 312 CERT_REQUIRED = ssl.CERT_REQUIRED
313 313 except ImportError:
314 314 CERT_REQUIRED = 2
315 315
316 316 def _ssl_wrap_socket(sock, key_file, cert_file,
317 317 cert_reqs=CERT_REQUIRED, ca_certs=None):
318 318 if ca_certs:
319 319 raise util.Abort(_(
320 320 'certificate checking requires Python 2.6'))
321 321
322 322 ssl = socket.ssl(sock, key_file, cert_file)
323 323 return httplib.FakeSocket(sock, ssl)
324 324
325 325 try:
326 326 _create_connection = socket.create_connection
327 327 except AttributeError:
328 328 _GLOBAL_DEFAULT_TIMEOUT = object()
329 329
330 330 def _create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
331 331 source_address=None):
332 332 # lifted from Python 2.6
333 333
334 334 msg = "getaddrinfo returns an empty list"
335 335 host, port = address
336 336 for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
337 337 af, socktype, proto, canonname, sa = res
338 338 sock = None
339 339 try:
340 340 sock = socket.socket(af, socktype, proto)
341 341 if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
342 342 sock.settimeout(timeout)
343 343 if source_address:
344 344 sock.bind(source_address)
345 345 sock.connect(sa)
346 346 return sock
347 347
348 348 except socket.error, msg:
349 349 if sock is not None:
350 350 sock.close()
351 351
352 352 raise socket.error, msg
353 353
354 354 class httpconnection(keepalive.HTTPConnection):
355 355 # must be able to send big bundle as stream.
356 356 send = _gen_sendfile(keepalive.HTTPConnection.send)
357 357
358 358 def connect(self):
359 359 if has_https and self.realhostport: # use CONNECT proxy
360 360 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
361 361 self.sock.connect((self.host, self.port))
362 362 if _generic_proxytunnel(self):
363 363 # we do not support client x509 certificates
364 364 self.sock = _ssl_wrap_socket(self.sock, None, None)
365 365 else:
366 366 keepalive.HTTPConnection.connect(self)
367 367
368 368 def getresponse(self):
369 369 proxyres = getattr(self, 'proxyres', None)
370 370 if proxyres:
371 371 if proxyres.will_close:
372 372 self.close()
373 373 self.proxyres = None
374 374 return proxyres
375 375 return keepalive.HTTPConnection.getresponse(self)
376 376
377 377 # general transaction handler to support different ways to handle
378 378 # HTTPS proxying before and after Python 2.6.3.
379 379 def _generic_start_transaction(handler, h, req):
380 380 if hasattr(req, '_tunnel_host') and req._tunnel_host:
381 381 tunnel_host = req._tunnel_host
382 382 if tunnel_host[:7] not in ['http://', 'https:/']:
383 383 tunnel_host = 'https://' + tunnel_host
384 384 new_tunnel = True
385 385 else:
386 386 tunnel_host = req.get_selector()
387 387 new_tunnel = False
388 388
389 389 if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
390 390 urlparts = urlparse.urlparse(tunnel_host)
391 391 if new_tunnel or urlparts[0] == 'https': # only use CONNECT for HTTPS
392 392 realhostport = urlparts[1]
393 393 if realhostport[-1] == ']' or ':' not in realhostport:
394 394 realhostport += ':443'
395 395
396 396 h.realhostport = realhostport
397 397 h.headers = req.headers.copy()
398 398 h.headers.update(handler.parent.addheaders)
399 399 return
400 400
401 401 h.realhostport = None
402 402 h.headers = None
403 403
404 404 def _generic_proxytunnel(self):
405 405 proxyheaders = dict(
406 406 [(x, self.headers[x]) for x in self.headers
407 407 if x.lower().startswith('proxy-')])
408 408 self._set_hostport(self.host, self.port)
409 409 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
410 410 for header in proxyheaders.iteritems():
411 411 self.send('%s: %s\r\n' % header)
412 412 self.send('\r\n')
413 413
414 414 # majority of the following code is duplicated from
415 415 # httplib.HTTPConnection as there are no adequate places to
416 416 # override functions to provide the needed functionality
417 417 res = self.response_class(self.sock,
418 418 strict=self.strict,
419 419 method=self._method)
420 420
421 421 while True:
422 422 version, status, reason = res._read_status()
423 423 if status != httplib.CONTINUE:
424 424 break
425 425 while True:
426 426 skip = res.fp.readline().strip()
427 427 if not skip:
428 428 break
429 429 res.status = status
430 430 res.reason = reason.strip()
431 431
432 432 if res.status == 200:
433 433 while True:
434 434 line = res.fp.readline()
435 435 if line == '\r\n':
436 436 break
437 437 return True
438 438
439 439 if version == 'HTTP/1.0':
440 440 res.version = 10
441 441 elif version.startswith('HTTP/1.'):
442 442 res.version = 11
443 443 elif version == 'HTTP/0.9':
444 444 res.version = 9
445 445 else:
446 446 raise httplib.UnknownProtocol(version)
447 447
448 448 if res.version == 9:
449 449 res.length = None
450 450 res.chunked = 0
451 451 res.will_close = 1
452 452 res.msg = httplib.HTTPMessage(cStringIO.StringIO())
453 453 return False
454 454
455 455 res.msg = httplib.HTTPMessage(res.fp)
456 456 res.msg.fp = None
457 457
458 458 # are we using the chunked-style of transfer encoding?
459 459 trenc = res.msg.getheader('transfer-encoding')
460 460 if trenc and trenc.lower() == "chunked":
461 461 res.chunked = 1
462 462 res.chunk_left = None
463 463 else:
464 464 res.chunked = 0
465 465
466 466 # will the connection close at the end of the response?
467 467 res.will_close = res._check_close()
468 468
469 469 # do we have a Content-Length?
470 470 # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
471 471 length = res.msg.getheader('content-length')
472 472 if length and not res.chunked:
473 473 try:
474 474 res.length = int(length)
475 475 except ValueError:
476 476 res.length = None
477 477 else:
478 478 if res.length < 0: # ignore nonsensical negative lengths
479 479 res.length = None
480 480 else:
481 481 res.length = None
482 482
483 483 # does the body have a fixed length? (of zero)
484 484 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
485 485 100 <= status < 200 or # 1xx codes
486 486 res._method == 'HEAD'):
487 487 res.length = 0
488 488
489 489 # if the connection remains open, and we aren't using chunked, and
490 490 # a content-length was not provided, then assume that the connection
491 491 # WILL close.
492 492 if (not res.will_close and
493 493 not res.chunked and
494 494 res.length is None):
495 495 res.will_close = 1
496 496
497 497 self.proxyres = res
498 498
499 499 return False
500 500
501 501 class httphandler(keepalive.HTTPHandler):
502 502 def http_open(self, req):
503 503 return self.do_open(httpconnection, req)
504 504
505 505 def _start_transaction(self, h, req):
506 506 _generic_start_transaction(self, h, req)
507 507 return keepalive.HTTPHandler._start_transaction(self, h, req)
508 508
509 509 def _verifycert(cert, hostname):
510 510 '''Verify that cert (in socket.getpeercert() format) matches hostname.
511 511 CRLs is not handled.
512 512
513 513 Returns error message if any problems are found and None on success.
514 514 '''
515 515 if not cert:
516 516 return _('no certificate received')
517 517 dnsname = hostname.lower()
518 518 def matchdnsname(certname):
519 519 return (certname == dnsname or
520 520 '.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1])
521 521
522 522 san = cert.get('subjectAltName', [])
523 523 if san:
524 524 certnames = [value.lower() for key, value in san if key == 'DNS']
525 525 for name in certnames:
526 526 if matchdnsname(name):
527 527 return None
528 528 return _('certificate is for %s') % ', '.join(certnames)
529 529
530 530 # subject is only checked when subjectAltName is empty
531 531 for s in cert.get('subject', []):
532 532 key, value = s[0]
533 533 if key == 'commonName':
534 534 try:
535 535 # 'subject' entries are unicode
536 536 certname = value.lower().encode('ascii')
537 537 except UnicodeEncodeError:
538 538 return _('IDN in certificate not supported')
539 539 if matchdnsname(certname):
540 540 return None
541 541 return _('certificate is for %s') % certname
542 542 return _('no commonName or subjectAltName found in certificate')
543 543
544 544 if has_https:
545 class BetterHTTPS(httplib.HTTPSConnection):
546 send = keepalive.safesend
545 class httpsconnection(httplib.HTTPSConnection):
546 response_class = keepalive.HTTPResponse
547 # must be able to send big bundle as stream.
548 send = _gen_sendfile(keepalive.safesend)
549 getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection)
547 550
548 551 def connect(self):
549 552 self.sock = _create_connection((self.host, self.port))
550 553
551 554 host = self.host
555 if self.realhostport: # use CONNECT proxy
556 something = _generic_proxytunnel(self)
557 host = self.realhostport.rsplit(':', 1)[0]
558
552 559 cacerts = self.ui.config('web', 'cacerts')
553 560 hostfingerprint = self.ui.config('hostfingerprints', host)
554 561
555 562 if cacerts and not hostfingerprint:
556 563 self.sock = _ssl_wrap_socket(self.sock, self.key_file,
557 564 self.cert_file, cert_reqs=CERT_REQUIRED,
558 565 ca_certs=util.expandpath(cacerts))
559 566 msg = _verifycert(self.sock.getpeercert(), host)
560 567 if msg:
561 568 raise util.Abort(_('%s certificate error: %s '
562 569 '(use --insecure to connect '
563 570 'insecurely)') % (host, msg))
564 571 self.ui.debug('%s certificate successfully verified\n' % host)
565 572 else:
566 573 self.sock = _ssl_wrap_socket(self.sock, self.key_file,
567 574 self.cert_file)
568 575 if hasattr(self.sock, 'getpeercert'):
569 576 peercert = self.sock.getpeercert(True)
570 577 peerfingerprint = util.sha1(peercert).hexdigest()
571 578 nicefingerprint = ":".join([peerfingerprint[x:x + 2]
572 579 for x in xrange(0, len(peerfingerprint), 2)])
573 580 if hostfingerprint:
574 581 if peerfingerprint.lower() != \
575 582 hostfingerprint.replace(':', '').lower():
576 583 raise util.Abort(_('invalid certificate for %s '
577 584 'with fingerprint %s') %
578 585 (host, nicefingerprint))
579 586 self.ui.debug('%s certificate matched fingerprint %s\n' %
580 587 (host, nicefingerprint))
581 588 else:
582 589 self.ui.warn(_('warning: %s certificate '
583 590 'with fingerprint %s not verified '
584 591 '(check hostfingerprints or web.cacerts '
585 592 'config setting)\n') %
586 593 (host, nicefingerprint))
587 594 else: # python 2.5 ?
588 595 if hostfingerprint:
589 596 raise util.Abort(_('no certificate for %s with '
590 597 'configured hostfingerprint') % host)
591 598 self.ui.warn(_('warning: %s certificate not verified '
592 599 '(check web.cacerts config setting)\n') %
593 600 host)
594 601
595 class httpsconnection(BetterHTTPS):
596 response_class = keepalive.HTTPResponse
597 # must be able to send big bundle as stream.
598 send = _gen_sendfile(BetterHTTPS.send)
599 getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection)
600
601 def connect(self):
602 if self.realhostport: # use CONNECT proxy
603 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
604 self.sock.connect((self.host, self.port))
605 if _generic_proxytunnel(self):
606 self.sock = _ssl_wrap_socket(self.sock, self.key_file,
607 self.cert_file)
608 else:
609 BetterHTTPS.connect(self)
610
611 602 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
612 603 def __init__(self, ui):
613 604 keepalive.KeepAliveHandler.__init__(self)
614 605 urllib2.HTTPSHandler.__init__(self)
615 606 self.ui = ui
616 607 self.pwmgr = passwordmgr(self.ui)
617 608
618 609 def _start_transaction(self, h, req):
619 610 _generic_start_transaction(self, h, req)
620 611 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
621 612
622 613 def https_open(self, req):
623 614 res = readauthforuri(self.ui, req.get_full_url())
624 615 if res:
625 616 group, auth = res
626 617 self.auth = auth
627 618 self.ui.debug("using auth.%s.* for authentication\n" % group)
628 619 else:
629 620 self.auth = None
630 621 return self.do_open(self._makeconnection, req)
631 622
632 623 def _makeconnection(self, host, port=None, *args, **kwargs):
633 624 keyfile = None
634 625 certfile = None
635 626
636 627 if len(args) >= 1: # key_file
637 628 keyfile = args[0]
638 629 if len(args) >= 2: # cert_file
639 630 certfile = args[1]
640 631 args = args[2:]
641 632
642 633 # if the user has specified different key/cert files in
643 634 # hgrc, we prefer these
644 635 if self.auth and 'key' in self.auth and 'cert' in self.auth:
645 636 keyfile = self.auth['key']
646 637 certfile = self.auth['cert']
647 638
648 639 conn = httpsconnection(host, port, keyfile, certfile, *args, **kwargs)
649 640 conn.ui = self.ui
650 641 return conn
651 642
652 643 class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler):
653 644 def __init__(self, *args, **kwargs):
654 645 urllib2.HTTPDigestAuthHandler.__init__(self, *args, **kwargs)
655 646 self.retried_req = None
656 647
657 648 def reset_retry_count(self):
658 649 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
659 650 # forever. We disable reset_retry_count completely and reset in
660 651 # http_error_auth_reqed instead.
661 652 pass
662 653
663 654 def http_error_auth_reqed(self, auth_header, host, req, headers):
664 655 # Reset the retry counter once for each request.
665 656 if req is not self.retried_req:
666 657 self.retried_req = req
667 658 self.retried = 0
668 659 # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
669 660 # it doesn't know about the auth type requested. This can happen if
670 661 # somebody is using BasicAuth and types a bad password.
671 662 try:
672 663 return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
673 664 self, auth_header, host, req, headers)
674 665 except ValueError, inst:
675 666 arg = inst.args[0]
676 667 if arg.startswith("AbstractDigestAuthHandler doesn't know "):
677 668 return
678 669 raise
679 670
680 671 class httpbasicauthhandler(urllib2.HTTPBasicAuthHandler):
681 672 def __init__(self, *args, **kwargs):
682 673 urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
683 674 self.retried_req = None
684 675
685 676 def reset_retry_count(self):
686 677 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
687 678 # forever. We disable reset_retry_count completely and reset in
688 679 # http_error_auth_reqed instead.
689 680 pass
690 681
691 682 def http_error_auth_reqed(self, auth_header, host, req, headers):
692 683 # Reset the retry counter once for each request.
693 684 if req is not self.retried_req:
694 685 self.retried_req = req
695 686 self.retried = 0
696 687 return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
697 688 self, auth_header, host, req, headers)
698 689
699 690 def getauthinfo(path):
700 691 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
701 692 if not urlpath:
702 693 urlpath = '/'
703 694 if scheme != 'file':
704 695 # XXX: why are we quoting the path again with some smart
705 696 # heuristic here? Anyway, it cannot be done with file://
706 697 # urls since path encoding is os/fs dependent (see
707 698 # urllib.pathname2url() for details).
708 699 urlpath = quotepath(urlpath)
709 700 host, port, user, passwd = netlocsplit(netloc)
710 701
711 702 # urllib cannot handle URLs with embedded user or passwd
712 703 url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
713 704 urlpath, query, frag))
714 705 if user:
715 706 netloc = host
716 707 if port:
717 708 netloc += ':' + port
718 709 # Python < 2.4.3 uses only the netloc to search for a password
719 710 authinfo = (None, (url, netloc), user, passwd or '')
720 711 else:
721 712 authinfo = None
722 713 return url, authinfo
723 714
724 715 handlerfuncs = []
725 716
726 717 def opener(ui, authinfo=None):
727 718 '''
728 719 construct an opener suitable for urllib2
729 720 authinfo will be added to the password manager
730 721 '''
731 722 handlers = [httphandler()]
732 723 if has_https:
733 724 handlers.append(httpshandler(ui))
734 725
735 726 handlers.append(proxyhandler(ui))
736 727
737 728 passmgr = passwordmgr(ui)
738 729 if authinfo is not None:
739 730 passmgr.add_password(*authinfo)
740 731 user, passwd = authinfo[2:4]
741 732 ui.debug('http auth: user %s, password %s\n' %
742 733 (user, passwd and '*' * len(passwd) or 'not set'))
743 734
744 735 handlers.extend((httpbasicauthhandler(passmgr),
745 736 httpdigestauthhandler(passmgr)))
746 737 handlers.extend([h(ui, passmgr) for h in handlerfuncs])
747 738 opener = urllib2.build_opener(*handlers)
748 739
749 740 # 1.0 here is the _protocol_ version
750 741 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
751 742 opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
752 743 return opener
753 744
754 745 scheme_re = re.compile(r'^([a-zA-Z0-9+-.]+)://')
755 746
756 747 def open(ui, url, data=None):
757 748 scheme = None
758 749 m = scheme_re.search(url)
759 750 if m:
760 751 scheme = m.group(1).lower()
761 752 if not scheme:
762 753 path = util.normpath(os.path.abspath(url))
763 754 url = 'file://' + urllib.pathname2url(path)
764 755 authinfo = None
765 756 else:
766 757 url, authinfo = getauthinfo(url)
767 758 return opener(ui, authinfo).open(url, data)
@@ -1,270 +1,268 b''
1 1 Proper https client requires the built-in ssl from Python 2.6.
2 2
3 3 $ "$TESTDIR/hghave" ssl || exit 80
4 4
5 5 Certificates created with:
6 6 printf '.\n.\n.\n.\n.\nlocalhost\nhg@localhost\n' | \
7 7 openssl req -newkey rsa:512 -keyout priv.pem -nodes -x509 -days 9000 -out pub.pem
8 8 Can be dumped with:
9 9 openssl x509 -in pub.pem -text
10 10
11 11 $ cat << EOT > priv.pem
12 12 > -----BEGIN PRIVATE KEY-----
13 13 > MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEApjCWeYGrIa/Vo7LH
14 14 > aRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8
15 15 > j/xgSwIDAQABAkBxHC6+Qlf0VJXGlb6NL16yEVVTQxqDS6hA9zqu6TZjrr0YMfzc
16 16 > EGNIiZGt7HCBL0zO+cPDg/LeCZc6HQhf0KrhAiEAzlJq4hWWzvguWFIJWSoBeBUG
17 17 > MF1ACazQO7PYE8M0qfECIQDONHHP0SKZzz/ZwBZcAveC5K61f/v9hONFwbeYulzR
18 18 > +wIgc9SvbtgB/5Yzpp//4ZAEnR7oh5SClCvyB+KSx52K3nECICbhQphhoXmI10wy
19 19 > aMTellaq0bpNMHFDziqH9RsqAHhjAiEAgYGxfzkftt5IUUn/iFK89aaIpyrpuaAh
20 20 > HY8gUVkVRVs=
21 21 > -----END PRIVATE KEY-----
22 22 > EOT
23 23
24 24 $ cat << EOT > pub.pem
25 25 > -----BEGIN CERTIFICATE-----
26 26 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV
27 27 > BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw
28 28 > MTAxNDIwMzAxNFoXDTM1MDYwNTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0
29 29 > MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL
30 30 > ADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX
31 31 > 6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA+amm
32 32 > r24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQw
33 33 > DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAFArvQFiAZJgQczRsbYlG1xl
34 34 > t+truk37w5B3m3Ick1ntRcQrqs+hf0CO1q6Squ144geYaQ8CDirSR92fICELI1c=
35 35 > -----END CERTIFICATE-----
36 36 > EOT
37 37 $ cat priv.pem pub.pem >> server.pem
38 38 $ PRIV=`pwd`/server.pem
39 39
40 40 $ cat << EOT > pub-other.pem
41 41 > -----BEGIN CERTIFICATE-----
42 42 > MIIBqzCCAVWgAwIBAgIJALwZS731c/ORMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV
43 43 > BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw
44 44 > MTAxNDIwNDUxNloXDTM1MDYwNTIwNDUxNlowMTESMBAGA1UEAwwJbG9jYWxob3N0
45 45 > MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL
46 46 > ADBIAkEAsxsapLbHrqqUKuQBxdpK4G3m2LjtyrTSdpzzzFlecxd5yhNP6AyWrufo
47 47 > K4VMGo2xlu9xOo88nDSUNSKPuD09MwIDAQABo1AwTjAdBgNVHQ4EFgQUoIB1iMhN
48 48 > y868rpQ2qk9dHnU6ebswHwYDVR0jBBgwFoAUoIB1iMhNy868rpQ2qk9dHnU6ebsw
49 49 > DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJ544f125CsE7J2t55PdFaF6
50 50 > bBlNBb91FCywBgSjhBjf+GG3TNPwrPdc3yqeq+hzJiuInqbOBv9abmMyq8Wsoig=
51 51 > -----END CERTIFICATE-----
52 52 > EOT
53 53
54 54 pub.pem patched with other notBefore / notAfter:
55 55
56 56 $ cat << EOT > pub-not-yet.pem
57 57 > -----BEGIN CERTIFICATE-----
58 58 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs
59 59 > aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTM1MDYwNTIwMzAxNFoXDTM1MDYw
60 60 > NTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv
61 61 > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK
62 62 > EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA
63 63 > +ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T
64 64 > BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJXV41gWnkgC7jcpPpFRSUSZaxyzrXmD1CIqQf0WgVDb
65 65 > /12E0vR2DuZitgzUYtBaofM81aTtc0a2/YsrmqePGm0=
66 66 > -----END CERTIFICATE-----
67 67 > EOT
68 68 $ cat priv.pem pub-not-yet.pem > server-not-yet.pem
69 69
70 70 $ cat << EOT > pub-expired.pem
71 71 > -----BEGIN CERTIFICATE-----
72 72 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs
73 73 > aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEwMTAxNDIwMzAxNFoXDTEwMTAx
74 74 > NDIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv
75 75 > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK
76 76 > EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA
77 77 > +ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T
78 78 > BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJfk57DTRf2nUbYaMSlVAARxMNbFGOjQhAUtY400GhKt
79 79 > 2uiKCNGKXVXD3AHWe13yHc5KttzbHQStE5Nm/DlWBWQ=
80 80 > -----END CERTIFICATE-----
81 81 > EOT
82 82 $ cat priv.pem pub-expired.pem > server-expired.pem
83 83
84 84 $ hg init test
85 85 $ cd test
86 86 $ echo foo>foo
87 87 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
88 88 $ echo foo>foo.d/foo
89 89 $ echo bar>foo.d/bAr.hg.d/BaR
90 90 $ echo bar>foo.d/baR.d.hg/bAR
91 91 $ hg commit -A -m 1
92 92 adding foo
93 93 adding foo.d/bAr.hg.d/BaR
94 94 adding foo.d/baR.d.hg/bAR
95 95 adding foo.d/foo
96 96 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
97 97 $ cat ../hg0.pid >> $DAEMON_PIDS
98 98
99 99 Test server address cannot be reused
100 100
101 101 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
102 102 abort: cannot start server at ':$HGPORT': Address already in use
103 103 [255]
104 104 $ cd ..
105 105
106 106 clone via pull
107 107
108 108 $ hg clone https://localhost:$HGPORT/ copy-pull
109 109 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
110 110 requesting all changes
111 111 adding changesets
112 112 adding manifests
113 113 adding file changes
114 114 added 1 changesets with 4 changes to 4 files
115 115 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
116 116 updating to branch default
117 117 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 118 $ hg verify -R copy-pull
119 119 checking changesets
120 120 checking manifests
121 121 crosschecking files in changesets and manifests
122 122 checking files
123 123 4 files, 1 changesets, 4 total revisions
124 124 $ cd test
125 125 $ echo bar > bar
126 126 $ hg commit -A -d '1 0' -m 2
127 127 adding bar
128 128 $ cd ..
129 129
130 130 pull without cacert
131 131
132 132 $ cd copy-pull
133 133 $ echo '[hooks]' >> .hg/hgrc
134 134 $ echo "changegroup = python '$TESTDIR'/printenv.py changegroup" >> .hg/hgrc
135 135 $ hg pull
136 136 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
137 137 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=https://localhost:$HGPORT/
138 138 pulling from https://localhost:$HGPORT/
139 139 searching for changes
140 140 adding changesets
141 141 adding manifests
142 142 adding file changes
143 143 added 1 changesets with 1 changes to 1 files
144 144 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
145 145 (run 'hg update' to get a working copy)
146 146 $ cd ..
147 147
148 148 cacert configured in local repo
149 149
150 150 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
151 151 $ echo "[web]" >> copy-pull/.hg/hgrc
152 152 $ echo "cacerts=`pwd`/pub.pem" >> copy-pull/.hg/hgrc
153 153 $ hg -R copy-pull pull --traceback
154 154 pulling from https://localhost:$HGPORT/
155 155 searching for changes
156 156 no changes found
157 157 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
158 158
159 159 cacert configured globally, also testing expansion of environment
160 160 variables in the filename
161 161
162 162 $ echo "[web]" >> $HGRCPATH
163 163 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
164 164 $ P=`pwd` hg -R copy-pull pull
165 165 pulling from https://localhost:$HGPORT/
166 166 searching for changes
167 167 no changes found
168 168 $ P=`pwd` hg -R copy-pull pull --insecure
169 169 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
170 170 pulling from https://localhost:$HGPORT/
171 171 searching for changes
172 172 no changes found
173 173
174 174 cacert mismatch
175 175
176 176 $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/
177 177 abort: 127.0.0.1 certificate error: certificate is for localhost (use --insecure to connect insecurely)
178 178 [255]
179 179 $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/ --insecure
180 180 warning: 127.0.0.1 certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
181 181 pulling from https://127.0.0.1:$HGPORT/
182 182 searching for changes
183 183 no changes found
184 184 $ hg -R copy-pull pull --config web.cacerts=pub-other.pem
185 185 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
186 186 [255]
187 187 $ hg -R copy-pull pull --config web.cacerts=pub-other.pem --insecure
188 188 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
189 189 pulling from https://localhost:$HGPORT/
190 190 searching for changes
191 191 no changes found
192 192
193 193 Test server cert which isn't valid yet
194 194
195 195 $ hg -R test serve -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
196 196 $ cat hg1.pid >> $DAEMON_PIDS
197 197 $ hg -R copy-pull pull --config web.cacerts=pub-not-yet.pem https://localhost:$HGPORT1/
198 198 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
199 199 [255]
200 200
201 201 Test server cert which no longer is valid
202 202
203 203 $ hg -R test serve -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
204 204 $ cat hg2.pid >> $DAEMON_PIDS
205 205 $ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/
206 206 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
207 207 [255]
208 208
209 209 Fingerprints
210 210
211 211 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
212 212 $ echo "localhost = 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca" >> copy-pull/.hg/hgrc
213 213 $ echo "127.0.0.1 = 914f1aff87249c09b6859b88b1906d30756491ca" >> copy-pull/.hg/hgrc
214 214
215 215 - works without cacerts
216 216 $ hg -R copy-pull id https://localhost:$HGPORT/ --config web.cacerts=
217 217 5fed3813f7f5
218 218
219 219 - fails when cert doesn't match hostname (port is ignored)
220 220 $ hg -R copy-pull id https://localhost:$HGPORT1/
221 221 abort: invalid certificate for localhost with fingerprint 28:ff:71:bf:65:31:14:23:ad:62:92:b4:0e:31:99:18:fc:83:e3:9b
222 222 [255]
223 223
224 224 - ignores that certificate doesn't match hostname
225 225 $ hg -R copy-pull id https://127.0.0.1:$HGPORT/
226 226 5fed3813f7f5
227 227
228 228 Prepare for connecting through proxy
229 229
230 230 $ kill `cat hg1.pid`
231 231 $ sleep 1
232 232
233 233 $ ("$TESTDIR/tinyproxy.py" $HGPORT1 localhost >proxy.log 2>&1 </dev/null &
234 234 $ echo $! > proxy.pid)
235 235 $ cat proxy.pid >> $DAEMON_PIDS
236 236 $ sleep 2
237 237
238 238 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
239 239 $ echo "always=True" >> copy-pull/.hg/hgrc
240 240 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
241 241 $ echo "localhost =" >> copy-pull/.hg/hgrc
242 242
243 243 Test unvalidated https through proxy
244 244
245 245 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure --traceback
246 246 pulling from https://localhost:$HGPORT/
247 247 searching for changes
248 248 no changes found
249 249
250 250 Test https with cacert and fingerprint through proxy
251 251
252 252 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub.pem
253 253 pulling from https://localhost:$HGPORT/
254 254 searching for changes
255 255 no changes found
256 256 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://127.0.0.1:$HGPORT/
257 257 pulling from https://127.0.0.1:$HGPORT/
258 258 searching for changes
259 259 no changes found
260 260
261 261 Test https with cert problems through proxy
262 262
263 263 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-other.pem
264 pulling from https://localhost:$HGPORT/
265 searching for changes
266 no changes found
264 abort: error: _ssl.c:499: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
265 [255]
267 266 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/
268 pulling from https://localhost:$HGPORT2/
269 searching for changes
270 no changes found
267 abort: error: _ssl.c:499: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
268 [255]
General Comments 0
You need to be logged in to leave comments. Login now