Project

Profile

Help

Feature #5199

closed

Multi-valued request/response headers

Added by Martynas Jusevicius almost 3 years ago. Updated 7 days ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Category:
IXSL extensions
Sprint/Milestone:
Start date:
2022-01-08
Due date:
% Done:

0%

Estimated time:
Applies to JS Branch:
2
Fix Committed on JS Branch:
Fixed in JS Release:
SEF Generated with:
Platforms:
Company:
-
Contact person:
-
Additional contact persons:
-

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

Actions #2

Updated by Norm Tovey-Walsh over 2 years ago

  • Sprint/Milestone set to SaxonJS 3.0
Actions #3

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?

Actions #4

Updated by Debbie Lockett 2 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.)

Actions #5

Updated by Martynas Jusevicius about 1 month 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?

Actions #6

Updated by Martynas Jusevicius about 1 month 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>>.

Actions #7

Updated by Debbie Lockett 7 days 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, where getResponseHeader() 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