Index: src/test/java/org/apache/harmony/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java =================================================================== --- src/test/java/org/apache/harmony/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java (revision 505187) +++ src/test/java/org/apache/harmony/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java (working copy) @@ -18,9 +18,13 @@ package org.apache.harmony.tests.internal.net.www.protocol.http; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; import java.net.Authenticator; import java.net.HttpURLConnection; import java.net.InetSocketAddress; +import java.net.MalformedURLException; import java.net.PasswordAuthentication; import java.net.Proxy; import java.net.ProxySelector; @@ -34,13 +38,19 @@ import junit.framework.TestCase; +import org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection; +import org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnectionManager; + /** - * Tests for HTTPURLConnection class constructors and methods. + * Tests for HttpURLConnection class constructors and methods. * */ public class HttpURLConnectionTest extends TestCase { + // do print debug information + private static final boolean DEBUG = false; + private final static Object bound = new Object(); static class MockServer extends Thread { @@ -51,7 +61,7 @@ public MockServer(String name) throws IOException { super(name); serverSocket = new ServerSocket(0); - serverSocket.setSoTimeout(1000); + serverSocket.setSoTimeout(5000); } public int port() { @@ -77,6 +87,128 @@ } } + static class MockHTTPServer extends MockServer { + // HTTP response codes + static final int OK_CODE = 200; + static final int NOT_FOUND_CODE = 404; + // how many times persistent connection will be used + // by server + int persUses; + // result code to be sent to client + int responseCode; + // response content to be sent to client + String response = ""; + // client's POST message + String clientPost = "Hello from client!"; + + public MockHTTPServer(String name, int persUses) throws IOException { + this(name, persUses, OK_CODE); + } + + public MockHTTPServer(String name, int persUses, + int responseCode) throws IOException { + super(name); + this.persUses = persUses; + this.responseCode = responseCode; + } + + public int port() { + return serverSocket.getLocalPort(); + } + + @Override + public void run() { + try { + synchronized (bound) { + started = true; + bound.notify(); + } + InputStream is = null; + Socket client = null; + try { + client = serverSocket.accept(); + accepted = true; + for (int i=0; i 0)) { + if (bytik == '\r') { + bytik = is.read(); + } + if (wasEOL && (bytik == '\n')) { + break; + } + wasEOL = (bytik == '\n'); + buff[num++] = (byte) bytik; + } + //int num = is.read(buff); + String message = new String(buff, 0, num); + if (DEBUG) { + System.out.println("---- Server got request: ----\n" + + message + "-----------------------------"); + } + + // Act as Server (not Proxy) side + if (message.startsWith("POST")) { + // client connection sent some data + // if the data was not read with header + if (DEBUG) { + System.out.println( + "---- Server read client's data: ----"); + } + num = is.read(buff); + message = new String(buff, 0, num); + if (DEBUG) { + System.out.println("'" + message + "'"); + System.out.println( + "------------------------------------"); + } + // check the received data + assertEquals(clientPost, message); + } + + client.getOutputStream().write(( + "HTTP/1.1 " + responseCode + " OK\n" + + "Content-type: text/html\n" + + "Content-length: " + + response.length() + "\n\n" + + response).getBytes()); + + if (responseCode != OK_CODE) { + // wait while test case check closed connection + // and interrupt this thread + try { + while (!isInterrupted()) { + Thread.sleep(1000); + } + } catch (Exception ignore) { } + } + } + } catch (SocketTimeoutException ignore) { + ignore.printStackTrace(); + } finally { + if (is != null) { + is.close(); + } + if (client != null) { + client.close(); + } + serverSocket.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + static class MockProxyServer extends MockServer { boolean acceptedAuthorizedRequest; @@ -89,18 +221,18 @@ public void run() { try { Socket socket = serverSocket.accept(); - socket.setSoTimeout(1000); + socket.setSoTimeout(5000); byte[] buff = new byte[1024]; int num = socket.getInputStream().read(buff); socket.getOutputStream().write(( "HTTP/1.0 407 Proxy authentication required\n" - + "Proxy-authenticate: Basic realm=\"remotehost\"\n\n") + + "Proxy-authenticate: Basic realm=\"remotehost\"\n\n") .getBytes()); num = socket.getInputStream().read(buff); if (num == -1) { // this connection was closed, create new one: socket = serverSocket.accept(); - socket.setSoTimeout(1000); + socket.setSoTimeout(5000); num = socket.getInputStream().read(buff); } String request = new String(buff, 0, num); @@ -114,7 +246,15 @@ } } } - + + public void setUp() { + if (DEBUG) { + System.out.println("\n=============================="); + System.out.println("===== Execution: "+getName()); + System.out.println("=============================="); + } + } + /** * ProxySelector implementation used in the test. */ @@ -283,8 +423,8 @@ (HttpURLConnection) url.openConnection( new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", proxy.port()))); - connection.setConnectTimeout(1000); - connection.setReadTimeout(1000); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); proxy.start(); @@ -299,5 +439,566 @@ Authenticator.setDefault(null); } } + + /** + * Test that an HTTP connection persists + */ + public void testConnectionsPersist() throws IOException, InterruptedException { + int initialFreeConnections = HttpConnectionManager.getDefault().numFreeConnections(); + MockServer httpServer = + new MockServer("ServerSocket for HttpURLConnectionTest"); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + HttpURLConnection c = (HttpURLConnection) + new URL("http://127.0.0.1:" + httpServer.port()).openConnection(); + c.setDoOutput(true); + c.setRequestMethod("POST"); + c.getOutputStream().close(); + assertEquals(initialFreeConnections + 1, HttpConnectionManager.getDefault().numFreeConnections()); + c = (HttpURLConnection) + new URL("http://127.0.0.1:" + httpServer.port()).openConnection(); + c.setDoOutput(true); + c.setRequestMethod("POST"); + OutputStream os = c.getOutputStream(); + assertEquals(initialFreeConnections, HttpConnectionManager.getDefault().numFreeConnections()); + os.close(); + assertEquals(initialFreeConnections + 1, HttpConnectionManager.getDefault().numFreeConnections()); + httpServer.join(); + } + /** + * Test that multiple HTTP connections persist + */ + public void testMultipleConnectionsPersist() throws IOException, InterruptedException { + int initialFreeConnections = HttpConnectionManager.getDefault().numFreeConnections(); + MockServer httpServer = + new MockServer("ServerSocket for HttpURLConnectionTest"); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + MockServer httpServer2 = + new MockServer("ServerSocket for HttpURLConnectionTest"); + httpServer2.start(); + synchronized(bound) { + if (!httpServer2.started) { + bound.wait(5000); + } + } + HttpURLConnection c = (HttpURLConnection) + new URL("http://127.0.0.1:" + httpServer.port()).openConnection(); + c.setDoOutput(true); + c.setRequestMethod("POST"); + OutputStream os = c.getOutputStream(); + HttpURLConnection c2 = (HttpURLConnection) + new URL("http://127.0.0.1:" + httpServer2.port()).openConnection(); + c2.setDoOutput(true); + c2.setRequestMethod("POST"); + OutputStream os2 = c2.getOutputStream(); + os.close(); + os2.close(); + assertEquals(initialFreeConnections + 2, HttpConnectionManager.getDefault().numFreeConnections()); + + c = (HttpURLConnection) + new URL("http://127.0.0.1:" + httpServer.port()).openConnection(); + c.setDoOutput(true); + c.setRequestMethod("POST"); + os = c.getOutputStream(); + assertEquals(initialFreeConnections + 1, HttpConnectionManager.getDefault().numFreeConnections()); + c2 = (HttpURLConnection) + new URL("http://127.0.0.1:" + httpServer2.port()).openConnection(); + c2.setDoOutput(true); + c2.setRequestMethod("POST"); + os2 = c2.getOutputStream(); + assertEquals(initialFreeConnections, HttpConnectionManager.getDefault().numFreeConnections()); + os.close(); + os2.close(); + assertEquals(initialFreeConnections + 2, HttpConnectionManager.getDefault().numFreeConnections()); + httpServer.join(); + httpServer2.join(); + } + + /** + * Test that a closed HTTP connection is not kept in the pool of live connections + */ + public void testForcedClosure() throws IOException, InterruptedException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException { + int initialFreeConnections = HttpConnectionManager.getDefault().numFreeConnections(); + MockServer httpServer = + new MockServer("ServerSocket for HttpURLConnectionTest"); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + HttpConnection connection = HttpConnectionManager.getDefault().getConnection("127.0.0.1", httpServer.port(), 1000); + HttpConnectionManager.getDefault().returnConnectionToPool(connection); + assertEquals(initialFreeConnections + 1, HttpConnectionManager.getDefault().numFreeConnections()); + HttpURLConnection c = (HttpURLConnection) + new URL("http://127.0.0.1:" + httpServer.port()).openConnection(); + c.setDoOutput(true); + c.setRequestMethod("POST"); + c.getOutputStream(); + assertEquals(initialFreeConnections, HttpConnectionManager.getDefault().numFreeConnections()); + c.disconnect(); + assertEquals(initialFreeConnections, HttpConnectionManager.getDefault().numFreeConnections()); + } + + /** + * Test that a connection is closed if the client does not read all the data + */ + public void testIncorrectUsage() throws MalformedURLException, IOException { + int initialFreeConnections = HttpConnectionManager.getDefault().numFreeConnections(); + HttpURLConnection c = (HttpURLConnection) + new URL("http://www.google.com").openConnection(); + c.setDoOutput(true); + c.setRequestMethod("GET"); + InputStream is = c.getInputStream(); // get the input stream but don't finish reading it + is.close(); + assertEquals(initialFreeConnections, HttpConnectionManager.getDefault().numFreeConnections()); + } + + /** + * Test that a connection is not closed if the client reads all the data + * but not closes input stream. read until -1. + */ + public void testConnectionPersistence() throws Exception { + MockHTTPServer httpServer = + new MockHTTPServer("HTTP Server for persistence checking", 2); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + + HttpURLConnection c = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + if (DEBUG) { + System.out.println("Actual connection class: "+c.getClass()); + } + + c.setDoInput(true); + c.setConnectTimeout(5000); + c.setReadTimeout(5000); + InputStream is = c.getInputStream(); + byte[] buffer = new byte[128]; + int totalBytes = 0; + int bytesRead = 0; + while((bytesRead = is.read(buffer)) > 0){ + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer, 0, bytesRead) + "'"); + } + totalBytes += bytesRead; + } + + HttpURLConnection c2 = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + c2.setDoInput(true); + c2.setConnectTimeout(5000); + c2.setReadTimeout(5000); + is = c2.getInputStream(); + buffer = new byte[128]; + totalBytes = 0; + bytesRead = 0; + while((bytesRead = is.read(buffer)) > 0){ + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer, 0, bytesRead) + "'"); + totalBytes += bytesRead; + } + } + } + + /** + * Test that a connection is not closed if the client reads all the data + * but not closes input stream. read() not receives -1. + */ + public void testConnectionPersistence2() throws Exception { + MockHTTPServer httpServer = + new MockHTTPServer("HTTP Server for persistence checking", 2); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + + HttpURLConnection c = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + if (DEBUG) { + System.out.println("Actual connection class: "+c.getClass()); + } + + c.setDoInput(true); + c.setConnectTimeout(5000); + c.setReadTimeout(5000); + InputStream is = c.getInputStream(); + int bytes2Read = httpServer.response.length(); + byte[] buffer = new byte[httpServer.response.length()]; + while((bytes2Read -= is.read(buffer)) > 0) { } + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer) + "'"); + } + + HttpURLConnection c2 = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + c2.setDoInput(true); + c2.setConnectTimeout(5000); + c2.setReadTimeout(5000); + is = c2.getInputStream(); + buffer = new byte[httpServer.response.length()]; + bytes2Read = httpServer.response.length(); + while((bytes2Read -= is.read(buffer)) > 0) { } + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer) + "'"); + } + } + + /** + * Test that a connection is not closed if it firstly does POST, + * and then does GET requests. + */ + public void testConnectionPersistence3() throws Exception { + MockHTTPServer httpServer = + new MockHTTPServer("HTTP Server for persistence checking", 2); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + + HttpURLConnection c = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + if (DEBUG) { + System.out.println("Actual connection class: "+c.getClass()); + } + + c.setDoInput(true); + c.setDoOutput(true); + c.setConnectTimeout(5000); + c.setReadTimeout(5000); + c.getOutputStream().write(httpServer.clientPost.getBytes()); + + InputStream is = c.getInputStream(); + int bytes2Read = httpServer.response.length(); + byte[] buffer = new byte[httpServer.response.length()]; + while((bytes2Read -= is.read(buffer)) > 0) { } + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer) + "'"); + } + + HttpURLConnection c2 = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + c2.setDoInput(true); + c2.setConnectTimeout(5000); + c2.setReadTimeout(5000); + is = c2.getInputStream(); + buffer = new byte[httpServer.response.length()]; + bytes2Read = httpServer.response.length(); + while((bytes2Read -= is.read(buffer)) > 0) { } + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer) + "'"); + } + } + + /** + * Test that a connection is not closed if it firstly does GET, + * and then does POST requests. + */ + public void testConnectionPersistence4() throws Exception { + MockHTTPServer httpServer = + new MockHTTPServer("HTTP Server for persistence checking", 2); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + + HttpURLConnection c = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + if (DEBUG) { + System.out.println("Actual connection class: "+c.getClass()); + } + + c.setDoInput(true); + c.setConnectTimeout(5000); + c.setReadTimeout(5000); + + InputStream is = c.getInputStream(); + int bytes2Read = httpServer.response.length(); + byte[] buffer = new byte[httpServer.response.length()]; + while((bytes2Read = is.read(buffer)) > 0) { } + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer) + "'"); + } + + HttpURLConnection c2 = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + c2.setDoOutput(true); + c2.setDoInput(true); + c2.setConnectTimeout(5000); + c2.setReadTimeout(5000); + c2.getOutputStream().write(httpServer.clientPost.getBytes()); + is = c2.getInputStream(); + buffer = new byte[httpServer.response.length()]; + bytes2Read = httpServer.response.length(); + while((bytes2Read = is.read(buffer)) > 0) { } + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer) + "'"); + } + } + + /** + * Test that a connection is not closed if it does POST for 2 times. + */ + public void testConnectionPersistence5() throws Exception { + MockHTTPServer httpServer = + new MockHTTPServer("HTTP Server for persistence checking", 2); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + + HttpURLConnection c = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + if (DEBUG) { + System.out.println("Actual connection class: "+c.getClass()); + } + c.setDoOutput(true); + c.setDoInput(true); + c.setConnectTimeout(5000); + c.setReadTimeout(5000); + c.getOutputStream().write(httpServer.clientPost.getBytes()); + InputStream is = c.getInputStream(); + int bytes2Read = httpServer.response.length(); + byte[] buffer = new byte[httpServer.response.length()]; + while((bytes2Read = is.read(buffer)) > 0) { } + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer) + "'"); + } + + HttpURLConnection c2 = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + c2.setDoOutput(true); + c2.setDoInput(true); + c2.setConnectTimeout(5000); + c2.setReadTimeout(5000); + c2.getOutputStream().write(httpServer.clientPost.getBytes()); + is = c2.getInputStream(); + buffer = new byte[httpServer.response.length()]; + bytes2Read = httpServer.response.length(); + while((bytes2Read = is.read(buffer)) > 0) { } + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer) + "'"); + } + } + + /** + * Test that a connection made through proxy will be reused + * for connection establishing without proxy. + */ + public void testProxiedConnectionPersistence() throws Exception { + MockHTTPServer httpServer = + new MockHTTPServer("HTTP Server for persistence checking", 2); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + + HttpURLConnection c = (HttpURLConnection) + new URL("http://some.host:1234") + .openConnection(new Proxy(Proxy.Type.HTTP, + new InetSocketAddress("localhost", + httpServer.port()))); + if (DEBUG) { + System.out.println("Actual connection class: "+c.getClass()); + } + c.setDoOutput(true); + c.setDoInput(true); + c.setConnectTimeout(5000); + c.setReadTimeout(5000); + c.getOutputStream().write(httpServer.clientPost.getBytes()); + InputStream is = c.getInputStream(); + int bytes2Read = httpServer.response.length(); + byte[] buffer = new byte[httpServer.response.length()]; + while((bytes2Read = is.read(buffer)) > 0) { } + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer) + "'"); + } + + HttpURLConnection c2 = (HttpURLConnection) + new URL("http://some.host:1234").openConnection(); + c2.setDoOutput(true); + c2.setDoInput(true); + c2.setConnectTimeout(5000); + c2.setReadTimeout(5000); + c2.getOutputStream().write(httpServer.clientPost.getBytes()); + is = c2.getInputStream(); + buffer = new byte[httpServer.response.length()]; + bytes2Read = httpServer.response.length(); + while((bytes2Read = is.read(buffer)) > 0) { } + if (DEBUG) { + System.out.println("Client got response: '" + + new String(buffer) + "'"); + } + } + + /** + * Test that a connection is closed in case of unsuccessful connection. + * Here client gets NOT_FOUND response. + */ + public void testConnectionNonPersistence() throws Exception { + MockHTTPServer httpServer = + new MockHTTPServer("HTTP Server for NOT FOUND checking", 1, + MockHTTPServer.NOT_FOUND_CODE); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + + int initialFreeConnections + = HttpConnectionManager.getDefault().numFreeConnections(); + + HttpURLConnection c = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + if (DEBUG) { + System.out.println("Actual connection class: "+c.getClass()); + } + + c.setDoInput(true); + c.setConnectTimeout(5000); + c.setReadTimeout(5000); + try { + InputStream is = c.getInputStream(); + fail("Expected IOException was not thrown"); + } catch (IOException expected) { + // expected + } finally { + httpServer.interrupt(); + } + assertEquals("Unsuccessful connection was not closed", + initialFreeConnections, + HttpConnectionManager.getDefault().numFreeConnections()); + } + + /** + * Test that a connection is not closed if the client does read all the data + * @throws IOException + * @throws MalformedURLException + */ + public void testCorrectUsage() throws MalformedURLException, IOException { + int initialFreeConnections = HttpConnectionManager.getDefault().numFreeConnections(); + HttpURLConnection c = (HttpURLConnection) + new URL("http://www.google.com").openConnection(); + c.setDoOutput(true); + c.setRequestMethod("GET"); + InputStream is = c.getInputStream(); + byte[] buffer = new byte[128]; + int totalBytes = 0; + int bytesRead = 0; + while((bytesRead = is.read(buffer)) > 0){ + totalBytes += bytesRead; + } + is.close(); + assertEquals(initialFreeConnections + 1, HttpConnectionManager.getDefault().numFreeConnections()); + + HttpURLConnection c2 = (HttpURLConnection) + new URL("http://www.google.com").openConnection(); + c2.setDoOutput(true); + c2.setRequestMethod("GET"); + InputStream is2 = c2.getInputStream(); + byte[] buffer2 = new byte[128]; + int totalBytes2 = 0; + int bytesRead2 = 0; + while((bytesRead2 = is2.read(buffer2)) > 0){ + totalBytes2 += bytesRead2; + } + is2.close(); + assertEquals(initialFreeConnections + 1, HttpConnectionManager.getDefault().numFreeConnections()); + assertEquals(totalBytes, totalBytes2); + } + + /** + * Test that the http.keepAlive system property has the required effect on persistent connections + */ + public void testKeepAliveSystemProperty() throws IOException, InterruptedException { + System.setProperty("http.keepAlive", "false"); + MockServer httpServer = + new MockServer("ServerSocket for HttpURLConnectionTest"); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + HttpURLConnection c = (HttpURLConnection) + new URL("http://127.0.0.1:" + httpServer.port()).openConnection(); + c.setDoOutput(true); + c.setRequestMethod("POST"); + OutputStream os = c.getOutputStream(); + os.close(); + assertEquals(0, HttpConnectionManager.getDefault().numFreeConnections()); + httpServer.join(); + System.setProperty("http.keepAlive", "true"); + } + + /** + * Test that the http.maxConnections system property has the required effect on persistent connections + */ + public void testMaxConnectionsSystemProperty() throws MalformedURLException, IOException { + int initialFreeConnections = HttpConnectionManager.getDefault().numFreeConnections(); + System.setProperty("http.maxConnections", "2"); + HttpURLConnection c = (HttpURLConnection) + new URL("http://www.apache.org").openConnection(); + c.setDoOutput(true); + c.setRequestMethod("GET"); + InputStream is = c.getInputStream(); + c = (HttpURLConnection) + new URL("http://www.apache.org").openConnection(); + c.setDoOutput(true); + c.setRequestMethod("GET"); + InputStream is2 = c.getInputStream(); + c = (HttpURLConnection) + new URL("http://www.apache.org").openConnection(); + c.setDoOutput(true); + c.setRequestMethod("GET"); + InputStream is3 = c.getInputStream(); + byte[] buffer = new byte[128]; + while(is.read(buffer) > 0){ + } + while(is2.read(buffer) > 0){ + } + while(is3.read(buffer) > 0){ + } + is.close(); + is2.close(); + is3.close(); + assertEquals(initialFreeConnections + 2, HttpConnectionManager.getDefault().numFreeConnections()); + } + }