jsr369-experts@servlet-spec.java.net

[jsr369-experts] Re: [servlet-spec users] Trailer header implementation

From: Shing Wai Chan <shing.wai.chan_at_oracle.com>
Date: Thu, 4 May 2017 17:54:04 -0700

There are three main questions on trailer.
Let me try to summarize it from my point of view.

Section 1: is trailer supported?
RFC 7230, Section 4.3 TE
   The "TE" header field in a request indicates what transfer codings,
   besides chunked, the client is willing to accept in response, and
   whether or not the client is willing to accept trailer fields in a
   chunked transfer coding.

   The presence of the keyword "trailers" indicates that the client is
   willing to accept trailer fields in a chunked transfer coding, as
   defined in Section 4.1.2, on behalf of itself and any downstream
   clients.

MT has proposed an API to query whether trailer is supported.
And we will need to add a API in HttpServletRequest. The following are options:
a) public boolean areTrailerFieldSupported(); // from MT
b) public false isTrailerFieldSupportSpecified(); // from MM
c) public boolean isTrailerSupported();
d) no new API users can just query TE header from the request

In (a)-(c), we will add an default method returning false.


Section 2: set trailer
RFC 7230, Section 4.4 Trailer
   When a message includes a message body encoded with the chunked
   transfer coding and the sender desires to send metadata in the form
   of trailer fields at the end of the message, the sender SHOULD
   generate a Trailer header field before the message body to indicate
   which fields will be present in the trailers. This allows the
   recipient to prepare for receipt of that metadata before it starts
   processing the body, which is useful if the message is being streamed
   and the recipient wishes to confirm an integrity check on the fly.

MT, GW and MM has a discussion on this.
The main issues are
1) generation of Http header with name "Trailer"
2) what happens when supplier generate headers with names other than those in (1)
3) when can we call #setTrailerFields

The following are options (adapted from MT with additional comments):
Aa) Keep the existing API:
    void setTrailerFields(Supplier<Map<String, String>>)
   For (1), it is developer's responsibility to generate the http header "Trailer".
   For (2), servlet container can
        a) only send headers specified in (1), ignore those headers not specified in (1).
   For (3), the API can be called before response commit

Ab) Keep the existing API:
    void setTrailerFields(Supplier<Map<String, String>>)
   For (1), it is developer's responsibility to generate the http header "Trailer".
   For (2), servlet container can
        b) send all the headers in supplier
   For (3), the API can be called before response commit

Ba) New API for each trailer field:
    void setTrailer(String name, Supplier<String>)
   For (1), it is the container's responsibility to write the http header "Trailer".
        Servlet container will:
        a) ignore the http header "Trailer" set by developer.
   For (2), it is not an issue.
   For (3), #setTrailerFields needs to be called before the any body message committed.

Bb) New API for each trailer field:
    void setTrailer(String name, Supplier<String>)
   For (1), it is the container's responsibility to write the http header "Trailer".
        Servlet container will:
        b) clear the http header "Trailer" set by developer.
   For (2), it is not an issue.
   For (3), #setTrailerFields needs to be called before the any body message committed.

C) New API for the whole trailer:
    void setTrailer(Function<String, String>)
   For (1), it is the developer's responsibility to generate the http header "Trailer".
   For (2), this will not happen for this API as container read trailer field
   names from "Trailer" http header.
   For (3), the API can be called before response commit

With the above setting, I believe Mark's preferences are
    (Ba), (Bb), (Ab), (Aa), (C)

In B's, we need to restrict when we can call the API.
Is it simplier to hava A's and C?
Any other opinion?


Section 3: get trailer
MM> For an inbound trailer Section 4.1.2 of RFC2370 says:
MM> <quote>
MM> When a chunked message containing a non-empty trailer is received,
MM> the recipient MAY process the fields (aside from those forbidden
MM> above) as if they were appended to the message's header section.
MM> </quote>
MM> So in addition to getTrailers() we should make the trailer headers available
MM> through the getHeader() api's and automatically add them when they become
MM> available (all inbound data is read).
MM> But this leads me to a concern that the RFC is a specification of how
MM> the transport layer should behave and it s not clear to me how much of this transport
MM> function should be exposed in the Servlet Specification. In the Servlet Spec we
MM> should not be making assumptions about how the transport layer works in which case
MM> we should not have a getTrailers() API but instead we just expose the headers using
MM> existing api's once all the data is read. That way we work whether or not the transport
MM> layer exposes the trailer headers or not.

