1

I'm creating service based on Tomcat Servlets and NIO. On input there is big XML request(~100 MB), send through HTML POST method. I want to stream only first 8 KiB, and after that immediately send response to client.

public class A extends HttpServlet {
    @Override
    protected void service(HttpServletRequest rq, HttpServletResponse rs) {
        byte[] buffer = new byte[1024*8];
        try {
            rq.getInputStream().read(buffer);
            rs.setContentType("text/plain");
            rs.getOutputStream().write("Some Response".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } 
     }
}

There is no problem when I'm trying to send small request (few lines in content), socket works properly.

2016-02-01 10:44:52 Http11NioProtocol [DEBUG] Socket: [org.apache.tomcat.util.net.NioEndpoint$KeyAttachment@74f19fed:org.apache.tomcat.util.net.NioChannel@c478210:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:63943]], Status in: [OPEN_READ], State out: [OPEN]

But, if I try to send a bigger request (above 100 MB), there is no response on client side.

2016-02-01 10:48:42 Http11NioProtocol [DEBUG] Socket: [org.apache.tomcat.util.net.NioEndpoint$KeyAttachment@2b36c88f:org.apache.tomcat.util.net.NioChannel@25f12241:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:64079]], Status in: [OPEN_READ], State out: [CLOSED]

2016-02-01 10:48:42 LimitLatch [DEBUG] Counting down[http-nio-8080-exec-3] latch=1

Tomcat don't want to open socket (State out: CLOSED), before I read entire input stream request.

Is it possible to send response into client without reading entire request? According to specification, I'm able to find interesting me informations on very first 8 KiB of the request.

1 Answers1

0

your code is only read a part of request,

you can use this method to convert Stream To String

private static String convertStreamToString(InputStream is) throws IOException{
        int i = 0;
        byte[] buff = new byte[1024];
        StringBuilder sb = new StringBuilder();
        while (i != -1) {            
            i = is.read(buff);
            if(i != -1){
            sb.append(new String(buff,0,i));
            }
        }
        return sb.toString();
    }

so, in your code it will be

public class A extends HttpServlet {
    @Override
    protected void service(HttpServletRequest rq, HttpServletResponse rs) {

        try {
            String response = convertStreamToString(rq.getInputStream());   
            rs.getOutputStream().write(response.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } 
     }
     private static String convertStreamToString(InputStream is) throws IOException{
        int i = 0;
        byte[] buff = new byte[1024];
        StringBuilder sb = new StringBuilder();
        while (i != -1) {            
            i = is.read(buff);
            if(i != -1){
                sb.append(new String(buff,0,i));
            }
        }
        return sb.toString();
    }

}

or you can use Scanner to convert your Stream to String directly

    Scanner scanner = new Scanner(inputStream);
    StringBuilder sb = new StringBuilder();
    while (scanner.hasNext()) {            
        sb.append(scanner.next());
    }

so, in your code it should be

public class A extends HttpServlet {
    @Override
    protected void service(HttpServletRequest rq, HttpServletResponse rs) {

        try {
             Scanner scanner = new Scanner(rq.getInputStream());
             StringBuilder sb = new StringBuilder();
             while (scanner.hasNext()) {            
                 sb.append(scanner.next());
             }
            rs.getOutputStream().write(sb.toString().getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } 
     }

}

this code

byte[] buffer = new byte[1024*8];
rq.getInputStream().read(buffer);

only read the 1024 bytes requests and stored it to buffer array (1024 bytes), method read will return how many bytes was read, because not at all 1024's byte will be read, if request only send 200 bytes, 824 bytes will be useless. so , if request send only 200 bytes

int x = rq.getInputStream().read(buffer);

this x , will be 200, so to read fully, you need to make loop

// integer to read
int bytesRead = 0;
// loop until bytesRead is -1 (end of request), -1 means there are no bytes to read, so it will read until there are no bytes to read
while(bytesRead != -1){
    bytesRead = rq.getInputStream().read(buffer);
    if(bytesRead != -1){
    // process the bytes here , but only process bytes from 0 to bytesRead
     }
}
user965347
  • 186
  • 2
  • 16