--- a/jdk/src/share/classes/java/net/SocketInputStream.java Fri Mar 12 09:03:02 2010 -0800
+++ b/jdk/src/share/classes/java/net/SocketInputStream.java Fri Mar 12 09:06:00 2010 -0800
@@ -118,6 +118,10 @@
* @exception IOException If an I/O error has occurred.
*/
public int read(byte b[], int off, int length) throws IOException {
+ return read(b, off, length, impl.getTimeout());
+ }
+
+ int read(byte b[], int off, int length, int timeout) throws IOException {
int n;
// EOF already encountered
@@ -143,7 +147,7 @@
// acquire file descriptor and do the read
FileDescriptor fd = impl.acquireFD();
try {
- n = socketRead0(fd, b, off, length, impl.getTimeout());
+ n = socketRead0(fd, b, off, length, timeout);
if (n > 0) {
return n;
}
@@ -161,7 +165,7 @@
impl.setConnectionResetPending();
impl.acquireFD();
try {
- n = socketRead0(fd, b, off, length, impl.getTimeout());
+ n = socketRead0(fd, b, off, length, timeout);
if (n > 0) {
return n;
}
--- a/jdk/src/share/classes/java/net/SocksSocketImpl.java Fri Mar 12 09:03:02 2010 -0800
+++ b/jdk/src/share/classes/java/net/SocksSocketImpl.java Fri Mar 12 09:06:00 2010 -0800
@@ -98,11 +98,31 @@
super.connect(new InetSocketAddress(host, port), timeout);
}
+ private static int remainingMillis(long deadlineMillis) throws IOException {
+ if (deadlineMillis == 0L)
+ return 0;
+
+ final long remaining = deadlineMillis - System.currentTimeMillis();
+ if (remaining > 0)
+ return (int) remaining;
+
+ throw new SocketTimeoutException();
+ }
+
private int readSocksReply(InputStream in, byte[] data) throws IOException {
+ return readSocksReply(in, data, 0L);
+ }
+
+ private int readSocksReply(InputStream in, byte[] data, long deadlineMillis) throws IOException {
int len = data.length;
int received = 0;
for (int attempts = 0; received < len && attempts < 3; attempts++) {
- int count = in.read(data, received, len - received);
+ int count;
+ try {
+ count = ((SocketInputStream)in).read(data, received, len - received, remainingMillis(deadlineMillis));
+ } catch (SocketTimeoutException e) {
+ throw new SocketTimeoutException("Connect timed out");
+ }
if (count < 0)
throw new SocketException("Malformed reply from SOCKS server");
received += count;
@@ -115,6 +135,12 @@
*/
private boolean authenticate(byte method, InputStream in,
BufferedOutputStream out) throws IOException {
+ return authenticate(method, in, out, 0L);
+ }
+
+ private boolean authenticate(byte method, InputStream in,
+ BufferedOutputStream out,
+ long deadlineMillis) throws IOException {
// No Authentication required. We're done then!
if (method == NO_AUTH)
return true;
@@ -162,7 +188,7 @@
out.write(0);
out.flush();
byte[] data = new byte[2];
- int i = readSocksReply(in, data);
+ int i = readSocksReply(in, data, deadlineMillis);
if (i != 2 || data[1] != 0) {
/* RFC 1929 specifies that the connection MUST be closed if
authentication fails */
@@ -201,18 +227,18 @@
// out.write(outToken);
// out.flush();
// data = new byte[2];
-// i = readSocksReply(in, data);
+// i = readSocksReply(in, data, deadlineMillis);
// if (i != 2 || data[1] == 0xff) {
// in.close();
// out.close();
// return false;
// }
-// i = readSocksReply(in, data);
+// i = readSocksReply(in, data, deadlineMillis);
// int len = 0;
// len = ((int)data[0] & 0xff) << 8;
// len += data[1];
// data = new byte[len];
-// i = readSocksReply(in, data);
+// i = readSocksReply(in, data, deadlineMillis);
// if (i == len)
// return true;
// in.close();
@@ -231,7 +257,8 @@
}
private void connectV4(InputStream in, OutputStream out,
- InetSocketAddress endpoint) throws IOException {
+ InetSocketAddress endpoint,
+ long deadlineMillis) throws IOException {
if (!(endpoint.getAddress() instanceof Inet4Address)) {
throw new SocketException("SOCKS V4 requires IPv4 only addresses");
}
@@ -249,7 +276,7 @@
out.write(0);
out.flush();
byte[] data = new byte[8];
- int n = readSocksReply(in, data);
+ int n = readSocksReply(in, data, deadlineMillis);
if (n != 8)
throw new SocketException("Reply from SOCKS server has bad length: " + n);
if (data[0] != 0 && data[0] != 4)
@@ -296,6 +323,15 @@
*/
@Override
protected void connect(SocketAddress endpoint, int timeout) throws IOException {
+ final long deadlineMillis;
+
+ if (timeout == 0) {
+ deadlineMillis = 0L;
+ } else {
+ long finish = System.currentTimeMillis() + timeout;
+ deadlineMillis = finish < 0 ? Long.MAX_VALUE : finish;
+ }
+
SecurityManager security = System.getSecurityManager();
if (endpoint == null || !(endpoint instanceof InetSocketAddress))
throw new IllegalArgumentException("Unsupported address type");
@@ -322,7 +358,7 @@
/*
* No default proxySelector --> direct connection
*/
- super.connect(epoint, timeout);
+ super.connect(epoint, remainingMillis(deadlineMillis));
return;
}
URI uri;
@@ -345,13 +381,13 @@
java.util.Iterator<Proxy> iProxy = null;
iProxy = sel.select(uri).iterator();
if (iProxy == null || !(iProxy.hasNext())) {
- super.connect(epoint, timeout);
+ super.connect(epoint, remainingMillis(deadlineMillis));
return;
}
while (iProxy.hasNext()) {
p = iProxy.next();
if (p == null || p == Proxy.NO_PROXY) {
- super.connect(epoint, timeout);
+ super.connect(epoint, remainingMillis(deadlineMillis));
return;
}
if (p.type() != Proxy.Type.SOCKS)
@@ -364,7 +400,7 @@
// Connects to the SOCKS server
try {
- privilegedConnect(server, serverPort, timeout);
+ privilegedConnect(server, serverPort, remainingMillis(deadlineMillis));
// Worked, let's get outta here
break;
} catch (IOException e) {
@@ -388,7 +424,7 @@
} else {
// Connects to the SOCKS server
try {
- privilegedConnect(server, serverPort, timeout);
+ privilegedConnect(server, serverPort, remainingMillis(deadlineMillis));
} catch (IOException e) {
throw new SocketException(e.getMessage());
}
@@ -403,7 +439,7 @@
// DOMAIN type of addresses (unresolved addresses here)
if (epoint.isUnresolved())
throw new UnknownHostException(epoint.toString());
- connectV4(in, out, epoint);
+ connectV4(in, out, epoint, deadlineMillis);
return;
}
@@ -414,7 +450,7 @@
out.write(USER_PASSW);
out.flush();
byte[] data = new byte[2];
- int i = readSocksReply(in, data);
+ int i = readSocksReply(in, data, deadlineMillis);
if (i != 2 || ((int)data[0]) != PROTO_VERS) {
// Maybe it's not a V5 sever after all
// Let's try V4 before we give up
@@ -422,12 +458,12 @@
// DOMAIN type of addresses (unresolved addresses here)
if (epoint.isUnresolved())
throw new UnknownHostException(epoint.toString());
- connectV4(in, out, epoint);
+ connectV4(in, out, epoint, deadlineMillis);
return;
}
if (((int)data[1]) == NO_METHODS)
throw new SocketException("SOCKS : No acceptable methods");
- if (!authenticate(data[1], in, out)) {
+ if (!authenticate(data[1], in, out, deadlineMillis)) {
throw new SocketException("SOCKS : authentication failed");
}
out.write(PROTO_VERS);
@@ -457,7 +493,7 @@
}
out.flush();
data = new byte[4];
- i = readSocksReply(in, data);
+ i = readSocksReply(in, data, deadlineMillis);
if (i != 4)
throw new SocketException("Reply from SOCKS server has bad length");
SocketException ex = null;
@@ -469,33 +505,33 @@
switch(data[3]) {
case IPV4:
addr = new byte[4];
- i = readSocksReply(in, addr);
+ i = readSocksReply(in, addr, deadlineMillis);
if (i != 4)
throw new SocketException("Reply from SOCKS server badly formatted");
data = new byte[2];
- i = readSocksReply(in, data);
+ i = readSocksReply(in, data, deadlineMillis);
if (i != 2)
throw new SocketException("Reply from SOCKS server badly formatted");
break;
case DOMAIN_NAME:
len = data[1];
byte[] host = new byte[len];
- i = readSocksReply(in, host);
+ i = readSocksReply(in, host, deadlineMillis);
if (i != len)
throw new SocketException("Reply from SOCKS server badly formatted");
data = new byte[2];
- i = readSocksReply(in, data);
+ i = readSocksReply(in, data, deadlineMillis);
if (i != 2)
throw new SocketException("Reply from SOCKS server badly formatted");
break;
case IPV6:
len = data[1];
addr = new byte[len];
- i = readSocksReply(in, addr);
+ i = readSocksReply(in, addr, deadlineMillis);
if (i != len)
throw new SocketException("Reply from SOCKS server badly formatted");
data = new byte[2];
- i = readSocksReply(in, data);
+ i = readSocksReply(in, data, deadlineMillis);
if (i != 2)
throw new SocketException("Reply from SOCKS server badly formatted");
break;
--- a/jdk/src/share/classes/java/util/zip/ZipFile.java Fri Mar 12 09:03:02 2010 -0800
+++ b/jdk/src/share/classes/java/util/zip/ZipFile.java Fri Mar 12 09:06:00 2010 -0800
@@ -36,6 +36,8 @@
import java.util.Set;
import java.util.HashSet;
import java.util.NoSuchElementException;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
import static java.util.zip.ZipConstants64.*;
/**
@@ -78,6 +80,17 @@
private static native void initIDs();
+ private static final boolean usemmap;
+
+ static {
+ // A system prpperty to disable mmap use to avoid vm crash when
+ // in-use zip file is accidently overwritten by others.
+ String prop = AccessController.doPrivileged(
+ new GetPropertyAction("sun.zip.disableMemoryMapping"));
+ usemmap = (prop == null ||
+ !(prop.length() == 0 || prop.equalsIgnoreCase("true")));
+ }
+
/**
* Opens a zip file for reading.
*
@@ -196,7 +209,7 @@
throw new NullPointerException("charset is null");
this.zc = ZipCoder.get(charset);
long t0 = System.nanoTime();
- jzfile = open(name, mode, file.lastModified());
+ jzfile = open(name, mode, file.lastModified(), usemmap);
sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
sun.misc.PerfCounter.getZipFileCount().increment();
this.name = name;
@@ -673,8 +686,8 @@
}
- private static native long open(String name, int mode, long lastModified)
- throws IOException;
+ private static native long open(String name, int mode, long lastModified,
+ boolean usemmap) throws IOException;
private static native int getTotal(long jzfile);
private static native int read(long jzfile, long jzentry,
long pos, byte[] b, int off, int len);
--- a/jdk/src/share/native/java/util/zip/ZipFile.c Fri Mar 12 09:03:02 2010 -0800
+++ b/jdk/src/share/native/java/util/zip/ZipFile.c Fri Mar 12 09:06:00 2010 -0800
@@ -81,7 +81,8 @@
JNIEXPORT jlong JNICALL
Java_java_util_zip_ZipFile_open(JNIEnv *env, jclass cls, jstring name,
- jint mode, jlong lastModified)
+ jint mode, jlong lastModified,
+ jboolean usemmap)
{
const char *path = JNU_GetStringPlatformChars(env, name, 0);
char *msg = 0;
@@ -109,7 +110,7 @@
goto finally;
}
#endif
- zip = ZIP_Put_In_Cache(path, zfd, &msg, lastModified);
+ zip = ZIP_Put_In_Cache0(path, zfd, &msg, lastModified, usemmap);
}
if (zip != 0) {
--- a/jdk/src/share/native/java/util/zip/zip_util.c Fri Mar 12 09:03:02 2010 -0800
+++ b/jdk/src/share/native/java/util/zip/zip_util.c Fri Mar 12 09:06:00 2010 -0800
@@ -251,11 +251,16 @@
if (zip->lock != NULL) MDESTROY(zip->lock);
free(zip->name);
freeCEN(zip);
+
#ifdef USE_MMAP
- if (zip->maddr != NULL) munmap((char *)zip->maddr, zip->mlen);
-#else
- free(zip->cencache.data);
+ if (zip->usemmap) {
+ if (zip->maddr != NULL)
+ munmap((char *)zip->maddr, zip->mlen);
+ } else
#endif
+ {
+ free(zip->cencache.data);
+ }
if (zip->comment != NULL)
free(zip->comment);
if (zip->zfd != -1) ZFILE_Close(zip->zfd);
@@ -585,49 +590,53 @@
ZIP_FORMAT_ERROR("invalid END header (bad central directory offset)");
#ifdef USE_MMAP
- /* On Solaris & Linux prior to JDK 6, we used to mmap the whole jar file to
- * read the jar file contents. However, this greatly increased the perceived
- * footprint numbers because the mmap'ed pages were adding into the totals shown
- * by 'ps' and 'top'. We switched to mmaping only the central directory of jar
- * file while calling 'read' to read the rest of jar file. Here are a list of
- * reasons apart from above of why we are doing so:
- * 1. Greatly reduces mmap overhead after startup complete;
- * 2. Avoids dual path code maintainance;
- * 3. Greatly reduces risk of address space (not virtual memory) exhaustion.
- */
- if (pagesize == 0) {
- pagesize = (jlong)sysconf(_SC_PAGESIZE);
- if (pagesize == 0) goto Catch;
- }
- if (cenpos > pagesize) {
- offset = cenpos & ~(pagesize - 1);
- } else {
- offset = 0;
+ if (zip->usemmap) {
+ /* On Solaris & Linux prior to JDK 6, we used to mmap the whole jar file to
+ * read the jar file contents. However, this greatly increased the perceived
+ * footprint numbers because the mmap'ed pages were adding into the totals shown
+ * by 'ps' and 'top'. We switched to mmaping only the central directory of jar
+ * file while calling 'read' to read the rest of jar file. Here are a list of
+ * reasons apart from above of why we are doing so:
+ * 1. Greatly reduces mmap overhead after startup complete;
+ * 2. Avoids dual path code maintainance;
+ * 3. Greatly reduces risk of address space (not virtual memory) exhaustion.
+ */
+ if (pagesize == 0) {
+ pagesize = (jlong)sysconf(_SC_PAGESIZE);
+ if (pagesize == 0) goto Catch;
+ }
+ if (cenpos > pagesize) {
+ offset = cenpos & ~(pagesize - 1);
+ } else {
+ offset = 0;
+ }
+ /* When we are not calling recursively, knownTotal is -1. */
+ if (knownTotal == -1) {
+ void* mappedAddr;
+ /* Mmap the CEN and END part only. We have to figure
+ out the page size in order to make offset to be multiples of
+ page size.
+ */
+ zip->mlen = cenpos - offset + cenlen + endhdrlen;
+ zip->offset = offset;
+ mappedAddr = mmap64(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, (off64_t) offset);
+ zip->maddr = (mappedAddr == (void*) MAP_FAILED) ? NULL :
+ (unsigned char*)mappedAddr;
+
+ if (zip->maddr == NULL) {
+ jio_fprintf(stderr, "mmap failed for CEN and END part of zip file\n");
+ goto Catch;
+ }
+ }
+ cenbuf = zip->maddr + cenpos - offset;
+ } else
+#endif
+ {
+ if ((cenbuf = malloc((size_t) cenlen)) == NULL ||
+ (readFullyAt(zip->zfd, cenbuf, cenlen, cenpos) == -1))
+ goto Catch;
}
- /* When we are not calling recursively, knownTotal is -1. */
- if (knownTotal == -1) {
- void* mappedAddr;
- /* Mmap the CEN and END part only. We have to figure
- out the page size in order to make offset to be multiples of
- page size.
- */
- zip->mlen = cenpos - offset + cenlen + endhdrlen;
- zip->offset = offset;
- mappedAddr = mmap64(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, (off64_t) offset);
- zip->maddr = (mappedAddr == (void*) MAP_FAILED) ? NULL :
- (unsigned char*)mappedAddr;
- if (zip->maddr == NULL) {
- jio_fprintf(stderr, "mmap failed for CEN and END part of zip file\n");
- goto Catch;
- }
- }
- cenbuf = zip->maddr + cenpos - offset;
-#else
- if ((cenbuf = malloc((size_t) cenlen)) == NULL ||
- (readFullyAt(zip->zfd, cenbuf, cenlen, cenpos) == -1))
- goto Catch;
-#endif
cenend = cenbuf + cenlen;
/* Initialize zip file data structures based on the total number
@@ -700,9 +709,11 @@
cenpos = -1;
Finally:
-#ifndef USE_MMAP
- free(cenbuf);
+#ifdef USE_MMAP
+ if (!zip->usemmap)
#endif
+ free(cenbuf);
+
return cenpos;
}
@@ -782,9 +793,17 @@
* If a zip error occurs, then *pmsg will be set to the error message text if
* pmsg != 0. Otherwise, *pmsg will be set to NULL.
*/
+
jzfile *
ZIP_Put_In_Cache(const char *name, ZFILE zfd, char **pmsg, jlong lastModified)
{
+ return ZIP_Put_In_Cache0(name, zfd, pmsg, lastModified, JNI_TRUE);
+}
+
+jzfile *
+ZIP_Put_In_Cache0(const char *name, ZFILE zfd, char **pmsg, jlong lastModified,
+ jboolean usemmap)
+{
static char errbuf[256];
jlong len;
jzfile *zip;
@@ -793,6 +812,9 @@
return NULL;
}
+#ifdef USE_MMAP
+ zip->usemmap = usemmap;
+#endif
zip->refs = 1;
zip->lastModified = lastModified;
@@ -877,8 +899,6 @@
return;
}
-#ifndef USE_MMAP
-
/* Empirically, most CEN headers are smaller than this. */
#define AMPLE_CEN_HEADER_SIZE 160
@@ -928,7 +948,6 @@
cache->pos = cenpos;
return cen;
}
-#endif /* not USE_MMAP */
typedef enum { ACCESS_RANDOM, ACCESS_SEQUENTIAL } AccessHint;
@@ -953,14 +972,17 @@
ze->comment = NULL;
#ifdef USE_MMAP
- cen = (char*) zip->maddr + zc->cenpos - zip->offset;
-#else
- if (accessHint == ACCESS_RANDOM)
- cen = readCENHeader(zip, zc->cenpos, AMPLE_CEN_HEADER_SIZE);
- else
- cen = sequentialAccessReadCENHeader(zip, zc->cenpos);
- if (cen == NULL) goto Catch;
+ if (zip->usemmap) {
+ cen = (char*) zip->maddr + zc->cenpos - zip->offset;
+ } else
#endif
+ {
+ if (accessHint == ACCESS_RANDOM)
+ cen = readCENHeader(zip, zc->cenpos, AMPLE_CEN_HEADER_SIZE);
+ else
+ cen = sequentialAccessReadCENHeader(zip, zc->cenpos);
+ if (cen == NULL) goto Catch;
+ }
nlen = CENNAM(cen);
elen = CENEXT(cen);
@@ -976,7 +998,6 @@
if ((ze->name = malloc(nlen + 1)) == NULL) goto Catch;
memcpy(ze->name, cen + CENHDR, nlen);
ze->name[nlen] = '\0';
-
if (elen > 0) {
char *extra = cen + CENHDR + nlen;
@@ -1037,9 +1058,10 @@
ze = NULL;
Finally:
-#ifndef USE_MMAP
- if (cen != NULL && accessHint == ACCESS_RANDOM) free(cen);
+#ifdef USE_MMAP
+ if (!zip->usemmap)
#endif
+ if (cen != NULL && accessHint == ACCESS_RANDOM) free(cen);
return ze;
}
--- a/jdk/src/share/native/java/util/zip/zip_util.h Fri Mar 12 09:03:02 2010 -0800
+++ b/jdk/src/share/native/java/util/zip/zip_util.h Fri Mar 12 09:06:00 2010 -0800
@@ -45,9 +45,6 @@
* Header sizes including signatures
*/
-#ifdef USE_MMAP
-#define SIGSIZ 4
-#endif
#define LOCHDR 30
#define EXTHDR 16
#define CENHDR 46
@@ -211,9 +208,9 @@
jlong mlen; /* length (in bytes) mmaped */
jlong offset; /* offset of the mmapped region from the
start of the file. */
-#else
+ jboolean usemmap; /* if mmap is used. */
+#endif
cencache cencache; /* CEN header cache */
-#endif
ZFILE zfd; /* open file descriptor */
void *lock; /* read lock */
char *comment; /* zip file comment */
@@ -259,6 +256,9 @@
jzfile *
ZIP_Put_In_Cache(const char *name, ZFILE zfd, char **pmsg, jlong lastModified);
+jzfile *
+ZIP_Put_In_Cache0(const char *name, ZFILE zfd, char **pmsg, jlong lastModified, jboolean usemmap);
+
void JNICALL
ZIP_Close(jzfile *zip);
--- a/jdk/test/ProblemList.txt Fri Mar 12 09:03:02 2010 -0800
+++ b/jdk/test/ProblemList.txt Fri Mar 12 09:06:00 2010 -0800
@@ -700,7 +700,6 @@
java/net/URLConnection/ZeroContentLength.java generic-all
# Solaris 11 i586 fails with samevm, not sure why
-java/net/HttpURLConnection/HttpResponseCode.java generic-all
java/net/ResponseCache/B6181108.java generic-all
java/net/ResponseCache/ResponseCacheTest.java generic-all
java/net/URL/GetContent.java generic-all
--- a/jdk/test/java/net/CookieHandler/CookieHandlerTest.java Fri Mar 12 09:03:02 2010 -0800
+++ b/jdk/test/java/net/CookieHandler/CookieHandlerTest.java Fri Mar 12 09:06:00 2010 -0800
@@ -24,9 +24,13 @@
/* @test
* @summary Unit test for java.net.CookieHandler
* @bug 4696506
+ * @run main/othervm CookieHandlerTest
* @author Yingxian Wang
*/
+// Run in othervm since a default cookier handler is set and this
+// can effect other HTTP related tests.
+
import java.net.*;
import java.util.*;
import java.io.*;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/Socket/SocksConnectTimeout.java Fri Mar 12 09:06:00 2010 -0800
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6223635
+ * @summary Code hangs at connect call even when Timeout is specified
+ */
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.Socket;
+import java.net.ServerSocket;
+import java.net.SocketTimeoutException;
+import java.io.IOException;
+import java.io.Closeable;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.TimeUnit;
+
+public class SocksConnectTimeout {
+ static ServerSocket serverSocket;
+ static final boolean debug = true;
+ static final Phaser startPhaser = new Phaser(2);
+ static final Phaser finishPhaser = new Phaser(2);
+ static int failed, passed;
+
+ public static void main(String[] args) {
+ try {
+ serverSocket = new ServerSocket(0);
+
+ (new Thread() {
+ @Override
+ public void run() { serve(); }
+ }).start();
+
+ Proxy socksProxy = new Proxy(Proxy.Type.SOCKS,
+ new InetSocketAddress(InetAddress.getLocalHost(), serverSocket.getLocalPort()));
+
+ test(socksProxy);
+ } catch (IOException e) {
+ unexpected(e);
+ } finally {
+ close(serverSocket);
+
+ if (failed > 0)
+ throw new RuntimeException("Test Failed: passed:" + passed + ", failed:" + failed);
+ }
+ }
+
+ static void test(Proxy proxy) {
+ startPhaser.arriveAndAwaitAdvance();
+ Socket socket = null;
+ try {
+ socket = new Socket(proxy);
+ connectWithTimeout(socket);
+ failed("connected successfully!");
+ } catch (SocketTimeoutException socketTimeout) {
+ debug("Passed: Received: " + socketTimeout);
+ passed();
+ } catch (Exception exception) {
+ failed("Connect timeout test failed", exception);
+ } finally {
+ finishPhaser.arriveAndAwaitAdvance();
+ close(socket);
+ }
+ }
+
+ static void connectWithTimeout(Socket socket) throws IOException {
+ socket.connect(new InetSocketAddress(InetAddress.getLocalHost(), 1234), 500);
+ }
+
+ static void serve() {
+ Socket client = null;
+ try {
+ startPhaser.arriveAndAwaitAdvance();
+ client = serverSocket.accept();
+ finishPhaser.awaitAdvanceInterruptibly(finishPhaser.arrive(), 5, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ unexpected(e);
+ } finally {
+ close(client);
+ }
+ }
+
+ static void debug(String message) {
+ if (debug)
+ System.out.println(message);
+ }
+
+ static void unexpected(Exception e ) {
+ System.out.println("Unexcepted Exception: " + e);
+ }
+
+ static void close(Closeable closeable) {
+ if (closeable != null) try { closeable.close(); } catch (IOException e) {unexpected(e);}
+ }
+
+ static void failed(String message) {
+ System.out.println(message);
+ failed++;
+ }
+
+ static void failed(String message, Exception e) {
+ System.out.println(message);
+ System.out.println(e);
+ failed++;
+ }
+
+ static void passed() { passed++; };
+
+}