jsr339-experts@jax-rs-spec.java.net

[jsr339-experts] Re: General rule for exposing/manipulating header values

From: Marek Potociar <marek.potociar_at_oracle.com>
Date: Tue, 28 Feb 2012 10:53:44 +0100

On 02/24/2012 10:01 PM, Bill Burke wrote:
> This is related to my last comment on:
>
> http://java.net/jira/browse/JAX_RS_SPEC-176
>
>
> Any interface that manipulates or exposed header values the client and server side and within Filter or Interceptors has
> to follow certain guidelines. You have to be extremely careful on whether the header manipulation api deals with header
> values that are strings or java.lang.Object.
>
> A ReaderInterceptorContext the header values can ONLY be strings as they are ALWAYS provided from an HTTP message
> (request on server side, response on client side). Vice-versa for WriterInterceptorContext. Header values may be actual
> Objects rather than strings as they are provided by user-code. This is specifically what I was referring to in previous
> emails.
>
> Both RequestHeader and ResponseHeader instances can either be populated through an HTTP message or from user code since
> they can be used on both client and server. Therefore their interfaces that manipulate or expose header values must be
> more generic and return java.lang.Object.
>
> Furthermore, header values must not be marshalled into string values until you are about to send the bits on the wire.
> And the following must be true:
>
> FooHeader fh = new FooHeader();
>
> Request/Response r = builder.header("foo", fh).build();
> Object value = r.getReXXXXHeaders().getHeader("foo");
>
> value == fh
>
> Same reference equality must be true if the header value was set via the Invocation.Builder interface. Why is this
> important? Well, consider a digital signature header like DKIM. You really can't complete the header until you
> calculate the signature of the entity and you might need information contained in the header object to calculate the
> signature (i.e. a private key).

You can store the extra information needed for producing a signature in the request properties and then add the header
only once it's fully calculated. Or am I missing something?

In general, I lean towards the idea of exposing headers for reading only as strings (which is in line with your earlier
request on this mailing list) as I assume that the most prevalent use-cases for manipulating headers are "set-once" and
"read-once".

Also, for the other use cases, I'm worried about the end-user experience and ability to write correct code. Since you
can set the value of the header using an arbitrary Java type, it is hard to make sure that the Object you're getting
from the map is of the type that you expect. In cases where the headers are used or modified by multiple filters the
correct defensive coding boilerplate is simply too much imho:

Object h = request.getHeader("Foo");
Foo foo;
if (h instanceof Foo) {
    foo = (Foo) h;
} else {
    hdOld = RuntimeDelegate.getInstance().createHeaderDelegate(h.getClass());
    hdFoo = RuntimeDelegate.getInstance().createHeaderDelegate(Foo.class);
    foo = hdFoo.fromString(hdOld.toString(h));
}

At last, this would once again open a problem in unified request/response. Note that having unified req/resp is not just
for the sake of nice API. It's a practical thing that lets users write common filters for the client and server side.

Marek

>
> Anyways, hope this sheds a bit of light on things.
>
>
>