Updated with New Information!
The Javascript callout capability in Apigee Edge is super flexible, making it nice for doing just about anything to a request or response message, whether that is parsing it, extracting data, transforming it, searching, validating, setting default values, and so on. Just running through that list makes me think I could post a series of quick articles here on the community site, outlining how to do each of these things. If I were to do that, I think @Birute Awasthi here at Apigee would be very pleased (Sheâs the community advocate). Iâll make no commitments, because I have a day job. But I will write at least ONE article in the set, on this topic: handling multi-value headers.
RFC2616, the IETF RFC that defines the HTTP protocol says:
Multiple message-header fields with the same field-name MAY be present in a message if and only if the entire field-value for that header field is defined as a comma-separated list [i.e., #(values)]. It MUST be possible to combine the multiple header fields into one âfield-name: field-valueâ pair, without changing the semantics of the message, by appending each subsequent field-value to the first, each 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, and thus a proxy MUST NOT change the order of these field values when a message is forwarded.
Clearly, it is legal for an HTTP request or response to have multiple values for a single header, or multiple instances of the same âheader keyâ. A good example might be, Cookie headers. A request may include multiple Cookie headers, something like:
GET /alpha/beta?q=7
Host: [www.example.com](www.example.com)
Cookie: JSESSIONID=alphabeta120394049
Cookie: AWSELBID=baaadbeef6767676767690220
And a response might include multiple Set-Cookies:
HTTP/1.1 200 OK
Server: Apigee-Edge
Set-Cookie: JSESSIONID=alphabeta120394049; HttpOnly
Set-Cookie: AWSELBID=baaadbeef6767676767690220; Path=/alpha
So how would a developer access those headers from within a Javascript callout? The ânormalâ way to access a single-valued header is:
var hdr = context.getVariable('request.header.host'),
ctype = context.getVariable('request.header.content-type);
Accessing a multi-valued header in the same way returns a Java ArrayList to the Javascript. (Remember, the JS callout is running inside the Rhino script engine, which itself is hosted on the Java VM. So the values returned from such calls as context.getVariable() are actually set by Java code).
According to the doc page on variables in Apigee Edge , multi-valued headers can be accessed with a .values suffix. In Javascript, this would look like this:
var hdr = context.getVariable('response.header.set-cookie.values');
If you then try to request hdr.toString(), you may get this error:
{
"fault": {
"faultstring": "Execution of Javascript-ExtractResponseCookies failed with error: Javascript runtime error: \"Access to Java class \"java.util.ArrayList\" is prohibited. (extractResponseCookies_js#11)\"",
"detail": {
"errorcode": "steps.javascript.ScriptExecutionFailed"
}
}
}
VERY unfriendly. In my opinion, this shouldnât happen, and Iâm petitioning the product gods to change this behavior.
The good news is there is a way to avoid this.
UPDATE, 2019 Feb 26.
If you want to get the raw, string value of a header, that may or may not have commas within it, then you can use this form:
var hdr = context.getVariable('response.header.set-cookie.values.string');
Just append â.values.stringâ to the ârequest.header.HEADERNAMEâ . This works for any header, request or response. For example, suppose a request header called âfooâ which has a value âa=bar,b-123â. Retrieving ârequest.header.foo.valuesâ gives you the ArrayList thing I described above. Retrieving ârequest.header.foo.values.stringâ gives you the original string value of the header, âa=bar,b=123â.
This also works in Condition statements (outside of JavaScript):
<Step>
<Name>SC-CheckAuthorization</Name>
<Condition>request.header.foo.values.string = "a=bar,b=123"</Condition>
</Step>
This is the older workaround I described previously (Donât Use This, itâs too complicated and has no advantage over the previous method):
var hdr = context.getVariable('response.header.set-cookie.values')+'';
This coerces the value, which is an arraylist, to a string. The result looks something like â[value1, value2, value3]â, so you need to deal with those square brackets if you decide to parse the value. For example:
// get the array of header values:
var a = hdr.substring(1, hdr.length-1).split(',');
Pretty simple, eh? Thanks to @Mike Dunker for the tip. To carry the example just a little further, if you want just the first portion of each cookie (not the HttpOnly, nor the Path, nor the Expires, etc), then:
for (var i=0, L = a.length, oneHeader; i<L; i++) {
oneHeader = a[i].split(';')[0].trim();
}
Ok, I hope this was helpful. In my next installment I will outline how to automatically file Biruteâs nag emails into a special invisible email folder.