/* * Copyright (c) 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.eclipse.jetty.spdy.http; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.util.List; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import org.eclipse.jetty.npn.NextProtoNego; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.spdy.AsyncConnectionFactory; import org.eclipse.jetty.spdy.SPDYServerConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestWatchman; import org.junit.runners.model.FrameworkMethod; public class ProtocolNegotiationTest { @Rule public final TestWatchman testName = new TestWatchman() { @Override public void starting(FrameworkMethod method) { super.starting(method); System.err.printf("Running %s.%s()%n", method.getMethod().getDeclaringClass().getName(), method.getName()); } }; protected Server server; protected SPDYServerConnector connector; protected InetSocketAddress startServer(SPDYServerConnector connector) throws Exception { server = new Server(); if (connector == null) connector = new SPDYServerConnector(null, newSslContextFactory()); connector.setPort(0); this.connector = connector; server.addConnector(connector); server.start(); return new InetSocketAddress("localhost", connector.getLocalPort()); } protected SslContextFactory newSslContextFactory() { SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setTrustStore("src/test/resources/truststore.jks"); sslContextFactory.setTrustStorePassword("storepwd"); sslContextFactory.setProtocol("TLSv1"); sslContextFactory.setIncludeProtocols("TLSv1"); return sslContextFactory; } @Test public void testServerAdvertisingHTTPSpeaksHTTP() throws Exception { InetSocketAddress address = startServer(null); connector.removeAsyncConnectionFactory("spdy/2"); connector.putAsyncConnectionFactory("http/1.1", new ServerHTTPAsyncConnectionFactory(connector)); SslContextFactory sslContextFactory = newSslContextFactory(); sslContextFactory.start(); SSLContext sslContext = sslContextFactory.getSslContext(); SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()); client.setUseClientMode(true); client.setSoTimeout(5000); NextProtoNego.put(client, new NextProtoNego.ClientProvider() { @Override public boolean supports() { return true; } @Override public void unsupported() { } @Override public String selectProtocol(List<String> strings) { Assert.assertNotNull(strings); Assert.assertEquals(1, strings.size()); String protocol = strings.get(0); Assert.assertEquals("http/1.1", protocol); return protocol; } }); client.startHandshake(); // Verify that the server really speaks http/1.1 OutputStream output = client.getOutputStream(); output.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost:" + address.getPort() + "\r\n" + "\r\n" + "").getBytes("UTF-8")); output.flush(); InputStream input = client.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8")); String line = reader.readLine(); Assert.assertTrue(line.contains(" 404 ")); client.close(); } @Test public void testServerAdvertisingSPDYAndHTTPSpeaksHTTPWhenNegotiated() throws Exception { InetSocketAddress address = startServer(null); connector.putAsyncConnectionFactory("http/1.1", new ServerHTTPAsyncConnectionFactory(connector)); SslContextFactory sslContextFactory = newSslContextFactory(); sslContextFactory.start(); SSLContext sslContext = sslContextFactory.getSslContext(); SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()); client.setUseClientMode(true); client.setSoTimeout(5000); NextProtoNego.put(client, new NextProtoNego.ClientProvider() { @Override public boolean supports() { return true; } @Override public void unsupported() { } @Override public String selectProtocol(List<String> strings) { Assert.assertNotNull(strings); Assert.assertEquals(2, strings.size()); String spdyProtocol = strings.get(0); Assert.assertEquals("spdy/2", spdyProtocol); String httpProtocol = strings.get(1); Assert.assertEquals("http/1.1", httpProtocol); return httpProtocol; } }); client.startHandshake(); // Verify that the server really speaks http/1.1 OutputStream output = client.getOutputStream(); output.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost:" + address.getPort() + "\r\n" + "\r\n" + "").getBytes("UTF-8")); output.flush(); InputStream input = client.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8")); String line = reader.readLine(); Assert.assertTrue(line.contains(" 404 ")); client.close(); } @Test public void testServerAdvertisingSPDYAndHTTPSpeaksDefaultProtocolWhenNPNMissing() throws Exception { InetSocketAddress address = startServer(new SPDYServerConnector(null, newSslContextFactory()) { @Override protected AsyncConnectionFactory getDefaultAsyncConnectionFactory() { return new ServerHTTPAsyncConnectionFactory(connector); } }); connector.putAsyncConnectionFactory("http/1.1", new ServerHTTPAsyncConnectionFactory(connector)); SslContextFactory sslContextFactory = newSslContextFactory(); sslContextFactory.start(); SSLContext sslContext = sslContextFactory.getSslContext(); SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket(address.getAddress(), address.getPort()); client.setUseClientMode(true); client.setSoTimeout(5000); NextProtoNego.put(client, new NextProtoNego.ClientProvider() { @Override public boolean supports() { return false; } @Override public void unsupported() { } @Override public String selectProtocol(List<String> strings) { return null; } }); client.startHandshake(); // Verify that the server really speaks http/1.1 OutputStream output = client.getOutputStream(); output.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost:" + address.getPort() + "\r\n" + "\r\n" + "").getBytes("UTF-8")); output.flush(); InputStream input = client.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8")); String line = reader.readLine(); Assert.assertTrue(line.contains(" 404 ")); client.close(); } }