简体   繁体   中英

Java - Sending HTTP response but the browser keeps loading

I'm using the following program to simulate a webserver.

public void runServer() {
        try {
            ServerSocketChannel srvSktChan = ServerSocketChannel.open();
            ServerSocket skt = srvSktChan.socket();
            Selector selector = Selector.open();

            skt.bind(new InetSocketAddress(8000));
            srvSktChan.configureBlocking(false);
            srvSktChan.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

        while (true) {
            try {
                System.out.println("ready to accept...");
                this.selector.select();
                System.out.println("accepted...");
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(1);
            }

            Iterator<SelectionKey> rdyKeys = selector.selectedKeys().iterator();
            while (rdyKeys.hasNext()) {
                SelectionKey currKey = rdyKeys.next();
                rdyKeys.remove();
                try {
                    if (currKey.isAcceptable()) {
                        this.acceptKey(currKey);
                        System.out.println("A client connected");
                    } else if (currKey.isReadable()) {
                        this.readFromKey(currKey);
                    } else if (currKey.isWritable()) {
                        this.writeToKey(currKey);
                    }
                } catch (IOException e) {
                    currKey.cancel();
                }
            }
        }
    }

    private void acceptKey(SelectionKey key) throws IOException {
        ServerSocketChannel channel = (ServerSocketChannel) key.channel();
        SocketChannel clientSkt = channel.accept();

        clientSkt.configureBlocking(false);
        SelectionKey clientKey = clientSkt.register(this.selector, SelectionKey.OP_READ);
        clientKey.attach(new ClientConnectionState());
    }

    private void writeToKey(SelectionKey key) throws IOException {
        SocketChannel clientSkt = (SocketChannel) key.channel();

        // get previously stored response for this client
        Srtring response = ((ClientState) key.attachment()).pendingResponse;

        ByteBuffer buf = ByteBuffer.wrap(response.getBytes("UTF-8"));
        clientSkt.write(buf);

        key.interestOps(SelectionKey.OP_READ);
    }

    private void readFromKey(SelectionKey key) throws IOException {
        SocketChannel clientSkt = (SocketChannel) key.channel();
        ByteBuffer buf = ByteBuffer.allocate(this.BUF_CAPACITY);
        
        clientSkt.read(buf);
        buf.flip();

        String response = "HTTP/1.1 200 OK\r\n\r\n\r\n\r\n; // see note below

        key.interestOps(SelectionKey.OP_WRITE);
        ((ClientState) key.attachment()).pendingResponse = response;
    }

This code works fine for the most part: when I open a browser and type 127.0.0.1:8000 , the Java program does log an HTTP GET request and writes back the response.

However, the browser tab keeps loading forever, until the connection times out. In the browser network tab, the request is marked as pending and never completes. The response does seem to get written correctly, as I've tried sending a body and it is in fact received by the browser, but is only rendered when I kill the java app (which causes the browser to stop loading). I have a feeling that it's a matter of how I'm terminating the resposne I'm writing, but I've tried numerous other alternatives, such as writing these responses:

"HTTP/1.1 200 OK\n"
                + "Content-type: text/html; charset=UTF-8\n"
                + "<html><body>OK</body></html>\r\n\r\n"
"HTTP/1.1 200 OK\n"
                + "Content-type: text/html; charset=UTF-8\n"
                + "<html><body>OK</body></html>
"HTTP/1.1 200 OK\r\n\r\n\r\nTEST\r\n\r\n"
"HTTP/1.1 200 OK\r\n\r\n\r\nTEST"

and so on. All of them cause the same behavior in the browser. I'm starting to think there might be something about the way I'm instantiating and using the buffer or the socket to write my response that causes the browser to think I'm not done sending my response.

I also tried other tools such as cURL and I always get the same issue, so it's not just the browser.

What am I doing wrong?

Content-Length is needed, browser cannot know the content length of your message, and naturally it cannot parse out the complete message.

And as mentioned in this answer: https://stackoverflow.com/a/2773415/9690075 :

If both Content-Length and Transfer-Encoding headers are missing, then at the end of the response the connection must be closed.

Try:

    private static String HTTP_RESPONSE_TEMPLATE =
            "HTTP/1.1 200 OK\r\n" +
                    "Content-Length: %d\r\n" +
                    "Content-Type: text/html\r\n" +
                    "\r\n" +
                    "%s";

    byte[] buildSuccessfulResponse(String content) {
        return String.format(HTTP_RESPONSE_TEMPLATE, content.length(), content).getBytes(StandardCharsets.UTF_8);
    }
    
    private void writeToKey(SelectionKey key) throws IOException {
        // get previously stored response for this client
        String response = ((ClientConnectionState) key.attachment()).pendingResponse;
        ByteBuffer buf = ByteBuffer.wrap(buildSuccessfulResponse(response));
        System.out.println(clientSkt.write(buf));
        //..
    }

    private void readFromKey(SelectionKey key) throws IOException {
        //..
        String response = "hello";
        key.interestOps(SelectionKey.OP_WRITE);
        ((ClientConnectionState) key.attachment()).pendingResponse = response;
        //..
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM