Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpServer within junit fails with address in use error after first test

I have a Java class for use with JUnit 4.x. Within each @Test method I create a new HttpServer, with port 9090 used. The first invocation works find, but subsequent ones error with "Address is already in use: bind".

Here's an example:

@Test
public void testSendNoDataHasValidResponse() throws Exception {
    InetSocketAddress address = new InetSocketAddress(9090);
    HttpHandler handler = new HttpHandler() {

        @Override
        public void handle(HttpExchange exchange) throws IOException {
            byte[] response = "Hello, world".getBytes();
            exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.length);
            exchange.getResponseBody().write(response);
            exchange.close();
        }
    };
    HttpServer server = HttpServer.create(address, 1);
    server.createContext("/me.html", handler);
    server.start();

    Client client = new Client.Builder(new URL("http://localhost:9090/me.html"), 20, "mykey").build();

    client.sync();
    server.stop(1);
    assertEquals(true, client.isSuccessfullySynchronized());
}

Clearly the HttpServer is held solely within each method and is stopped before the end. I fail to see what's continuing to hold any sockets open. The first test passes, subsequent ones fail every time.

Any ideas?

EDIT with corrected method:

@Test
public void testSendNoDataHasValidResponse() throws Exception {
    server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 1);
    HttpHandler handler = new HttpHandler() {

        @Override
        public void handle(HttpExchange exchange) throws IOException {
            byte[] response = "Hello, world".getBytes();
            exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.length);
            exchange.getResponseBody().write(response);
            exchange.close();
        }
    };
    server.createContext("/me.html", handler);
    server.start();
    InetSocketAddress address = server.getAddress();
    String target = String.format("http://%s:%s/me.html", address.getHostName(), address.getPort());

    Client client = new Client.Builder(new URL(target), 20, "mykey").build();

    client.sync();
    server.stop(0);
    assertEquals(true, client.isSuccessfullySynchronized());
}
like image 481
jmkgreen Avatar asked Jan 17 '23 16:01

jmkgreen


2 Answers

jello's answer is on the money.

Other workarounds:

  • Reuse the same HttpServer for all your tests. To clean it up between tests, you can remove all its contexts. If you give it a custom executor, you can also wait for or kill off all the worker threads too.

  • Create each HttpServer on a new port. You can do this by specifying a port number of zero when creating the InetSocketAddress. You can then find the actual port in useby querying the server for its port after creating it, and use that in tests.

  • Change the global server socket factory to a custom factory which returns the same server socket every time. That lets you reuse the same actual socket for many tests, without having to reuse the HttpServer.

like image 90
Tom Anderson Avatar answered Jan 20 '23 16:01

Tom Anderson


There is usually a 2 minute wait time before you can rebind to a specific port number. Run netstat to confirm if your server's connection is in TIME_WAIT. If so, you can get around it by using the SO_REUSEADDR option before binding. Docs are here for java.

like image 24
jello Avatar answered Jan 20 '23 14:01

jello