Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When a request is handled by a servlet, is the entire request header/body/etc. loaded already?

When a request is made to a web page, and it is handled via a servlet (which is processed via tomcat), once you enter processing at the servlet level (or a spring mvc controller), has the entire request header/body/etc. been sent from the client to the server already?

Say the client is performing a http POST to a web page, and the post contains allot of form elements.

Will all this data be going through tomcat and your executing servlet or if you don't actually reference:

request.getParamater("abc")

Then you won't incurr that extra load since it won't be streamed?

like image 877
codecompleting Avatar asked Jan 04 '12 20:01

codecompleting


People also ask

What is request in servlet?

Defines an object to provide client request information to a servlet. The servlet container creates a ServletRequest object and passes it as an argument to the servlet's service method. A ServletRequest object provides data including parameter name and values, attributes, and an input stream.

What is header in servlet?

This header specifies the types of encodings that the browser knows how to handle. Values of gzip or compress are the two most common possibilities. 4. Accept-Language. This header specifies the client's preferred languages in case the servlet can produce results in more than one language.

Which method is used by servlet to respond to the request from client?

The doGet method, because it is returning text to the client, uses the HttpServletResponse 's getWriter method. It sets the response header field, content type, before writing the body of the response, and closes the writer after writing the response.

What type of requests can servlets respond to?

Although servlets can respond to any type of request, they are commonly used to extend the applications hosted by web servers. For such applications, Java Servlet technology defines HTTP-specific servlet classes.


1 Answers

I cannot find a reference but I belive that the servlet starts processing once the whole header is available (all request headers followed by two newlines). That's why you have getInputStream() and getReader() rather than getBody() returning String or byte[].

This way the servlet can start handling the request data while the client is still sending it, allowing servlets to process very large amounts of data with small memory footprint. For instance upload servlet can read uploaded file byte by byte and save it to disk without the need to have full request contents in memory at the same time.

Here is a servlet I used for testing (in Scala, sorry for that):

@WebServlet(Array("/upload"))
class UploadServlet extends HttpServlet {

    @Override
    override def doPost(request: HttpServletRequest, response: HttpServletResponse) {
        println(request.getParameter("name"));
        val input = Source.fromInputStream(request.getInputStream)
        input.getLines() foreach println
        println("Done")
    }

}

Now I use nc to simulate slow client:

$ nc localhost 8080

Nothing happens on the server side. I now manually send some HTTP headers:

POST /upload?name=foo HTTP/1.1
Host: localhost:8080
Content-Length: 10000000

Still nothing happens on the server side. Tomcat accepted the connection but haven't yet invoked UploadServlet.doPost. But the moment I hit Enter two times, the servlet prints the name parameter but blocks on getLines() (getInputStream() underneath).

I can now send lines of text (Tomcat expects 10000000 bytes) using nc and they are incrementally printed on the server side (input.getLines() returns an Iterator[String] blocking until new line is available).

Servlets summary

  1. Tomcat waits for the whole HTTP header before it starts processing the request (passing it to matching servlet)

  2. Request body does not have to be fully available prior to doPost() invocation. This is fine, otherwise we would soon run out of memory.

  3. The same applies to sending response - we can do this incrementally.

Spring MVC

With Spring MVC you have to be careful. Consider the following two methods (note different argument types):

@Controller
@RequestMapping(value = Array("/upload"))
class UploadController  {

    @RequestMapping(value = Array("/good"), method = Array(POST))
    @ResponseStatus(HttpStatus.NO_CONTENT)
    def goodUpload(body: InputStream) {
        //...
    }

    @RequestMapping(value = Array("/bad"), method = Array(POST))
    @ResponseStatus(HttpStatus.NO_CONTENT)
    def badUpload(@RequestBody body: Array[Byte]) {
        //...
    }

}

Entering /upload/good will invoke goodUpload handler method as soon as HTTP header is received but it will block if you try to read body InputStream if no body has yet been received.

However /upload/bad will wait until the whole POST body is available since we have explicitly requested the whole body as a byte array (String would have the same effect): @RequestBody body: Array[Byte].

So it is up to you how Spring MVC handles large request bodies.

TCP/IP level

Remember that HTTP works on top of TCP/IP. Just because you haven't called getInputStream()/getReader() doesn't mean that the server is not receiving the data from the client. In fact, the operating system manages the network socket and keeps receiving TCP/IP packets, which aren't consumed. It means that the data from the client is pushed to the server, but the operating system has to buffer that data.

Maybe somebody more experienced can answer what is happening in this situations (not really a question for this site). O/S may close the socket abruptly if the server does not read incoming data or it may simply buffer it and swap if the buffer grows to large? Another solution might be to stop acknowledging client packets, causing the client to slow down/stop. Really depends on the O/S, not on HTTP/servlets.

like image 104
Tomasz Nurkiewicz Avatar answered Oct 22 '22 18:10

Tomasz Nurkiewicz