Karthik Kastury
Karthik Kastury

Reputation: 587

Chunked HTTP Responses in a Spring based web application

We are building a framework atop the Spring framework in Java. We want to be able to add support for HTTP Chunked Responses. Does spring have an API for this?

What we are trying to achieve is basically send out the HTML <head> section before the rest of the response is prepared. So that the client side can start downloading the stylesheets/js etc..

If there is a way to see if the client supports HTTP Chunked Responses would be even better.

Upvotes: 2

Views: 6940

Answers (4)

scrhartley
scrhartley

Reputation: 1537

Looking at how things work

I believe that Spring uses chunked transfer encoding by default for HTTP 1.1, while HTTP 2 is structured as frames and so is inherently streaming.

With an HTTP response you have the status line, headers and a body (let's ignore trailers, since browsers mostly don't let you do anything useful with these).
In the happy path for MVC, you collect all your data, set any headers you want on the response and then the template that you're using as your view will be used as the response body.
In the error path, an exception is thrown which overrides the default status code and changes which view will be used (usually some other template).

So in the error path, a flush in the main template is irrelevant since it wouldn't be used, and in the happy path, the majority of the time is spent collecting data and so the brief time spent writing out the template is largely irrelevant.

Basic Strategies

If you want to send the HTML head before the data is collected, you have a couple of options, but the problem with these approaches is that they interfere with (i.e. break) Spring's normal error handling:

  1. Write some HTML to the response in the controller, then collect the data and finally render the rest in the view template. E.g.:
response.setContentType("text/html;charset=UTF-8"); // Needed as Spring won't have set this yet.
response.getWriter().write(htmlStartIncludingHead);
response.getWriter().flush();
Model model = collectDataAndbuildModel();
return "myViewWithoutHead";
  1. Move the task of collecting data (directly or indirectly, synchronously or asynchronously) to be done inside the template in order to make flushes there be useful. If you flush before you wait to retrieve some amount of data, then the user can receive already completed content while waiting.
    In JSP you can flush using <% out.flush(); %> or <% response.getWriter().flush(); %>.
    In FreeMarker, it has a dedicated flush directive: <#flush>.

Towards a working solution

In order to experiment with a solution which tries various approaches to address error handling whilst moving the data collection to occur while running the template, I've made a proof of concept where the Model's values in the Controller are changed to Callables and/or Futures:
https://github.com/scrhartley/freemarker-streaming-poc
I've also made a version for JSP which focuses solely on using different types of Futures as Model values:
https://github.com/scrhartley/jsp-streaming-poc

The tricks used for error handling boil down to:

  1. Output a meta refresh tag to perform a client side redirect when an error occurs
  2. Output a script tag to perform some JavaScript page manipulation when an error occurs
  3. Use a template level equivalent to try/catch, allowing you to output some fallback content when an error occurs.
    • FreeMarker has attempt/recover directives. Attempt swallows flushes inside it and so needs to be used accordingly.
    • JSP has the JSTL catch tag.

Warning / Troubleshooting

HTTP streaming may not work if your servers are configured incorrectly. For notes on buffering and Nagle's algorithm see:
https://medium.com/airbnb-engineering/improving-performance-with-http-streaming-ba9e72c66408

Upvotes: 1

Devon_C_Miller
Devon_C_Miller

Reputation: 16528

RFC2616 specifies a TE: trailers header that is an explicit statement that the client accepts chunked transfers, but I have never seen a browser or device actually send this. Any device that sends a HTTP/1.1 request should accept chunked transfers.

In my experience, Spring will automatically perform chunked transfers any time you do not specify a content-length. My experience is mostly with Spring controllers, so YMMV with JSPs.

Upvotes: 1

Amir Pashazadeh
Amir Pashazadeh

Reputation: 7322

Try using Apache Tiles as your layout manager. You can set flush attribute on different tiles of your page, and as far as I know flushing a tile flushes to response.

By the way don't you use a compression mechanism, for example an Apache httpd in front of your container or even a gzip servlet filter? If this is the case, Apache Tiles won't help you, and it is better for you to forget about your need.

Upvotes: 0

Jay D
Jay D

Reputation: 3297

HTTP is a layer 7 ( /OSI Model) / 5 layer (TCP/IP Model) protocol. And a given application framework runs at or on the top of that.

Thus a given HTTP (get/POST) / pipelined request is essentially using a TCP pipe opened by the framework on the client side.

On the server side, typically you have the capability to process pipelined requests ( multiple get/post requests in one TCP pipe).

IN your requirement, you want a given request to be split into 2 subrequests probably by establishing a new TCP pipe to carry first half and second half of the request.

Now the challenge here is that even if your client side achieves that , the server side should accept such split requests. Typically the server side would throw away malformed HTTP requests.

Upvotes: -1

Related Questions