HTTP server capable of Keep-Alive

2019-04-14 02:47发布

问题:

I'm trying to create a http server in Java which is capable of providing keep-alive connections. I'm using the com.sun.net.httpserver.HttpServer class.

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class httpHandler implements HttpHandler {

private String resp = "<?xml version='1.0'?><root-node></root-node>";

private OutputStream os = null;

public void handle(HttpExchange t) throws IOException {
    System.out.println("Handling message...");
    java.io.InputStream is = t.getRequestBody();

    System.out.println("Got request body. Reading request body...");
    byte[] b = new byte[500];
    is.read(b);
    System.out.println("This is the request: " + new String(b));

    String response = resp;
    Headers header = t.getResponseHeaders();
    header.add("Connection", "Keep-Alive");
    header.add("Keep-Alive", "timeout=14 max=100");
    header.add("Content-Type", "application/soap+xml");
    t.sendResponseHeaders(200, response.length());

    if(os == null) {
        os = t.getResponseBody();
    }

    os.write(response.getBytes());

    System.out.println("Done with exchange. Closing connection");
    os.close();
}

public static void main(String[] args) {
    HttpServer server = null;
    try {
        server = HttpServer.create(new InetSocketAddress(8080), 5);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    server.createContext("/", new httpHandler());
    server.setExecutor(null); // creates a default executor
    System.out.println("Starting server...");
    server.start();
}
}

The client does not close the connection. The server seems to close it instead directly after the exchange has occurred. I tried deleting the os.close line but then the server will not reply to the second request. But it doesn't close it either. I have a feeling it involves doing something in the main code with the server object but I have no idea what. Google isn't turning up much either.

Anyone here got any ideas? Any help would be much appreciated.

回答1:

It looks like the problem is you didn't drain all data from request. You should keep doing is.read() until it returns -1, then close it.

Since you didn't drain the request, there are still bytes left. The server cannot simply "jump" to the next request; it's not like a disk, more like a tape. Server has to read(and discard) all data from current request, before the it can reach the next request.

Without a limit this can be used to attack the server; so the server would only attempt to drain up to a limit, which is 64K by default. You are probably receiving a request bigger than 64K.

Usually the handler should read the entire request first. Otherwise how does it know how to serve the request?

More seriously, if the request is not drained first, deadlock can happen. Clients are typically simple: they writes the request, then read the response. If the server writes the response before it reads all the request, the client could be still writing the request. Both are writing to each other but neither is reading. If buffers are full then we are in a deadlock, both are blocked on write. Note there's no timeout for write()!



回答2:

Why do you get the ResponseBody OutputStream only if the previous was null?

if(os == null) {
    os = t.getResponseBody();
}

Might be better to get the OutputStream each time as you cannot be sure if the previous one is identical to the one of current Request.



回答3:

try to call t.close(), the server will reply



回答4:

It's the client that sends the keep-alive headers. If it does that, HttpServer doesn't close the connection. You don't have to do anything about it.