Search code examples
javahttphttpserver

Stop HttpServer after request are served


I am using a simple Http server from com.sun.net.httpserver, as described in simple HTTP server in Java using only Java SE API.

Everything works fine, however I am unsure how can I cleanly shutdown the server once I no longer need it. I do not want to stop the server while it is still sending the data for some request, only once it is idle.

My particular scenario is a desktop application performing OAuth dance. I use a local web server embedded in the application to provide a callback response, the application launches a desktop browser to show the server response using java.awt.Desktop.browse API.

I have tried calling HttpServer.stop(0) directly from my HttpHandler.handle function, after I have written the response page into the HttpExchange response output stream, but this is too early, the browser shows ERR_EMPTY_RESPONSE.

The same happens when I stop the server once my main application has finished its work - this is often too early, the HttpServer has not completed sending the data out yet at that moment.

I could provide a few seconds delay value to stop, but I would like to achieve a proper and clean synchronization.

What is a proper solution to this?


Solution

  • I can offer a solution based on one CountDownLatch + custom ExecutorService that is set up as executor for HttpServer:

    public void runServer() throws Exception {
        final ExecutorService ex = Executors.newSingleThreadExecutor();
        final CountDownLatch c = new CountDownLatch(1);
    
        HttpServer server = HttpServer.create(new InetSocketAddress(8888), 0);
    
        server.createContext("/test", (HttpExchange h) -> {
            StringBuilder resp = new StringBuilder();
            for (int i = 0; i < 1_000_000; i++)
                resp.append(i).append(", ");
            String response = resp.toString();
            h.sendResponseHeaders(200, response.length());
            OutputStream os = h.getResponseBody();
            os.write(response.getBytes());
            os.close();
            c.countDown();           // count down, letting `c.await()` to return
        });
    
        server.setExecutor(ex);      // set up a custom executor for the server
        server.start();              // start the server
        System.out.println("HTTP server started");
        c.await();                   // wait until `c.countDown()` is invoked
        ex.shutdown();               // send shutdown command to executor
        // wait until all tasks complete (i. e. all responses are sent)
        ex.awaitTermination(1, TimeUnit.HOURS); 
        server.stop(0);
        System.out.println("HTTP server stopped");
    }
    

    I did test this on our work network environment, and it seems to work correctly. HttpServer stops no earlier than response is fully sent, so I think this is exactly what you need.

    Another approach may be not shutting down the executor ex, but sending there a new task containing server.stop() after the response is written to a stream. As ex is constructed single-threaded, such task will execute not earlier than previous task completes, i. e. a response is fully sent:

    public void run() throws Exception {
        final ExecutorService ex = Executors.newSingleThreadExecutor();
        final HttpServer server = HttpServer.create(new InetSocketAddress(8888), 0);
    
        server.createContext("/test", (HttpExchange h) -> {
            // ... generate and write a response
            ex.submit(() -> { 
                server.stop(0); 
                System.out.println("HTTP server stopped"); 
            });
        });
        server.setExecutor(ex); 
        server.start();
        System.out.println("HTTP server started");
    }
    

    For more information, see ExecutorService