Return-Path: Delivered-To: apmail-harmony-commits-archive@www.apache.org Received: (qmail 27456 invoked from network); 3 Jul 2007 06:08:48 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 3 Jul 2007 06:08:48 -0000 Received: (qmail 1346 invoked by uid 500); 3 Jul 2007 06:08:51 -0000 Delivered-To: apmail-harmony-commits-archive@harmony.apache.org Received: (qmail 1259 invoked by uid 500); 3 Jul 2007 06:08:51 -0000 Mailing-List: contact commits-help@harmony.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@harmony.apache.org Delivered-To: mailing list commits@harmony.apache.org Received: (qmail 1248 invoked by uid 99); 3 Jul 2007 06:08:51 -0000 Received: from herse.apache.org (HELO herse.apache.org) (140.211.11.133) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 02 Jul 2007 23:08:51 -0700 X-ASF-Spam-Status: No, hits=-99.5 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 02 Jul 2007 23:08:45 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id 9F6171A981A; Mon, 2 Jul 2007 23:08:25 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r552679 [1/2] - in /harmony/enhanced/classlib/trunk/modules/luni: ./ src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/ src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/ src/test/api/common/org/apache... Date: Tue, 03 Jul 2007 06:08:25 -0000 To: commits@harmony.apache.org From: pyang@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20070703060825.9F6171A981A@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: pyang Date: Mon Jul 2 23:08:23 2007 New Revision: 552679 URL: http://svn.apache.org/viewvc?view=rev&rev=552679 Log: Apply patch for HARMONY-617([classlib][luni] HttpURLConnection does not support persistent connections) Added: harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java harmony/enhanced/classlib/trunk/modules/luni/src/test/impl/ harmony/enhanced/classlib/trunk/modules/luni/src/test/impl/org/ harmony/enhanced/classlib/trunk/modules/luni/src/test/impl/org/apache/ harmony/enhanced/classlib/trunk/modules/luni/src/test/impl/org/apache/harmony/ harmony/enhanced/classlib/trunk/modules/luni/src/test/impl/org/apache/harmony/tests/ harmony/enhanced/classlib/trunk/modules/luni/src/test/impl/org/apache/harmony/tests/internal/ harmony/enhanced/classlib/trunk/modules/luni/src/test/impl/org/apache/harmony/tests/internal/net/ harmony/enhanced/classlib/trunk/modules/luni/src/test/impl/org/apache/harmony/tests/internal/net/www/ harmony/enhanced/classlib/trunk/modules/luni/src/test/impl/org/apache/harmony/tests/internal/net/www/protocol/ harmony/enhanced/classlib/trunk/modules/luni/src/test/impl/org/apache/harmony/tests/internal/net/www/protocol/http/ harmony/enhanced/classlib/trunk/modules/luni/src/test/impl/org/apache/harmony/tests/internal/net/www/protocol/http/PersistenceTest.java Modified: harmony/enhanced/classlib/trunk/modules/luni/.classpath harmony/enhanced/classlib/trunk/modules/luni/build.xml harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java Modified: harmony/enhanced/classlib/trunk/modules/luni/.classpath URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/.classpath?view=diff&rev=552679&r1=552678&r2=552679 ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/.classpath (original) +++ harmony/enhanced/classlib/trunk/modules/luni/.classpath Mon Jul 2 23:08:23 2007 @@ -6,6 +6,7 @@ + Modified: harmony/enhanced/classlib/trunk/modules/luni/build.xml URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/build.xml?view=diff&rev=552679&r1=552678&r2=552679 ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/build.xml (original) +++ harmony/enhanced/classlib/trunk/modules/luni/build.xml Mon Jul 2 23:08:23 2007 @@ -283,6 +283,7 @@ + Added: harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java?view=auto&rev=552679 ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java (added) +++ harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java Mon Jul 2 23:08:23 2007 @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.harmony.luni.internal.net.www.protocol.http; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.SocketAddress; +import java.net.URI; + +import org.apache.harmony.luni.util.Msg; + +/** + * An HttpConfiguration contains all the details needed to create an http connection + * and to compare whether or not two connections are the same. An HttpConfiguration + * will either consist of a Proxy or a port number (int) + * and host name (String) or all three, depending on whether or not a + * Proxy is used and the type of Proxy it is. + * + * HttpConfiguration is used as a key by HttpConnectionManager + * to retrieve HttpConnections from its connection pool. + */ +public class HttpConfiguration { + + private Proxy proxy; + private int hostPort; + private String hostName; + private URI uri; + + public HttpConfiguration(URI uri) { + this.uri = uri; + this.hostName = uri.getHost(); + this.hostPort = uri.getPort(); + if(hostPort == -1) { + if(uri.getScheme().equals("https")) { //$NON-NLS-1$ + hostPort = 443; + } else { + hostPort = 80; + } + } + } + + public HttpConfiguration(URI uri, Proxy proxy) { + this.uri = uri; + this.proxy = proxy; + if (proxy.type() == Proxy.Type.HTTP) { + SocketAddress proxyAddr = proxy.address(); + if (!(proxyAddr instanceof InetSocketAddress)) { + throw new IllegalArgumentException(Msg.getString( + "K0316", proxyAddr.getClass())); //$NON-NLS-1$ + } + InetSocketAddress iProxyAddr = (InetSocketAddress) proxyAddr; + this.hostName = iProxyAddr.getHostName(); + this.hostPort = iProxyAddr.getPort(); + } else { + // using SOCKS proxy + this.hostName = uri.getHost(); + this.hostPort = uri.getPort(); + if(hostPort == -1) { + if(uri.getScheme().equals("https")) { //$NON-NLS-1$ + hostPort = 443; + } else { + hostPort = 80; + } + } + } + this.uri = uri; + SocketAddress proxyAddr = proxy.address(); + if (!(proxyAddr instanceof InetSocketAddress)) { + throw new IllegalArgumentException(Msg.getString( + "K0316", proxyAddr.getClass())); //$NON-NLS-1$ + } + InetSocketAddress iProxyAddr = (InetSocketAddress) proxyAddr; + this.hostName = iProxyAddr.getHostName(); + this.hostPort = iProxyAddr.getPort(); + } + + /** + * Returns true if this configuration uses a Proxy + */ + public boolean usesProxy() { + return proxy != null; + } + + /** + * Returns the Proxy for this configuration, or null if a proxy + * is not used + */ + public Proxy getProxy() { + return proxy; + } + + /** + * Returns the host name for this configuration, or null if an http Proxy is used + */ + public String getHostName() { + return hostName; + } + + /** + * Returns the port for this configuration, or 0 if an http Proxy is used + */ + public int getHostPort() { + return hostPort; + } + + @Override + public boolean equals(Object arg0) { + if(!(arg0 instanceof HttpConfiguration)) { + return false; + } else { + HttpConfiguration config = (HttpConfiguration)arg0; + if(config.proxy != null && proxy != null) { + return config.proxy.equals(proxy) && uri.equals(config.uri); + } + return uri.equals(config.uri); + } + } + + @Override + public int hashCode() { + return uri.hashCode(); + } + +} \ No newline at end of file Added: harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java?view=auto&rev=552679 ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java (added) +++ harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java Mon Jul 2 23:08:23 2007 @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.harmony.luni.internal.net.www.protocol.http; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Socket; +import java.net.SocketException; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +import org.apache.harmony.luni.internal.nls.Messages; + +/** + * An HttpConnection represents a persistent http or https connection and contains + * various utility methods to access that connection. + */ +public class HttpConnection { + + private boolean usingSecureSocket = false; + + private Socket socket; + private SSLSocket sslSocket; + + private InputStream inputStream; + private OutputStream outputStream; + private InputStream sslInputStream; + private OutputStream sslOutputStream; + + private HttpConfiguration config; + + public HttpConnection(HttpConfiguration config, int connectTimeout) throws IOException { + this.config = config; + String hostName = config.getHostName(); + int hostPort = config.getHostPort(); + Proxy proxy = config.getProxy(); + if(proxy == null || proxy.type() == Proxy.Type.HTTP) { + socket = new Socket(); + } else { + socket = new Socket(proxy); + } + socket.connect(new InetSocketAddress(hostName, hostPort), connectTimeout); + } + + public void closeSocketAndStreams() { + if(usingSecureSocket) { + if (null != sslOutputStream) { + OutputStream temp = sslOutputStream; + sslOutputStream = null; + try { + temp.close(); + } catch (Exception ex) { + // ignored + } + } + + if (null != sslInputStream) { + InputStream temp = sslInputStream; + sslInputStream = null; + try { + temp.close(); + } catch (Exception ex) { + // ignored + } + } + + if (null != sslSocket) { + Socket temp = sslSocket; + sslSocket = null; + try { + temp.close(); + } catch (Exception ex) { + // ignored + } + } + } + if (null != outputStream) { + OutputStream temp = outputStream; + outputStream = null; + try { + temp.close(); + } catch (Exception ex) { + // ignored + } + } + + if (null != inputStream) { + InputStream temp = inputStream; + inputStream = null; + try { + temp.close(); + } catch (Exception ex) { + // ignored + } + } + + if (null != socket) { + Socket temp = socket; + socket = null; + try { + temp.close(); + } catch (Exception ex) { + // ignored + } + } + } + + public void setSoTimeout(int readTimeout) throws SocketException { + socket.setSoTimeout(readTimeout); + } + + public OutputStream getOutputStream() throws IOException { + if(usingSecureSocket) { + if (sslOutputStream == null) { + sslOutputStream = sslSocket.getOutputStream(); + } + return sslOutputStream; + } else if(outputStream == null) { + outputStream = socket.getOutputStream(); + } + return outputStream; + } + + public InputStream getInputStream() throws IOException { + if(usingSecureSocket) { + if (sslInputStream == null) { + sslInputStream = sslSocket.getInputStream(); + } + return sslInputStream; + } else if(inputStream == null) { + inputStream = socket.getInputStream(); + } + return inputStream; + } + + public HttpConfiguration getHttpConfiguration() { + return config; + } + + public SSLSocket getSecureSocket(SSLSocketFactory sslSocketFactory, HostnameVerifier hostnameVerifier) throws IOException { + if(!usingSecureSocket) { + String hostName = config.getHostName(); + int port = config.getHostPort(); + // create the wrapper over connected socket + sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, + hostName, port, true); + sslSocket.setUseClientMode(true); + sslSocket.startHandshake(); + if (!hostnameVerifier.verify(hostName, sslSocket.getSession())) { + throw new IOException(Messages.getString("luni.02", hostName)); //$NON-NLS-1$ + } + usingSecureSocket = true; + } + return sslSocket; + } + + Socket getSocket() { + return socket; + } + + /* + * This method has been copied from the Apache Jakarta Commons HttpClient project + * http://svn.apache.org/repos/asf/jakarta/commons/proper/httpclient/trunk/HttpClient/src/java/org/apache/commons/httpclient/HttpConnection.java r480424 + */ + protected boolean isStale() throws IOException { + boolean isStale = true; + if (!socket.isClosed()) { + // the socket is open, but could still have been closed from the other end + isStale = false; + try { + if (inputStream.available() <= 0) { + int soTimeout = socket.getSoTimeout(); + try { + socket.setSoTimeout(1); + inputStream.mark(1); + int byteRead = inputStream.read(); + if (byteRead == -1) { + // again - if the socket is reporting all data read, + // probably stale + isStale = true; + } else { + inputStream.reset(); + } + } finally { + socket.setSoTimeout(soTimeout); + } + } + } catch (InterruptedIOException e) { + if (!isSocketTimeoutException(e)) { + throw e; + } + // aha - the connection is NOT stale - continue on! + } catch (IOException e) { + // oops - the connection is stale, the read or soTimeout failed. + isStale = true; + } + } + + return isStale; + } + + /* + * This field has been copied from the Apache Jakarta Commons HttpClient project + * http://svn.apache.org/repos/asf/jakarta/commons/proper/httpclient/trunk/HttpClient/src/java/org/apache/commons/httpclient/HttpConnection.java r480424 + */ + static private final Class SOCKET_TIMEOUT_CLASS = SocketTimeoutExceptionClass(); + + /* + * This method has been copied from the Apache Jakarta Commons HttpClient project + * http://svn.apache.org/repos/asf/jakarta/commons/proper/httpclient/trunk/HttpClient/src/java/org/apache/commons/httpclient/HttpConnection.java r480424 + */ + public static boolean isSocketTimeoutException(final InterruptedIOException e) { + if (SOCKET_TIMEOUT_CLASS != null) { + return SOCKET_TIMEOUT_CLASS.isInstance(e); + } else { + return true; + } + } + + /* + * This method has been copied from the Apache Jakarta Commons HttpClient project + * http://svn.apache.org/repos/asf/jakarta/commons/proper/httpclient/trunk/HttpClient/src/java/org/apache/commons/httpclient/HttpConnection.java r480424 + */ + static private Class SocketTimeoutExceptionClass() { + try { + return Class.forName("java.net.SocketTimeoutException"); + } catch (ClassNotFoundException e) { + return null; + } + } + +} \ No newline at end of file Added: harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java?view=auto&rev=552679 ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java (added) +++ harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java Mon Jul 2 23:08:23 2007 @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.harmony.luni.internal.net.www.protocol.http; + +import java.io.IOException; +import java.net.Proxy; +import java.net.URI; +import java.security.AccessController; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.harmony.luni.util.PriviAction; + +/** + * HttpConnectionManager manages a pool of HttpConnections + * that are not currently in use and is used to get hold of persistent HttpConnections. + * Clients should return an HttpConnection to the pool after use by calling + * returnConnectionToPool + * + * Two system properties affect the behaviour of this class - http.maxConnections + * and http.keepAlive. http.keepAlive determines whether + * or not connections should be persisted and http.maxConnections + * determines the maximum number of connections to each individual host that + * should be kept in the pool. + */ +public class HttpConnectionManager { + + // The maximum number of connections to any location + private static int maxConnections = 5; + + // Keeps connections alive if true + private static boolean keepAlive = true; + + private static HttpConnectionManager defaultConnectionManager; + private ConnectionPool pool = new ConnectionPool(); + + /** + * Returns the default connection manager + */ + public static HttpConnectionManager getDefault() { + if(defaultConnectionManager == null) { + defaultConnectionManager = new HttpConnectionManager(); + } + return defaultConnectionManager; + } + + public HttpConnection getConnection(URI uri, int connectTimeout) throws IOException { + checkSystemProperties(); + HttpConfiguration config = new HttpConfiguration(uri); + return pool.getHttpConnection(config, connectTimeout); + } + + public HttpConnection getConnection(URI uri, Proxy proxy, int connectTimeout) throws IOException { + checkSystemProperties(); + HttpConfiguration config = new HttpConfiguration(uri, proxy); + return pool.getHttpConnection(config, connectTimeout); + } + + public void returnConnectionToPool(HttpConnection connection) { + checkSystemProperties(); + pool.returnConnection(connection); + } + + public int numFreeConnections() { + return pool.numFreeConnections(); + } + + private void checkSystemProperties() { + String httpMaxConnections = AccessController.doPrivileged(new PriviAction("http.maxConnections")); + String httpKeepAlive = AccessController.doPrivileged(new PriviAction("http.keepAlive")); + if(httpMaxConnections != null) { + maxConnections = Integer.parseInt(httpMaxConnections); + } + if(httpKeepAlive != null) { + keepAlive = Boolean.parseBoolean(httpKeepAlive); + if(!keepAlive) { + pool.clear(); + } + } + } + + private static class ConnectionPool { + + private Map> freeConnectionMap = new HashMap>(); // Map of free Sockets + + public synchronized void clear() { + for (Iterator> iter = freeConnectionMap.values().iterator(); iter.hasNext();) { + List connections = iter.next(); + for (Iterator iterator = connections.iterator(); iterator.hasNext();) { + HttpConnection connection = iterator.next(); + connection.closeSocketAndStreams(); + } + } + freeConnectionMap.clear(); + } + + public synchronized void returnConnection(HttpConnection connection) { + if(!connection.getSocket().isClosed() && keepAlive) { + HttpConfiguration config = connection.getHttpConfiguration(); + List connections = freeConnectionMap.get(config); + if(connections == null) { + connections = new ArrayList(); + freeConnectionMap.put(config, connections); + } + if(connections.size() < HttpConnectionManager.maxConnections) { + if(!connections.contains(connection)) { + connections.add(connection); + } + } else { + connection.closeSocketAndStreams(); + } + } else { + // Make sure all streams are closed etc. + connection.closeSocketAndStreams(); + } + } + + public synchronized HttpConnection getHttpConnection(HttpConfiguration config, int connectTimeout) throws IOException { + List connections = freeConnectionMap.get(config); + if(keepAlive && connections == null) { + connections = new ArrayList(); + freeConnectionMap.put(config, connections); + } + if(!keepAlive || connections.isEmpty()) { + HttpConnection connection = new HttpConnection(config, connectTimeout); + return connection; + } else { + HttpConnection connection = connections.get(0); + connections.remove(0); + if(!connection.isStale()) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkConnect(connection.getSocket().getInetAddress().getHostName(), connection.getSocket().getPort()); + } + return connection; + } else { + return getHttpConnection(config, connectTimeout); + } + } + } + + public int numFreeConnections() { + int numFree = 0; + for (Iterator> iter = freeConnectionMap.values().iterator(); iter.hasNext();) { + List connections = iter.next(); + numFree += connections.size(); + } + return numFree; + } + } + + public void reset() { + pool.clear(); + } + +} \ No newline at end of file Modified: harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java?view=diff&rev=552679&r1=552678&r2=552679 ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java (original) +++ harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java Mon Jul 2 23:08:23 2007 @@ -17,7 +17,6 @@ package org.apache.harmony.luni.internal.net.www.protocol.http; -import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -33,8 +32,6 @@ import java.net.Proxy; import java.net.ProxySelector; import java.net.ResponseCache; -import java.net.Socket; -import java.net.SocketAddress; import java.net.SocketPermission; import java.net.URI; import java.net.URISyntaxException; @@ -74,12 +71,12 @@ private int httpVersion = 1; // Assume HTTP/1.1 + protected HttpConnection connection; + private InputStream is; private InputStream uis; - protected Socket socket; - private OutputStream socketOut; private OutputStream cacheOut; @@ -130,8 +127,12 @@ @Override public void close() throws IOException { - bytesRemaining = 0; - closeSocket(); + if(bytesRemaining > 0) { + bytesRemaining = 0; + disconnect(true); // Should close the socket if client hasn't read all the data + } else { + disconnect(false); + } /* * if user has set useCache to true and cache exists, aborts it when * closing @@ -153,6 +154,7 @@ @Override public int read() throws IOException { if (bytesRemaining <= 0) { + disconnect(false); return -1; } int result = is.read(); @@ -162,6 +164,9 @@ cacheOut.write(result); } bytesRemaining--; + if (bytesRemaining <= 0) { + disconnect(false); + } return result; } @@ -176,6 +181,7 @@ throw new ArrayIndexOutOfBoundsException(); } if (bytesRemaining <= 0) { + disconnect(false); return -1; } if (length > bytesRemaining) { @@ -190,11 +196,15 @@ cacheOut.write(buf, offset, result); } } + if (bytesRemaining <= 0) { + disconnect(false); + } return result; } public long skip(int amount) throws IOException { if (bytesRemaining <= 0) { + disconnect(false); return -1; } if (amount > bytesRemaining) { @@ -204,6 +214,9 @@ if (result > 0) { bytesRemaining -= result; } + if (bytesRemaining <= 0) { + disconnect(false); + } return result; } } @@ -219,8 +232,12 @@ @Override public void close() throws IOException { + if(!atEnd && available() > 0) { + disconnect(true); + } else { + disconnect(false); + } atEnd = true; - closeSocket(); // if user has set useCache to true and cache exists, abort if (useCaches && null != cacheRequest) { cacheRequest.abort(); @@ -261,6 +278,7 @@ readChunkSize(); } if (atEnd) { + disconnect(false); return -1; } bytesRemaining--; @@ -286,6 +304,7 @@ readChunkSize(); } if (atEnd) { + disconnect(false); return -1; } if (length > bytesRemaining) { @@ -305,6 +324,7 @@ public long skip(int amount) throws IOException { if (atEnd) { + disconnect(false); return -1; } if (bytesRemaining <= 0) { @@ -428,6 +448,7 @@ } sendCache(closed); } + disconnect(false); } @Override @@ -534,8 +555,8 @@ protected HttpURLConnection(URL url, int port) { super(url); defaultPort = port; - reqHeader = (Header) defaultReqHeader.clone(); + try { uri = url.toURI(); } catch (URISyntaxException e) { @@ -584,14 +605,19 @@ if (getFromCache()) { return; } + try { + uri = url.toURI(); + } catch (URISyntaxException e1) { + // ignore + } // socket to be used for connection - Socket socket = null; + connection = null; // try to determine: to use the proxy or not if (proxy != null) { // try to make the connection to the proxy // specified in constructor. // IOException will be thrown in the case of failure - socket = getHTTPConnection(proxy); + connection = getHTTPConnection(proxy); } else { // Use system-wide ProxySelect to select proxy list, // then try to connect via elements in the proxy list. @@ -604,7 +630,7 @@ continue; } try { - socket = getHTTPConnection(selectedProxy); + connection = getHTTPConnection(selectedProxy); proxy = selectedProxy; break; // connected } catch (IOException e) { @@ -614,64 +640,38 @@ } } } - if (socket == null) { + if (connection == null) { // make direct connection - socket = getHTTPConnection(null); + connection = getHTTPConnection(null); } - socket.setSoTimeout(getReadTimeout()); - setUpTransportIO(socket); + connection.setSoTimeout(getReadTimeout()); + setUpTransportIO(connection); connected = true; } /** - * Returns connected socket to be used for this HTTP connection. TODO: - * implement persistent connections. + * Returns connected socket to be used for this HTTP connection. */ - protected Socket getHTTPConnection(Proxy proxy) throws IOException { - Socket socket; + protected HttpConnection getHTTPConnection(Proxy proxy) throws IOException { + HttpConnection connection; if (proxy == null || proxy.type() == Proxy.Type.DIRECT) { - this.proxy = null; // not using proxy - socket = new Socket(); - socket.connect(new InetSocketAddress(getHostName(), getHostPort()), - getConnectTimeout()); - } else if (proxy.type() == Proxy.Type.HTTP) { - socket = new Socket(); - - SocketAddress proxyAddr = proxy.address(); - - if (!(proxyAddr instanceof InetSocketAddress)) { - throw new IllegalArgumentException(Msg.getString( - "K0316", proxyAddr.getClass())); //$NON-NLS-1$ - } - - InetSocketAddress iProxyAddr = (InetSocketAddress) proxyAddr; - - if( iProxyAddr.getAddress() == null ) { - // Resolve proxy, see HARMONY-3113 - socket.connect(new InetSocketAddress((iProxyAddr.getHostName()), - iProxyAddr.getPort()), getConnectTimeout()); - } else { - socket.connect(iProxyAddr, getConnectTimeout()); - } + this.proxy = null; // not using proxy + connection = HttpConnectionManager.getDefault().getConnection(uri, getConnectTimeout()); } else { - // using SOCKS proxy - socket = new Socket(proxy); - socket.connect(new InetSocketAddress(getHostName(), getHostPort()), - getConnectTimeout()); + connection = HttpConnectionManager.getDefault().getConnection(uri, proxy, getConnectTimeout()); } - return socket; + return connection; } /** * Sets up the data streams used to send request[s] and read response[s]. * - * @param socket - * socket to be used for connection + * @param connection + * HttpConnection to be used */ - protected void setUpTransportIO(Socket socket) throws IOException { - this.socket = socket; - socketOut = socket.getOutputStream(); - is = new BufferedInputStream(socket.getInputStream()); + protected void setUpTransportIO(HttpConnection connection) throws IOException { + socketOut = connection.getOutputStream(); + is = connection.getInputStream(); } // Tries to get head and body from cache, return true if has got this time @@ -721,16 +721,17 @@ */ @Override public void disconnect() { - try { - closeSocket(); - } catch (IOException e) { - } + disconnect(true); } - void closeSocket() throws IOException { - if (is != null) { - is.close(); + private void disconnect(boolean closeSocket) { + if(closeSocket && connection != null) { + connection.closeSocketAndStreams(); + } else if (connection != null) { + HttpConnectionManager.getDefault().returnConnectionToPool(connection); + connection = null; } + connection = null; } protected void endRequest() throws IOException { @@ -868,7 +869,6 @@ // connect before sending requests connect(); - doRequest(); /* @@ -1070,7 +1070,7 @@ if (method == HEAD || (responseCode >= 100 && responseCode < 200) || responseCode == HTTP_NO_CONTENT || responseCode == HTTP_NOT_MODIFIED) { - closeSocket(); + disconnect(); uis = new LimitedInputStream(0); } putToCache(); @@ -1163,8 +1163,7 @@ // if we are doing output make sure the appropriate headers are sent if (os != null) { if (reqHeader.get("Content-Type") == null) { //$NON-NLS-1$ - output - .append("Content-Type: application/x-www-form-urlencoded\r\n"); //$NON-NLS-1$ + output.append("Content-Type: application/x-www-form-urlencoded\r\n"); //$NON-NLS-1$ } if (os.isCached()) { if (reqHeader.get("Content-Length") == null) { //$NON-NLS-1$ @@ -1198,8 +1197,7 @@ * if both setFixedLengthStreamingMode and * content-length are set, use fixedContentLength first */ - output - .append((fixedContentLength >= 0) ? String + output.append((fixedContentLength >= 0) ? String .valueOf(fixedContentLength) : reqHeader.get(i)); } else { @@ -1381,7 +1379,7 @@ // drop everything and reconnect, might not be required for // HTTP/1.1 endRequest(); - closeSocket(); + disconnect(); connected = false; String credentials = getAuthorizationCredentials(challenge); if (credentials == null) { @@ -1404,7 +1402,7 @@ // drop everything and reconnect, might not be required for // HTTP/1.1 endRequest(); - closeSocket(); + disconnect(); connected = false; String credentials = getAuthorizationCredentials(challenge); if (credentials == null) { @@ -1450,7 +1448,7 @@ hostPort = -1; } endRequest(); - closeSocket(); + disconnect(); connected = false; continue; } @@ -1467,7 +1465,7 @@ * authorization challenge * * @param challenge - * @return + * @return authorization credentials * @throws IOException */ private String getAuthorizationCredentials(String challenge) @@ -1517,4 +1515,4 @@ } } } -} +} \ No newline at end of file Modified: harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java?view=diff&rev=552679&r1=552678&r2=552679 ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java (original) +++ harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java Mon Jul 2 23:08:23 2007 @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.harmony.luni.internal.net.www.protocol.https; import java.io.IOException; @@ -22,7 +21,6 @@ import java.io.OutputStream; import java.net.ProtocolException; import java.net.Proxy; -import java.net.Socket; import java.net.URL; import java.security.Permission; import java.security.Principal; @@ -387,7 +385,7 @@ responseMessage, responseCode)); } // if there are some remaining data in the stream - read it out - InputStream is = socket.getInputStream(); + InputStream is = connection.getInputStream(); while (is.available() != 0) { is.read(); } @@ -397,7 +395,8 @@ super.connect(); } if (!makingSSLTunnel) { - setUpTransportIO(wrapConnection(socket)); + sslSocket = connection.getSecureSocket(getSSLSocketFactory(), getHostnameVerifier()); + setUpTransportIO(connection); } } @@ -420,21 +419,5 @@ return super.requestString(); } - /** - * Create the secure socket over the connected socket and verify remote - * hostname. - */ - private Socket wrapConnection(Socket socket) throws IOException { - String hostname = url.getHost(); - // create the wrapper over connected socket - sslSocket = (SSLSocket) getSSLSocketFactory().createSocket(socket, - hostname, url.getPort(), true); - sslSocket.setUseClientMode(true); - sslSocket.startHandshake(); - if (!getHostnameVerifier().verify(hostname, sslSocket.getSession())) { - throw new IOException(Messages.getString("luni.02", hostname)); //$NON-NLS-1$ - } - return sslSocket; - } } } Modified: harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java?view=diff&rev=552679&r1=552678&r2=552679 ============================================================================== --- harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java (original) +++ harmony/enhanced/classlib/trunk/modules/luni/src/test/api/common/org/apache/harmony/luni/tests/internal/net/www/protocol/http/HttpURLConnectionTest.java Mon Jul 2 23:08:23 2007 @@ -14,13 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.harmony.luni.tests.internal.net.www.protocol.http; import java.io.IOException; +import java.io.InputStream; 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; @@ -30,17 +31,18 @@ import java.net.SocketTimeoutException; import java.net.URI; import java.net.URL; +import java.security.Permission; import java.util.ArrayList; import junit.framework.TestCase; - /** - * Tests for HTTPURLConnection class constructors and methods. - * + * Tests for HttpURLConnection class constructors and methods. */ public class HttpURLConnectionTest extends TestCase { + private static final boolean DEBUG = false; + private final static Object bound = new Object(); static class MockServer extends Thread { @@ -51,7 +53,7 @@ public MockServer(String name) throws IOException { super(name); serverSocket = new ServerSocket(0); - serverSocket.setSoTimeout(1000); + serverSocket.setSoTimeout(5000); } public int port() { @@ -77,6 +79,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 +213,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") + "HTTP/1.0 407 Proxy authentication required\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 +238,15 @@ } } } - + + public void setUp() { + if (DEBUG) { + System.out.println("\n=============================="); + System.out.println("===== Execution: "+getName()); + System.out.println("=============================="); + } + } + /** * ProxySelector implementation used in the test. */ @@ -256,7 +388,7 @@ server.join(); proxy.join(); - assertTrue("Connection does not use proxy", + assertTrue("Connection does not use proxy", connection.usingProxy()); assertTrue("Proxy server was not used", proxy.accepted); } finally { @@ -274,17 +406,17 @@ "user", "password".toCharArray()); } }); - + try { MockProxyServer proxy = new MockProxyServer("ProxyServer"); URL url = new URL("http://remotehost:55555/requested.data"); - HttpURLConnection connection = + HttpURLConnection connection = (HttpURLConnection) url.openConnection( - new Proxy(Proxy.Type.HTTP, + 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 +431,398 @@ Authenticator.setDefault(null); } } - -} + + /** + * 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) + "'"); + } + } + + public void testSecurityManager() throws MalformedURLException, IOException, InterruptedException { + try { + MockHTTPServer httpServer = + new MockHTTPServer("HTTP Server for persistence checking", 2); + httpServer.start(); + synchronized(bound) { + if (!httpServer.started) { + bound.wait(5000); + } + } + MySecurityManager sm = new MySecurityManager(); + System.setSecurityManager(sm); + + // Check that a first connection calls checkConnect + try { + HttpURLConnection c = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + if (DEBUG) { + System.out.println("Actual connection class: "+c.getClass()); + } + c.connect(); + fail("Should have thrown a SecurityException upon connection"); + + } catch (SecurityException e) { + } + + // Now create a connection properly + System.setSecurityManager(null); + HttpURLConnection c = (HttpURLConnection) + new URL("http://localhost:"+httpServer.port()).openConnection(); + 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; + } + + // Now check that a second connection also calls checkConnect + System.setSecurityManager(sm); + try { + 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; + } + } + fail("Expected a SecurityException to be thrown"); + } catch (SecurityException e) { + } + } finally { + System.setSecurityManager(null); + } + } + + private static class MySecurityManager extends SecurityManager { + + @Override + public void checkConnect(String host, int port) { + throw new SecurityException(); + } + + @Override + public void checkConnect(String host, int port, Object context) { + throw new SecurityException(); + } + + @Override + public void checkPermission(Permission permission) { + // allows a new security manager to be set + } + + } + +} \ No newline at end of file