Feature #5199
closedMulti-valued request/response headers
0%
Description
Looks like multi-values are not supported in 2.3 because headers
type is map(xs:string, xs:string)
.
https://www.saxonica.com/saxon-js/documentation2/index.html#!development/http
Maybe the type should be map(xs:string, xs:string*)
?
A similar issue to the URL param parsing: https://stackoverflow.com/questions/68944773/parsing-a-url-query-string-into-a-map-of-parameters-with-xpath
Updated by Norm Tovey-Walsh over 2 years ago
- Sprint/Milestone set to SaxonJS 3.0
Updated by Martynas Jusevicius over 2 years ago
Another manifestation of this is that given request headers with repeat names, for example
Link: <http://www.w3.org/ns/auth/acl#Read>; rel=http://www.w3.org/ns/auth/acl#mode
Link: <http://www.w3.org/ns/auth/acl#Write>; rel=http://www.w3.org/ns/auth/acl#mode
Link: <http://www.w3.org/ns/auth/acl#Control>; rel=http://www.w3.org/ns/auth/acl#mode
Link: <http://www.w3.org/ns/auth/acl#Append>; rel=http://www.w3.org/ns/auth/acl#mode
?headers?link
seems to return only the first value:
<xsl:message>?headers?link: <xsl:value-of select="?headers?link"/>
Output:
xsl:message: ?headers?link: <http://www.w3.org/ns/auth/acl#Read>; rel=http://www.w3.org/ns/auth/acl#mode
Executing the same request as fetch in the Firefox console, I get concatenated values from all Link
headers:
<http://www.w3.org/ns/auth/acl#Read>; rel=http://www.w3.org/ns/auth/acl#mode, <http://www.w3.org/ns/auth/acl#Write>; rel=http://www.w3.org/ns/auth/acl#mode, <http://www.w3.org/ns/auth/acl#Control>; rel=http://www.w3.org/ns/auth/acl#mode, <http://www.w3.org/ns/auth/acl#Append>; rel=http://www.w3.org/ns/auth/acl#mode
That seems to be the correct behavior. RFC7230 3.2.2. Field Order says:
A recipient MAY combine multiple header fields with the same field name into one "field-name: field-value" pair, without changing the semantics of the message, by appending each subsequent field value to the combined field value in order, separated by a comma. The order in which header fields with the same field name are received is therefore significant to the interpretation of the combined field value; a proxy MUST NOT change the order of these field values when forwarding a message.
Could this be addressed in Saxon-JS 2.x?
Updated by Debbie Lockett 4 months ago
- Tracker changed from Bug to Support
- Category set to IXSL extensions
- Status changed from New to In Progress
I think the issue here is how the request and headers XDM maps are constructed. Since duplicate keys are not permitted in XDM maps, you will need to supply multiple values for headers in a single string as a comma separated list. This will then work as expected.
I've done some testing, and I believe multiple values can be handled. In responses, multiple values are returned as a comma separated concatenated string.
I don't know how you have constructed the request map when you have attempted ?headers?link
and not got what you expected. Did you construct an XDM map directly and not handle what to do for duplicate keys? Or have you just constructed a JavaScript Headers
object (and a JavaScript Request
object), and not actually converted these to XDM maps?
I think perhaps that's it, you've constructed the request and headers as JavaScript objects. So then you are using the ?
lookup operator on a JavaScript object not an actual XDM map. This can be used as a shorthand to access properties of JavaScript objects that can be treated like an XDM map; but the object is not an actual XDM map. I think this explains why ?headers?link
only returns the first link (because the $request
item is actually a JSValue wrapped JavaScript object).
I'm afraid you need to do a bit more work to convert the JavaScript objects to XDM maps to use with the SaxonJS HTTP client.
(I'm changing this issue from bug to support; and will await feedback.)
Updated by Martynas Jusevicius 3 months ago
So I'm thinking that in my last example I probably meant response headers, not request headers. As I definitely wasn't constructing Link
headers in a request :)
Right now I cannot setup or find a server that would respond with multiple Link
headers, but I'm pretty sure I meant that in such case all response headers (of the same name) except the first one are ignored in $response?headers?link
. Maybe you can see in the code whether that could be the case?
Why would a map(xs:string, xs:string*)
datatype not be appropriate? For both request and response ?headers
. I would much prefer the value as a sequence than single string as a comma separated list. That would be a more structured approach?
Updated by Martynas Jusevicius 3 months ago
ChatGPT wrote an HTTP server for me so I could double-check the behaviour. Higher-level HTTP frameworks only allow setting headers as maps so I had to go for http.server
which allows writing raw response headers.
Response headers as seen in the browser's Network tab:
Link: <https://example.com/page1>; rel="next"
Link: <https://example.com/page2>; rel="prev"
SaxonJS 2.6 output:
?headers?link: <https://example.com/page1>; rel="next", <https://example.com/page2>; rel="prev"
So I was wrong in my previous comment:
all response headers (of the same name) except the first one are ignored
?headers?link
does not ignore values - both header values are present - but concatenated into a single string. So this is not a bug but a feature request:
Why would a
map(xs:string, xs:string*)
datatype not be appropriate? For both request and response?headers
. I would much prefer the value as a sequence than single string as a comma separated list. That would be a more structured approach?
So in this case I would expect a sequence with 2 values:
?headers?link = ('<https://example.com/page1>; rel="next"', '<https://example.com/page2>; rel="prev"')
JAX-RS Response::getHeaders
uses the same kind of contract - the return type is MultivaluedMap<K,V> extends Map<K,List<V>>
.
Updated by Debbie Lockett 2 months ago
- Tracker changed from Support to Feature
- Status changed from In Progress to Rejected
OK, so I have updated the issue to be a Feature request; but I'm going to reject it.
I think the suggestion to change the datatype for the headers
value in the HTTP request and response maps is reasonable, but I think it is unnecessary, and that changing this now could potentially cause more problems and confusion than we would solve.
The current implementation combines multiple values in a single string, in a comma separated list. The implementation is based on the underlying JavaScript APIs:
- in SaxonJS 2 we are using
XMLHttpRequest
, wheregetResponseHeader()
returns a single string: "If there are multiple response headers with the same name, then their values are returned as a single concatenated string, where each value is separated from the previous one by a pair of comma and space." (see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getResponseHeader) - for SaxonJS 3, we will use
Headers
interface of the Fetch API, here "When Header values are iterated over, they are automatically sorted in lexicographical order, and values from duplicate header names are combined." (see https://developer.mozilla.org/en-US/docs/Web/API/Headers) - and they are combined in a comma separated string.
I think it is reasonable that the SaxonJS HTTP API does the same thing as the underlying JavaScript API here, and leaves it to the user to split the string as required (which is not hard). I think that if we changed the API this could potentially cause awkward problems for users, if a sequence is returned where a single string was previously expected.
Please register to edit this issue
Also available in: Atom PDF Tracking page