i don't like fahrenheit. it's nothing personal, but they are hard to get used to, as much as (i guess) celsius are hard to adjust to for fahrenheit-centric people. this means that in many cases, it would be great to see web pages in metric units, and well-done internationalized sites often have that option. but unlike language preferences, which can be communicated via HTTP, there is no such mechanism for measurement systems (and the many other dimensions along which a service might want to provide choices).
so what would it take to extend HTTP in a way that content negotiation is more generic and not restricted to the currently hardcoded set of negotiable dimensions? this is just a first thought experiment, and i am wondering how useful this might be.
instead of hardcoding the negotiation axis in the header field, it should be dynamic, but there also should be some set of values for which a shared meaning (such as the axes hardcoded into HTTP now) has been established. an easy way to achieve this is to say that a negotiation axis is identified by token or by URI, where tokens have to be registered (in the same way as link relations can be registered in the IANA link relation registry), while URIs can be freely used and require no registration (like extension link relation types).
let's assume there's a registered "temperature" negotiation axis. a client could request to get responses in metric units by using an HTTP header field similar to Accept-Language, but since the axis now is configurable, let's assume we use a generic Accept-Axis header field:
Accept-Axis: "temperature" celsius;q=1, fahrenheit;q=0.7, kelvin;q=0.5
the syntax is similar to Accept-Language's language negotiation (or Accept-Encoding or Accept-Charset), only that it also includes an axis identifier. in the same way as Accept-Language defines the rules for how to match requested and available languages, the "temperature" axis has to define the same matching rules. in case of temperature system identifiers, all that's required is probably comparison, but other axes may specify more complex rules, such as the matching of language tags.
in the response, similar to Content-Language but using a generic header field, the server specifies which value has been chosen for a particular axis:
Content-Axis: "temperature" celsius
both the Accept-Axis and the Content-Axis header field can be repeated, so that it is possible to specify multiple axes in both requests and responses. however, this might produce an unwanted overhead, in particular if multiples axes are support. it thus would be possible to advertise the available axes via special headers. for example, a request could specifically ask to get information about a set of axes:
Accept-Axes: "temperature" "weight" "length"
this way, a server could make sure to only serve information about these axes in the response; any other available axes would not be exposed via Content-Axis headers. if a client wants to see all supported axes, it can send the following request header:
Accept-Axes: *
since such a request header field can be sent in an OPTIONS request, a server should have a way to simply list the available axes, which could be done as follows:
Content-Axes: "temperature" "weight"
when getting this information, a client can adjust the requested axes according to the supported ones. when combining link hints with this mechanism, overhead could be kept pretty low. when a client follows a link, that link could already specify the supported axes in an axes hint similar to the formats hint. however, when following the link, the client might also include an Accept-Axes header, allowing it to inquire about the runtime capabilities of the server (as opposed to the capabilities advertised in the link hint).
in the services that we provide, we actually have a fair number of "negotiation axes". some of them are not so much across the traditional lines of language or temperature, but for example control whether resources are sent in a more minimalistic way (excluding certain navigation options in the hypermedia format), or as fully interlinked resources. while this is not something that perfectly fits the traditional content negotiation bill, it might still be a good fit for a generalized HTTP content negotiation feature:
Content-Axes: "http://identifiers.emc.com/axis/linkage"
this way, we might (in our specific, non-standardized and non-registered way) expose the ability of a resource to be represented in two variants, with the axis supporting two values for sparsely or fully interlinked resources. clients can then request a specific variant by specifying the desired linkage:
Accept-Axis: "http://identifiers.emc.com/axis/linkage" sparse;q=1, full;q=0.5
currently, we are using query parameters for this kind of "negotiation", but it seems that pushing it into HTTP's uniform interface might make interactions a little bit simpler, but of course it would have the usual side-effect of content negotiation, meaning that there would be no separate URIs for the two variants.
i still need to think about how all of this affects caching (probably in a way which would make these interactions not very cache-friendly), and how it relates to Transparent Content Negotiation (TCN) as specified in RFC 2295. TCN probably could be extended to also cover generalized content negotiation, but currently TCN's dimensions are limited to the ones that are built into HTTP.
Are you saying that there would be lots of Accept-X headers? temperature, currency?
Just some quick thoughts, Erik, from my (one-sighted) realms:
- the majority of HTTP clients/server don't know how to parse/build headers in general, but predominantly content negotiation headers
- regarding caching, I hope I'm not wrong, but even a larger "majority" doesn't know of semantic equivalence, so Accept:x,y is not equivalent with Accept:y,x
At least that's where I'm stuck - and that's smth I'm trying to improve/fix in my own little corner of github.
Until there are proper HTTP parsers&builders, I can only tag amendments as "impractical" (don't get me wrong, I value the concept - but I'd like to build on a foundation that I can "touch").
Posted by: Andreineculau | Monday, August 19, 2013 at 00:45
thanks for the feedback, andrei. what i am proposing is not not have specific "Accept-Temperature" and "Accept-Currency" headers, because header field names definitely should be fixed and not dynamic. that's why the header fields mentioned in the blog post are fixed, they are Accept-Axis, Content-Axis, Accept-Axes, and Content-Axes.
these proposed header field names are fixed, and identifying the actual negotiation axis is done by using axis identifiers (registered keywords or URIs) in these header fields. so all your HTTP header handling needs to do is parsing the header field value, and you can rely on the header field names being fixed.
Posted by: dret | Monday, August 19, 2013 at 08:14
My apologies. I have no idea how I reached that conclusion, Erik.
But the rest still stands: Accept-Axis is a tad more complex than Accept[-*]/Content-*, and even Accept[-*]/Content-* is rarely implemented "correctly" (or as flexible as the ID specs it).
To be a bit more explicit and change my comment on the positive side
- what I personally like about your proposal is the abstraction level - I am biased in my liking because I thought about this when imagining some sort of an Accept-Currency header (I've been working in the finance industry) and then thinking why not just make content negotiation headers abstract from the "namespace" as I cold it (your "axis")
- I am 100% behind your proposal - given that the RFC comes not just with an ABNF grammar, but also with an AST structure to parse into and to build from, which sits at the core of the implementation of these headers
It is not as simple as to say "all you need to do is parse". Most RFCs start with the premise that it is the concept, the effects and the side-effects that are the hardest to nail - while implementation is just details, a hop with no quality check but just a quantity check (in a minimum number of implementation instances).
PS: A bit of context for my previously a-bit-harsh comment - I am on the path of trying to bring some sense (at least to me) into handling HTTP server-side at https://github.com/andreineculau/http-decision-diagram . But in order for that to happen, one needs to parse HTTP headers properly, which I try to do with PEGs (ABNFs are not enough for creating ASTs) at https://github.com/andreineculau/core-pegjs but even that is not enough, because one needs to also build HTTP headers (AST to string) and associate specific logic (e.g. Accept header selecting best match given the resource's provided content-types) which I try to do (for NodeJS) at https://github.com/andreineculau/api-pegjs .
So now imagine that I have all that on my hands, and I'm not far, but still considerably "not-close" from a clean ideal implementation :) and then I read about more headers. Hope you accept my sincere apologies both for my misunderstanding and my previous tone.
Posted by: Andreineculau | Monday, August 19, 2013 at 12:43
again, thanks for the response. good to see we agree on the general idea. you're definitely correct that this idea is more complex than the "hardcoded" current content negotiation schemes. but i guess that's hard to avoid when the goal is to come up with something that should be more flexible than those headers. and i guess your "namespace" idea is pretty much exactly what i am proposing, only that the namespace i am suggesting has two distinct sets: the registered "simple values", and the unregistered URIs.
and of course if that were developed into a draft heading towards RFCness, then the header would need to specify a grammar in ABNF. but it seems to me that many pieces of that grammar are already existing in other headers (the "Link" header, the existing "Accept-*" headers), so it wouldn't be too hard to define such a grammar.
the "processing model" of that grammar would be what you refer to as the AST, i assume. the exact syntax and rules for how to encode and select variants in the proposed headers is not something that i would claim is fully specified in this little blog post. i was more wondering about the idea and its utility in general, and it seems like you can see some uses cases for yourself, which is encouraging. thanks for your comments!
Posted by: dret | Friday, August 23, 2013 at 14:44