Let me try to summarize what we discussed on trailer.
A. Reading the request trailer headers.
b) HttpServletRequest:
Add the following API to HttpServletRequest:
public String getTrailer(String name);
public Collection<String> getTrailers(String name);
public Collection<String> getTrailerNames();
Note: existing methods in HttpServletRequest interface use Enumeration here.
c) RequestTrailerMap (from Simone Bordet):
Add the following API to HttpServletRequest:
public Map<String, String> getTrailers();
throws IllegalStateException if it is called before request input has been consumed (see SD from below and GW)
d) RequestTrailerField (from Simone Bordet):
Add the following API to HttpServletRequest:
public TrailerFields getTrailers();
TrailerFields is a class representing a list of trailer fields
SD> I think all of these [above] still have the problem that it is not really
SD> obvious when the trailers are available. What happens if you call them
SD> before the request has been read and the trailers consumed? If we went
SD> with one of these approaches I would expect them to throw
SD> IllegalStateException if the end of the request has not yet been read.
e) ConsumerCallback (from Stuart Douglas):
Add the following API to HttpServletRequest:
setTrailerCallback(Consumer<Map<String, String>> callback).
GW> Alternately, we could say that it will only ever be called by the
GW> thread that calls HttpInput.read() and is about the return -1, but
GW> immediately prior to that will call accept?
Note: For (b)-(e) above, then the trailer headers will "not" be
available to HttpServletRequest#getHeader, #getHeaders, #getHeaderNames.
GW> So my preference is A.c. If we are to go with A.e, then we need a lot
GW> more specification about how and when it is to be called. Eg. can a
GW> call to the setter immediately callback the accept within the scope of
GW> the call?
No.
B. Writing the response trailer headers.
a) HttpServletResponse:
public void addTrailer(String name, String value)
public void getTrailer(String name)
public Collection<String> getTrailers(String name)
public Collection<String> getTrailerNames()
b) Supplier (from Simone Bordet):
public void setTrailers(Supplier<TrailerFields>);
where TrailerFields is defined in A.d above.
c) SupplierMap (cf. A.c, above)
public void setTrailers(Supplier<Map<String, String>>);
SD> This does not support multiple headers with the same name (not sure if
SD> it will be a problem in practice).
Yes. In this case, the user need to combine headers with the name
into one header before calling the API.
According section 4.1.2 of RFC 7230:
A sender MUST NOT generate a trailer that contains a field necessary
for message framing (e.g., Transfer-Encoding and Content-Length),
routing (e.g., Host), request modifiers (e.g., controls and
conditionals in Section 5 of [RFC7231]), authentication (e.g., see
[RFC7235] and [RFC6265]), response control data (e.g., see Section
7.1 of [RFC7231]), or determining how to process the payload (e.g.,
Content-Encoding, Content-Type, Content-Range, and Trailer).
Since this is a supplier, I suggest the following:
The trailers that run afoul of the provisions of section 4.1.2 of RFC 7230
are ignored.
GW> I'm also in favour of B.c, but again we need to be more specific about
GW> what thread will call that API. Specifically I believe that we should
GW> say that it is called within the scope of whatever thread/call causes
GW> the response content to be completed. Typically this will be any thread
GW> calling close() on the output stream or writer.
+1.
MT> I'd be happy with those but I'd prefer the Map to be Map<String,List<String>>.
GW> I'm a bit agnostic on the matter of Map<String,String> vs Map<String,List<String>>
GW> as regardless we will have to check strings for CSV anyway.
So, I prefer the simpler Map<String, String> in this case.
MT> Fair point. I leant towards A.e rather than A.c for reasons of symmetry
MT> between input and output but if that means a lot more specification then
MT> A.c does look like the better choice.
MM> A.c and B.c look reasonable to me too.
Look like we have an agreement on B.c (SuppierMap write).
Now, let us discuss more on A.c (RequestTrailerMap read) and A.e (ConsumerCallback read).
Thanks.
Shing Wai Chan
> On Apr 12, 2017, at 11:17 AM, Martin Mulholland <mmulholl_at_us.ibm.com> wrote:
>
> A.c and B.c look reasonable to me too.
>
> Thank you,
> Martin Mulholland.
> WebSphere Application Server Web Tier Architect
> email: mmulholl_at_us.ibm.com
>
>
> Mark Thomas <markt_at_apache.org> wrote on 04/12/2017 04:30:22 AM:
>
> > From: Mark Thomas <markt_at_apache.org>
> > To: jsr369-experts_at_servlet-spec.java.net
> > Date: 04/12/2017 04:31 AM
> > Subject: [servlet-spec users] [jsr369-experts] Re: Re: revised
> > trailer proposals
> >
> > On 12/04/17 02:21, Greg Wilkins wrote:
> > >
> > > On 12 April 2017 at 11:07, Greg Wilkins <gregw_at_webtide.com
> > > <mailto:gregw_at_webtide.com <mailto:gregw_at_webtide.com>>> wrote:
> > >
> > > All,
> > >
> > > I'm a bit agnostic on the matter of Map<String,String> vs
> > > Map<String,List<String>> as regardless we will have to check strings
> > > for CSV anyway.
> > >
> > > I'm a bit cautious about the setTrailerCallback(Consumer<Map<String,
> > > String>> callback) API because there are insufficient details about
> > > when and who will call callback.accept(trailers)). If it is a
> > > true container callback, then it needs to be treated like the other
> > > container callbacks - context classloader set, mutually excluded
> > > from other callbacks etc.
> > >
> > > Alternately, we could say that it will only ever be called by the
> > > thread that calls HttpInput.read() and is about the return -1, but
> > > immediately prior to that will call accept?
> >
> > That is how I was thinking of implementing it in an attempt to avoid /
> > reduce threading issues.
> >
> > > Either way, I don't see much improvement on a method like
> > >
> > >
> > >
> > > Sorry pushed send too soon.
> > >
> > > Either way, I don't see much improvement on a method like:
> > >
> > >
> > > /**
> > > * @return A map of trailers or null if none are available.
> > > * @throws IllegalStateException if called before all request input
> > > has been consumed.
> > > */
> > > public Map<String,String> getTrailers();
> > >
> > >
> > > So my preference is A.c. If we are to go with A.e, then we need a lot
> > > more specification about how and when it is to be called. Eg. can a
> > > call to the setter immediately callback the accept within the scope of
> > > the call?
> >
> > Fair point. I leant towards A.e rather than A.c for reasons of symmetry
> > between input and output but if that means a lot more specification then
> > A.c does look like the better choice.
> >
> > > I'm also in favour of B.c, but again we need to be more specific about
> > > what thread will call that API. Specifically I believe that we should
> > > say that it is called within the scope of whatever thread/call causes
> > > the response content to be completed. Typically this will be any thread
> > > calling close() on the output stream or writer.
> >
> > That seems reasonable to me.
> >
> > Mark
> >
>