| // |
| // ======================================================================== |
| // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.server.ssl; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.Socket; |
| import java.nio.charset.StandardCharsets; |
| import java.security.cert.X509Certificate; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Queue; |
| |
| import javax.net.ssl.SNIHostName; |
| import javax.net.ssl.SNIServerName; |
| import javax.net.ssl.SSLParameters; |
| import javax.net.ssl.SSLSocket; |
| import javax.net.ssl.SSLSocketFactory; |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.eclipse.jetty.http.HttpVersion; |
| import org.eclipse.jetty.io.Connection; |
| import org.eclipse.jetty.server.HttpConfiguration; |
| import org.eclipse.jetty.server.HttpConnectionFactory; |
| import org.eclipse.jetty.server.Request; |
| import org.eclipse.jetty.server.SecureRequestCustomizer; |
| import org.eclipse.jetty.server.Server; |
| import org.eclipse.jetty.server.ServerConnector; |
| import org.eclipse.jetty.server.SocketCustomizationListener; |
| import org.eclipse.jetty.server.SslConnectionFactory; |
| import org.eclipse.jetty.server.handler.AbstractHandler; |
| import org.eclipse.jetty.util.ConcurrentArrayQueue; |
| import org.eclipse.jetty.util.IO; |
| import org.eclipse.jetty.util.ssl.SslContextFactory; |
| import org.hamcrest.Matchers; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class SslConnectionFactoryTest |
| { |
| Server _server; |
| ServerConnector _connector; |
| int _port; |
| |
| @Before |
| public void before() throws Exception |
| { |
| String keystorePath = "src/test/resources/keystore"; |
| File keystoreFile = new File(keystorePath); |
| if (!keystoreFile.exists()) |
| { |
| throw new FileNotFoundException(keystoreFile.getAbsolutePath()); |
| } |
| |
| _server = new Server(); |
| |
| HttpConfiguration http_config = new HttpConfiguration(); |
| http_config.setSecureScheme("https"); |
| http_config.setSecurePort(8443); |
| http_config.setOutputBufferSize(32768); |
| HttpConfiguration https_config = new HttpConfiguration(http_config); |
| https_config.addCustomizer(new SecureRequestCustomizer()); |
| |
| |
| SslContextFactory sslContextFactory = new SslContextFactory(); |
| sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath()); |
| sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); |
| sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); |
| |
| ServerConnector https = _connector = new ServerConnector(_server, |
| new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()), |
| new HttpConnectionFactory(https_config)); |
| https.setPort(0); |
| https.setIdleTimeout(30000); |
| |
| _server.addConnector(https); |
| |
| _server.setHandler(new AbstractHandler() |
| { |
| @Override |
| public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException |
| { |
| response.setStatus(200); |
| response.getWriter().write("url="+request.getRequestURI()+"\nhost="+request.getServerName()); |
| response.flushBuffer(); |
| } |
| }); |
| |
| _server.start(); |
| _port=https.getLocalPort(); |
| } |
| |
| @After |
| public void after() throws Exception |
| { |
| _server.stop(); |
| _server=null; |
| } |
| |
| @Test |
| public void testConnect() throws Exception |
| { |
| String response= getResponse("127.0.0.1",null); |
| Assert.assertThat(response,Matchers.containsString("host=127.0.0.1")); |
| } |
| |
| @Test |
| public void testSNIConnect() throws Exception |
| { |
| String response; |
| |
| response= getResponse("localhost","localhost","jetty.eclipse.org"); |
| Assert.assertThat(response,Matchers.containsString("host=localhost")); |
| } |
| |
| |
| private String getResponse(String host,String cn) throws Exception |
| { |
| String response = getResponse(host,host,cn); |
| Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 200 OK")); |
| Assert.assertThat(response,Matchers.containsString("url=/ctx/path")); |
| return response; |
| } |
| |
| @Test |
| public void testBadHandshake() throws Exception |
| { |
| try(Socket socket=new Socket("127.0.0.1", _port); OutputStream out = socket.getOutputStream()) |
| { |
| out.write("Rubbish".getBytes()); |
| out.flush(); |
| |
| Assert.assertThat(socket.getInputStream().read(),Matchers.equalTo(-1)); |
| } |
| |
| } |
| |
| private String getResponse(String sniHost,String reqHost, String cn) throws Exception |
| { |
| SslContextFactory clientContextFactory = new SslContextFactory(true); |
| clientContextFactory.start(); |
| SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory(); |
| |
| SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port); |
| |
| if (cn!=null) |
| { |
| SNIHostName serverName = new SNIHostName(sniHost); |
| List<SNIServerName> serverNames = new ArrayList<>(); |
| serverNames.add(serverName); |
| |
| SSLParameters params = sslSocket.getSSLParameters(); |
| params.setServerNames(serverNames); |
| sslSocket.setSSLParameters(params); |
| } |
| sslSocket.startHandshake(); |
| |
| |
| if (cn!=null) |
| { |
| X509Certificate cert = ((X509Certificate)sslSocket.getSession().getPeerCertificates()[0]); |
| |
| Assert.assertThat(cert.getSubjectX500Principal().getName("CANONICAL"), Matchers.startsWith("cn="+cn)); |
| } |
| |
| sslSocket.getOutputStream().write(("GET /ctx/path HTTP/1.0\r\nHost: "+reqHost+":"+_port+"\r\n\r\n").getBytes(StandardCharsets.ISO_8859_1)); |
| String response = IO.toString(sslSocket.getInputStream()); |
| |
| sslSocket.close(); |
| clientContextFactory.stop(); |
| return response; |
| } |
| |
| |
| @Test |
| public void testSocketCustomization() throws Exception |
| { |
| final Queue<String> history = new ConcurrentArrayQueue<>(); |
| |
| _connector.addBean(new SocketCustomizationListener() |
| { |
| @Override |
| protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl) |
| { |
| history.add("customize connector "+connection+","+ssl); |
| } |
| }); |
| |
| _connector.getBean(SslConnectionFactory.class).addBean(new SocketCustomizationListener() |
| { |
| @Override |
| protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl) |
| { |
| history.add("customize ssl "+connection+","+ssl); |
| } |
| }); |
| |
| _connector.getBean(HttpConnectionFactory.class).addBean(new SocketCustomizationListener() |
| { |
| @Override |
| protected void customize(Socket socket, Class<? extends Connection> connection, boolean ssl) |
| { |
| history.add("customize http "+connection+","+ssl); |
| } |
| }); |
| |
| String response= getResponse("127.0.0.1",null); |
| Assert.assertThat(response,Matchers.containsString("host=127.0.0.1")); |
| |
| Assert.assertEquals("customize connector class org.eclipse.jetty.io.ssl.SslConnection,false",history.poll()); |
| Assert.assertEquals("customize ssl class org.eclipse.jetty.io.ssl.SslConnection,false",history.poll()); |
| Assert.assertEquals("customize connector class org.eclipse.jetty.server.HttpConnection,true",history.poll()); |
| Assert.assertEquals("customize http class org.eclipse.jetty.server.HttpConnection,true",history.poll()); |
| Assert.assertEquals(0,history.size()); |
| } |
| |
| } |