It differs from more common read timeout, which may happen when connection is already opened, request is sent, but it takes too long to get response. Connection refused is also different beast, meaning that server actively refused your request, probably while you have been accessing wrong host or port. You should not ignore this connect timeout just because it is less common. When it hits, it will get you down you down as quickly as read timeout.
Connect timeout can occur during connection opening process. That is after connection attempt was not refused and before any possible read timeout.
Fairly rare occasions lead to this type of timeout. It may be firewall misconfiguration silently throwing away your packets, network failure or server can be dying down being unable to even to open socket correctly. Any of those conditions is quite hard to meet so when you need to write some piece of software anticipating this type of timeout, some deterministic way to induce it on demand will come handy.
Basic idea is to create ServerSocket with one position long backlog. Backlog is basically request queue and if we make artificial request to fill it up, any consequent request is doomed.
Update: Backlog queue implementation is differs between platforms. This does work on Mac OS X and does NOT on Linux or Windows. Damn it!Test method uses standard java HttpURLConnection to make the request and it's setConnectionTimeout to set connection timeout to some rasonable value.
Here comes the complete test case:
import java.net.HttpURLConnection; | |
import java.net.ServerSocket; | |
import java.net.Socket; | |
import java.net.SocketTimeoutException; | |
import java.net.URL; | |
import org.testng.Assert; | |
import org.testng.annotations.AfterClass; | |
import org.testng.annotations.BeforeClass; | |
import org.testng.annotations.Test; | |
public class ConnectTimeoutTest { | |
private ServerSocket serverSocket; | |
private int port; | |
@BeforeClass | |
public void beforeClass() throws IOException { | |
//server socket with single element backlog queue (1) and dynamicaly allocated port (0) | |
serverSocket = new ServerSocket(0, 1); | |
//just get the allocated port | |
port = serverSocket.getLocalPort(); | |
//fill backlog queue by this request so consequent requests will be blocked | |
new Socket().connect(serverSocket.getLocalSocketAddress()); | |
} | |
@AfterClass | |
public void afterClass() throws IOException { | |
//some cleanup | |
if (serverSocket != null && !serverSocket.isClosed()) { | |
serverSocket.close(); | |
} | |
} | |
@Test | |
public void testConnect() throws IOException { | |
URL url = new URL("http://localhost:" + port); //use allocated port | |
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); | |
connection.setConnectTimeout(1000); | |
//connection.setReadTimeout(2000); //irelevant in this case | |
try { | |
connection.getInputStream(); | |
} catch (SocketTimeoutException stx) { | |
Assert.assertEquals(stx.getMessage(), "connect timed out"); //that's what are we waiting for | |
} | |
} | |
} |
Oddly enough, different exception (java.net.ConnectException: Operation timed out) is thrown after 75 seconds in case that connection timeout is NOT set, comparing to case when it IS set (java.net.SocketTimeoutException: connect timed out).
Bonus code! Setup code for Apache Httpclient might look like:
HttpConnectionParamBean connectionBean = new HttpConnectionParamBean(httpParams); connectionBean.setConnectionTimeout(getConnectTimeout());//httpParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000L); connectionBean.setSoTimeout(getReadTimeout());//httpParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, 5000L); DefaultHttpClient httpClient = new DefaultHttpClient(httpParams);
Enjoy your timeouts!
No comments:
Post a Comment