@@ -879,6 +879,7 @@ def wrap_conn(self):
879879 try :
880880 self .sslconn = self .server .context .wrap_socket (
881881 self .sock , server_side = True )
882+ self .server .selected_protocols .append (self .sslconn .selected_npn_protocol ())
882883 except ssl .SSLError as e :
883884 # XXX Various errors can have happened here, for example
884885 # a mismatching protocol version, an invalid certificate,
@@ -901,6 +902,8 @@ def wrap_conn(self):
901902 cipher = self .sslconn .cipher ()
902903 if support .verbose and self .server .chatty :
903904 sys .stdout .write (" server: connection cipher is now " + str (cipher ) + "\n " )
905+ sys .stdout .write (" server: selected protocol is now "
906+ + str (self .sslconn .selected_npn_protocol ()) + "\n " )
904907 return True
905908
906909 def read (self ):
@@ -979,7 +982,7 @@ def run(self):
979982 def __init__ (self , certificate = None , ssl_version = None ,
980983 certreqs = None , cacerts = None ,
981984 chatty = True , connectionchatty = False , starttls_server = False ,
982- ciphers = None , context = None ):
985+ npn_protocols = None , ciphers = None , context = None ):
983986 if context :
984987 self .context = context
985988 else :
@@ -992,6 +995,8 @@ def __init__(self, certificate=None, ssl_version=None,
992995 self .context .load_verify_locations (cacerts )
993996 if certificate :
994997 self .context .load_cert_chain (certificate )
998+ if npn_protocols :
999+ self .context .set_npn_protocols (npn_protocols )
9951000 if ciphers :
9961001 self .context .set_ciphers (ciphers )
9971002 self .chatty = chatty
@@ -1001,6 +1006,7 @@ def __init__(self, certificate=None, ssl_version=None,
10011006 self .port = support .bind_port (self .sock )
10021007 self .flag = None
10031008 self .active = False
1009+ self .selected_protocols = []
10041010 self .conn_errors = []
10051011 threading .Thread .__init__ (self )
10061012 self .daemon = True
@@ -1195,6 +1201,7 @@ def server_params_test(client_context, server_context, indata=b"FOO\n",
11951201 Launch a server, connect a client to it and try various reads
11961202 and writes.
11971203 """
1204+ stats = {}
11981205 server = ThreadedEchoServer (context = server_context ,
11991206 chatty = chatty ,
12001207 connectionchatty = False )
@@ -1220,12 +1227,14 @@ def server_params_test(client_context, server_context, indata=b"FOO\n",
12201227 if connectionchatty :
12211228 if support .verbose :
12221229 sys .stdout .write (" client: closing connection.\n " )
1223- stats = {
1230+ stats . update ( {
12241231 'compression' : s .compression (),
12251232 'cipher' : s .cipher (),
1226- }
1233+ 'client_npn_protocol' : s .selected_npn_protocol ()
1234+ })
12271235 s .close ()
1228- return stats
1236+ stats ['server_npn_protocols' ] = server .selected_protocols
1237+ return stats
12291238
12301239 def try_protocol_combo (server_protocol , client_protocol , expect_success ,
12311240 certsreqs = None , server_options = 0 , client_options = 0 ):
@@ -1853,6 +1862,43 @@ def test_dh_params(self):
18531862 if "ADH" not in parts and "EDH" not in parts and "DHE" not in parts :
18541863 self .fail ("Non-DH cipher: " + cipher [0 ])
18551864
1865+ def test_selected_npn_protocol (self ):
1866+ # selected_npn_protocol() is None unless NPN is used
1867+ context = ssl .SSLContext (ssl .PROTOCOL_TLSv1 )
1868+ context .load_cert_chain (CERTFILE )
1869+ stats = server_params_test (context , context ,
1870+ chatty = True , connectionchatty = True )
1871+ self .assertIs (stats ['client_npn_protocol' ], None )
1872+
1873+ @unittest .skipUnless (ssl .HAS_NPN , "NPN support needed for this test" )
1874+ def test_npn_protocols (self ):
1875+ server_protocols = ['http/1.1' , 'spdy/2' ]
1876+ protocol_tests = [
1877+ (['http/1.1' , 'spdy/2' ], 'http/1.1' ),
1878+ (['spdy/2' , 'http/1.1' ], 'http/1.1' ),
1879+ (['spdy/2' , 'test' ], 'spdy/2' ),
1880+ (['abc' , 'def' ], 'abc' )
1881+ ]
1882+ for client_protocols , expected in protocol_tests :
1883+ server_context = ssl .SSLContext (ssl .PROTOCOL_TLSv1 )
1884+ server_context .load_cert_chain (CERTFILE )
1885+ server_context .set_npn_protocols (server_protocols )
1886+ client_context = ssl .SSLContext (ssl .PROTOCOL_TLSv1 )
1887+ client_context .load_cert_chain (CERTFILE )
1888+ client_context .set_npn_protocols (client_protocols )
1889+ stats = server_params_test (client_context , server_context ,
1890+ chatty = True , connectionchatty = True )
1891+
1892+ msg = "failed trying %s (s) and %s (c).\n " \
1893+ "was expecting %s, but got %%s from the %%s" \
1894+ % (str (server_protocols ), str (client_protocols ),
1895+ str (expected ))
1896+ client_result = stats ['client_npn_protocol' ]
1897+ self .assertEqual (client_result , expected , msg % (client_result , "client" ))
1898+ server_result = stats ['server_npn_protocols' ][- 1 ] \
1899+ if len (stats ['server_npn_protocols' ]) else 'nothing'
1900+ self .assertEqual (server_result , expected , msg % (server_result , "server" ))
1901+
18561902
18571903def test_main (verbose = False ):
18581904 if support .verbose :
0 commit comments