HTTP/1.1 and HTTP/2 have the concept of trailers, that is HTTP headers that can be sent after the message body, in both requests and responses.
In HTTP/1.1 trailers can be sent using the chunked transfer coding, for example in requests (but the same is valid in responses):
POST / HTTP/1.1\r\n Host: host\r\n Transfer-Encoding: chunked\r\n \r\n A\r\n 0123456789\r\n 0\r\n Trailer-Name: trailer-value\r\n Foo: bar\r\n \r\n
As you can see, between the indication of the terminal chunk length 0\r\n
and the terminal empty line \r\n
, HTTP/1.1 allows to put the trailers.
In HTTP/2, the situation is similar:
HEADERS - end_stream=false DATA - length=10, end_stream=false HEADERS - end_stream=true
The first HEADERS
frame contains the request line and headers, followed by a DATA
frame that does not end the stream yet, followed by a HEADERS
frame that contains the trailers, and that ends the stream.
A typical use of trailers would be to add dynamically generated metadata about the content, for example message integrity checksums.
Another typical use is for applications that stream content: in case of problems during the streaming, they can add trailers with information about what went wrong.
Other protocols such as gRPC make use of the trailers and therefore can be mapped on top of HTTP.
The Servlet APIs, up to version 3.1, do not expose a standard API to access the trailers. HTTP trailers APIs are, however, now being discussed for inclusion in Servlet 4.0.
The recently released Jetty 9.4.4.v20170414 includes support for HTTP trailers, for both HTTP/1.1 and HTTP/2, via custom Jetty APIs.
This is how you can use them in a Servlet:
public class TrailerServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { Request jettyRequest = (Request)request; // Read the content first. ServletInputStream input = jettyRequest.getInputStream(); while (true) { int read = input.read(); if (read < 0) { break; } } // Now the request trailers can be accessed. HttpFields requestTrailers = jettyRequest.getTrailers(); // Use the request trailers. HttpFields responseTrailers = new HttpFields(); trailers.put("trailer1", "foo"); // Set trailer Supplier to tell the container // that there will be response trailers. Response jettyResponse = (Response)response; jettyResponse.setTrailers(() -> trailers); // Write some content and commit the response. ServletOutputStream output = response.getOutputStream(); output.write("foo_bar_baz"); output.flush(); // Add another trailer. trailers.put("trailer2", "bar"); // Write more content. output.write("done"); // Add a last trailer. trailers.put("last", "baz"); } }
Request trailers will only be available after the request content has been fully read.
For the response trailers, the reason to use a Supplier
in the response APIs is to tell the container to use the chunked transfer coding (in case of HTTP/1.1), even if the response content length is known. In this way, the container can prepare for sending the trailers, and eventually send them when the whole content has been sent.
Try out HTTP trailers in Jetty 9.4.4, and report back how you use it and how you like it (so that we can make it even better) either in the Jetty mailing lists, or in a Jetty GitHub issue (open it just for the discussion).
Enjoy !
0 Comments