On April 6, 2017, I proposed (in (a) StandardHeader option [1])
to use the standard header APIs as one of the option to retrieve trailer fields.
This option was rejected in [2] and [3] as follows:

GW> with regards to request trailers, the key decision is when they are
GW> available, specially with async. Does the application have to read content
GW> to -1 or onAllDataRead before the trailers are available? I think yes.
GW> Then it does not make much sense to make them available via the normal
GW> header API and I think they need their own API.

SD> I don't think this is a good approach. The trailers will not be
SD> available immediately, so this will basically result in the headers
SD> changing after the request has been read. I think it could be very
SD> confusing to the developer.

Since the trailer fields are processed "as if they were appended to the message's
header section."
Should trailer fields be available to get header API's, too?

Any comments?

Shing Wai Chan

ps.
[1] https://java.net/projects/servlet-spec/lists/jsr369-experts/archive/2017-04/message/5
[2] https://java.net/projects/servlet-spec/lists/jsr369-experts/archive/2017-04/message/6
[3] https://java.net/projects/servlet-spec/lists/jsr369-experts/archive/2017-04/message/7



> On May 4, 2017, at 8:29 AM, Martin Mulholland <mmulholl_at_us.ibm.com> wrote:
>
> For an inbound trailer Section 4.1.2 of RFC2370 says:
>
> <quote>
> When a chunked message containing a non-empty trailer is received,
> the recipient MAY process the fields (aside from those forbidden
> above) as if they were appended to the message's header section.
> </quote>
>
> So in addition to getTrailers() we should make the trailer headers available
> through the getHeader() api's and automatically add them when they become
> available (all inbound data is read).
>
> But this leads me to a concern that the RFC is a specification of how
> the transport layer should behave and it s not clear to me how much of this transport
> function should be exposed in the Servlet Specification. In the Servlet Spec we
> should not be making assumptions about how the transport layer works in which case
> we should not have a getTrailers() API but instead we just expose the headers using
> existing api's once all the data is read. That way we work whether or not the transport
> layer exposes the trailer headers or not.
>
>
> Thank you,
> Martin Mulholland.
>
>
>
>
> From: Mark Thomas <markt_at_apache.org>
> To: jsr369-experts_at_servlet-spec.java.net
> Date: 05/03/2017 07:03 AM
> Subject: [servlet-spec users] [jsr369-experts] Trailer header implementation
>
>
>
> Hi,
>
> I've been working on trailer header implementation. No particular issues
> with requests but the response implementation is identifying a lot of
> questions.
>
> The first one is triggered by Section 4.4 for RFC 7230:
>
> <quote
> When a message includes a message body encoded with the chunked
> transfer coding and the sender desires to send metadata in the form
> of trailer fields at the end of the message, the sender SHOULD
> generate a Trailer header field before the message body to indicate
> which fields will be present in the trailers. This allows the
> recipient to prepare for receipt of that metadata before it starts
> processing the body, which is useful if the message is being streamed
> and the recipient wishes to confirm an integrity check on the fly.
> </quote>
>
> 1. How is the container meant to construct the Trailer header field? The
> point of using a supplier was so the header names and values did not
> have to be known when the response was committed. However, section 4.4
> requires that the names are known.
>
> The remaining questions are a little simpler:
>
> 2. What happens if setTrailerFields() is called after the response is
> committed?
>
> 3. What happens if setTrailerFields() is called and trailer fields are
> not supported (HTTP 0.9, HTTP 1.0, some proxy protocols, etc.)?
>
> 4. What happens if setTrailerFields() is called multiple times?
>
> 5. What happens if setTrailerFields() is called with a null Supplier?
>
> My current thinking:
>
> 1) Do we ignore the 'SHOULD' or do we change the API? I'm leaning
> towards changing the API on the basis that if we are going to add
> trailer support we should do it as correctly as possible.
>
> 2) Throw an IllegalStateException
> But. It also depends on 1). If we don't have to set the Trailers
> header it may still be possible to send the trailer fields
>
> 3) Throw an IllegalStateException
>
> 4) Use the most recent Supplier
>
> 5) Allow it and protect against any possible NPE in the container
>
>
> Of these, 1) is the most serious to address. After that, 2) & 3) since
> the application has no way to tell if this is going to happen. Do we
> need a boolean areTrailerFieldsSupported() method?
>
> Mark
>
>
>
>