/* * Copyright (c) 2009-2009 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.client; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.junit.Test; /** * @version $Revision$ $Date$ */ public abstract class AbstractConnectionTest { protected HttpClient newHttpClient() { HttpClient httpClient = new HttpClient(); // httpClient.setConnectorType(HttpClient.CONNECTOR_SOCKET); return httpClient; } protected ServerSocket newServerSocket() throws IOException { ServerSocket serverSocket=new ServerSocket(); serverSocket.bind(null); return serverSocket; } @Test public void testServerClosedConnection() throws Exception { ServerSocket serverSocket = new ServerSocket(); serverSocket.bind(null); int port=serverSocket.getLocalPort(); HttpClient httpClient = newHttpClient(); httpClient.setMaxConnectionsPerAddress(1); httpClient.start(); try { CountDownLatch latch = new CountDownLatch(1); HttpExchange exchange = new ConnectionExchange(latch); exchange.setAddress(new Address("localhost", port)); exchange.setRequestURI("/"); httpClient.send(exchange); Socket remote = serverSocket.accept(); // HttpClient.send() above is async, so if we write the response immediately // there is a chance that it arrives before the request is being sent, so we // read the request before sending the response to avoid the race InputStream input = remote.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8")); String line; while ((line = reader.readLine()) != null) { if (line.length() == 0) break; } OutputStream output = remote.getOutputStream(); output.write("HTTP/1.1 200 OK\r\n".getBytes("UTF-8")); output.write("Content-Length: 0\r\n".getBytes("UTF-8")); output.write("\r\n".getBytes("UTF-8")); output.flush(); assertEquals(HttpExchange.STATUS_COMPLETED, exchange.waitForDone()); remote.close(); // Need to wait a bit to allow the client to detect // that the server has closed the connection Thread.sleep(500); // The server has closed the connection and another attempt to send // with the same connection would fail because the connection has been // closed by the client as well. // The client must open a new connection in this case, and we check // that the new request completes correctly exchange.reset(); httpClient.send(exchange); remote = serverSocket.accept(); input = remote.getInputStream(); reader = new BufferedReader(new InputStreamReader(input, "UTF-8")); while ((line = reader.readLine()) != null) { if (line.length() == 0) break; } output = remote.getOutputStream(); output.write("HTTP/1.1 200 OK\r\n".getBytes("UTF-8")); output.write("Content-Length: 0\r\n".getBytes("UTF-8")); output.write("\r\n".getBytes("UTF-8")); output.flush(); assertEquals(HttpExchange.STATUS_COMPLETED, exchange.waitForDone()); } finally { httpClient.stop(); } } protected String getScheme() { return "http"; } @Test public void testServerClosedIncomplete() throws Exception { ServerSocket serverSocket = newServerSocket(); int port=serverSocket.getLocalPort(); HttpClient httpClient = newHttpClient(); httpClient.setMaxConnectionsPerAddress(1); httpClient.start(); try { CountDownLatch latch = new CountDownLatch(1); HttpExchange exchange = new ConnectionExchange(latch); exchange.setScheme(getScheme()); exchange.setAddress(new Address("localhost", port)); exchange.setRequestURI("/"); httpClient.send(exchange); Socket remote = serverSocket.accept(); // HttpClient.send() above is async, so if we write the response immediately // there is a chance that it arrives before the request is being sent, so we // read the request before sending the response to avoid the race InputStream input = remote.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8")); String line; while ((line = reader.readLine()) != null) { if (line.length() == 0) break; } OutputStream output = remote.getOutputStream(); output.write("HTTP/1.1 200 OK\r\n".getBytes("UTF-8")); output.write("Content-Length: 10\r\n".getBytes("UTF-8")); output.write("\r\n".getBytes("UTF-8")); output.flush(); remote.close(); int status = exchange.waitForDone(); assertEquals(HttpExchange.STATUS_EXCEPTED, status); } finally { httpClient.stop(); } } @Test public void testServerHalfClosedIncomplete() throws Exception { ServerSocket serverSocket = new ServerSocket(); serverSocket.bind(null); int port=serverSocket.getLocalPort(); HttpClient httpClient = newHttpClient(); httpClient.setIdleTimeout(10000); httpClient.setMaxConnectionsPerAddress(1); httpClient.start(); try { CountDownLatch latch = new CountDownLatch(1); HttpExchange exchange = new ConnectionExchange(latch); exchange.setAddress(new Address("localhost", port)); exchange.setRequestURI("/"); httpClient.send(exchange); Socket remote = serverSocket.accept(); // HttpClient.send() above is async, so if we write the response immediately // there is a chance that it arrives before the request is being sent, so we // read the request before sending the response to avoid the race InputStream input = remote.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8")); String line; while ((line = reader.readLine()) != null) { if (line.length() == 0) break; } OutputStream output = remote.getOutputStream(); output.write("HTTP/1.1 200 OK\r\n".getBytes("UTF-8")); output.write("Content-Length: 10\r\n".getBytes("UTF-8")); output.write("\r\n".getBytes("UTF-8")); output.flush(); remote.shutdownOutput(); assertEquals(HttpExchange.STATUS_EXCEPTED, exchange.waitForDone()); } finally { httpClient.stop(); } } @Test public void testConnectionFailed() throws Exception { ServerSocket serverSocket = new ServerSocket(); serverSocket.bind(null); int port=serverSocket.getLocalPort(); serverSocket.close(); HttpClient httpClient = newHttpClient(); httpClient.start(); try { CountDownLatch latch = new CountDownLatch(1); HttpExchange exchange = new ConnectionExchange(latch); exchange.setAddress(new Address("localhost", port)); exchange.setRequestURI("/"); httpClient.send(exchange); boolean passed = latch.await(4000, TimeUnit.MILLISECONDS); assertTrue(passed); long wait = 100; long maxWait = 10 * wait; long curWait = wait; while (curWait < maxWait && !exchange.isDone()) { Thread.sleep(wait); curWait += wait; } assertEquals(HttpExchange.STATUS_EXCEPTED, exchange.getStatus()); } finally { httpClient.stop(); } } @Test public void testMultipleConnectionsFailed() throws Exception { ServerSocket serverSocket = new ServerSocket(); serverSocket.bind(null); int port=serverSocket.getLocalPort(); serverSocket.close(); HttpClient httpClient = newHttpClient(); httpClient.setMaxConnectionsPerAddress(1); httpClient.start(); try { HttpExchange[] exchanges = new HttpExchange[20]; final CountDownLatch latch = new CountDownLatch(exchanges.length); for (int i = 0; i < exchanges.length; ++i) { HttpExchange exchange = new HttpExchange() { @Override protected void onConnectionFailed(Throwable x) { latch.countDown(); } }; exchange.setAddress(new Address("localhost", port)); exchange.setRequestURI("/"); exchanges[i] = exchange; } for (HttpExchange exchange : exchanges) httpClient.send(exchange); for (HttpExchange exchange : exchanges) assertEquals(HttpExchange.STATUS_EXCEPTED, exchange.waitForDone()); assertTrue(latch.await(1000, TimeUnit.MILLISECONDS)); } finally { httpClient.stop(); } } @Test public void testConnectionTimeout() throws Exception { HttpClient httpClient = newHttpClient(); int connectTimeout = 5000; httpClient.setConnectTimeout(connectTimeout); httpClient.start(); try { CountDownLatch latch = new CountDownLatch(1); HttpExchange exchange = new ConnectionExchange(latch); // Using a IP address has a different behavior than using a host name exchange.setAddress(new Address("127.0.0.1", 1)); exchange.setRequestURI("/"); httpClient.send(exchange); boolean passed = latch.await(connectTimeout * 2L, TimeUnit.MILLISECONDS); assertTrue(passed); int status = exchange.waitForDone(); assertEquals(HttpExchange.STATUS_EXCEPTED, status); } finally { httpClient.stop(); } } @Test public void testIdleConnection() throws Exception { ServerSocket serverSocket = new ServerSocket(); serverSocket.bind(null); int port=serverSocket.getLocalPort(); HttpClient httpClient = newHttpClient(); httpClient.setIdleTimeout(700); httpClient.start(); try { HttpExchange exchange = new ConnectionExchange(); exchange.setAddress(new Address("localhost", port)); exchange.setRequestURI("/"); HttpDestination dest = httpClient.getDestination(new Address("localhost", port),false); httpClient.send(exchange); Socket server = serverSocket.accept(); server.setSoTimeout(5000); byte[] buf = new byte[4096]; int len=server.getInputStream().read(buf); assertEquals(1,dest.getConnections()); assertEquals(0,dest.getIdleConnections()); server.getOutputStream().write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".getBytes()); assertEquals(HttpExchange.STATUS_COMPLETED,exchange.waitForDone()); Thread.sleep(200); // TODO get rid of this assertEquals(1,dest.getConnections()); assertEquals(1,dest.getIdleConnections()); exchange = new ConnectionExchange(); exchange.setAddress(new Address("localhost", port)); exchange.setRequestURI("/"); httpClient.send(exchange); assertEquals(1,dest.getConnections()); assertEquals(0,dest.getIdleConnections()); len=server.getInputStream().read(buf); assertEquals(1,dest.getConnections()); assertEquals(0,dest.getIdleConnections()); server.getOutputStream().write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".getBytes()); Thread.sleep(500); assertEquals(1,dest.getConnections()); assertEquals(1,dest.getIdleConnections()); Thread.sleep(500); assertEquals(0,dest.getConnections()); assertEquals(0,dest.getIdleConnections()); serverSocket.close(); } finally { httpClient.stop(); } } protected class ConnectionExchange extends HttpExchange { private final CountDownLatch latch; protected ConnectionExchange() { this.latch = null; } protected ConnectionExchange(CountDownLatch latch) { this.latch = latch; } @Override protected void onConnectionFailed(Throwable ex) { if (latch!=null) latch.countDown(); } @Override protected void onException(Throwable x) { if (latch!=null) latch.countDown(); } } }