From patchwork Fri Oct 9 19:33:53 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [4,of,5,clonebundles,V2] clonebundles: filter on SNI requirement From: Gregory Szorc X-Patchwork-Id: 10934 Message-Id: <5a067bc8dc2c1c1edb0b.1444419233@gps-mbp.local> To: mercurial-devel@selenic.com Date: Fri, 09 Oct 2015 12:33:53 -0700 # HG changeset patch # User Gregory Szorc # Date 1444418686 25200 # Fri Oct 09 12:24:46 2015 -0700 # Node ID 5a067bc8dc2c1c1edb0b42e786fa846a003903da # Parent 0a6acb63419fb7835c48ad4967ef8ed34e70aa94 clonebundles: filter on SNI requirement Server Name Indication (SNI) is commonly used in CDNs and other hosted environments. Unfortunately, Python <2.7.9 does not support SNI and when these older Python versions attempt to negotiate TLS to an SNI server, they raise an opaque error like "_ssl.c:507: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure." We introduce a manifest attribute to denote the URL requires SNI and have clients without SNI support filter these entries. diff --git a/hgext/clonebundles.py b/hgext/clonebundles.py --- a/hgext/clonebundles.py +++ b/hgext/clonebundles.py @@ -49,8 +49,21 @@ TYPE Bundle2 bundles ("HG20") are currently not supported. The actual value doesn't impact client behavior beyond filtering: clients will still sniff the bundle type from downloaded content. + +REQUIRESNI + Whether Server Name Indication (SNI) is required to connect to the URL. + SNI allows servers to use multiple certificates on the same IP. It is + somewhat common in CDNs and other hosting providers. Older Python + versions do not support SNI. Defining this attribute enables clients + with older Python versions to filter this entry. + + If this is defined, it is important to advertise a non-SNI fallback + URL or clients running old Python releases may not be able to clone + with the clonebundles facility. + + Value should be "true". """ from mercurial import ( extensions, diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -11,8 +11,9 @@ import errno, urllib, urllib2 import util, scmutil, changegroup, base85, error import discovery, phases, obsolete, bookmarks as bookmod, bundle2, pushkey import lock as lockmod import streamclone +import sslutil import tags import url as urlmod def readbundle(ui, fh, fname, vfs=None): @@ -1591,8 +1592,12 @@ def filterclonebundleentries(ui, entries ui.debug('filtering %s because bundle2 is not supported\n' % e['URL']) continue + if 'REQUIRESNI' in e and not sslutil.hassni: + ui.debug('filtering %s because SNI not supported\n' % e['URL']) + continue + newentries.append(e) return newentries diff --git a/tests/test-clonebundles.t b/tests/test-clonebundles.t --- a/tests/test-clonebundles.t +++ b/tests/test-clonebundles.t @@ -188,4 +188,38 @@ Bundle2 is not yet supported adding changesets adding manifests adding file changes added 2 changesets with 2 changes to 2 files + +URLs requiring SNI are filtered in Python <2.7.9 + + $ cp full.hg sni.hg + $ cat > server/.hg/clonebundles.manifest << EOF + > http://localhost:$HGPORT1/sni.hg REQUIRESNI=true + > http://localhost:$HGPORT1/full.hg + > EOF + +#if sslcontext +Python 2.7.9+ support SNI + + $ hg clone -U http://localhost:$HGPORT sni-supported + applying clone bundle from http://localhost:$HGPORT1/sni.hg + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files + finished applying clone bundle + searching for changes + no changes found +#else +Python <2.7.9 will filter SNI URLs + + $ hg clone -U http://localhost:$HGPORT sni-unsupported + applying clone bundle from http://localhost:$HGPORT1/full.hg + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files + finished applying clone bundle + searching for changes + no changes found +#endif