Patchwork [4,of,5,clonebundles,V2] clonebundles: filter on SNI requirement

login
register
mail settings
Submitter Gregory Szorc
Date Oct. 9, 2015, 7:33 p.m.
Message ID <5a067bc8dc2c1c1edb0b.1444419233@gps-mbp.local>
Download mbox | patch
Permalink /patch/10934/
State Superseded
Commit 2faa7671a4b3686a433b1f490e5b37222a99cc0d
Delegated to: Matt Mackall
Headers show

Comments

Gregory Szorc - Oct. 9, 2015, 7:33 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# 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.

Patch

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