Filter Response Headers via JavaScript Policy

I am looking to filter response headers from the back end using JavaScript policy. I realize I can accomplish this with Apigee policies, but am looking to see the latency differences between the two methods. Currently, I am able to grab all of the response headers and put them into an array with the following:

var headerNames = context.getVariable('response.headers.names');
var headerCount = context.getVariable('response.headers.count');
//print(headerNames);


headerNames = headerNames.toArray();


for (var i = 0; i < headerNames.length; i++) {
    print(headerNames[i] + ":" + context.getVariable('response.header.'+headerNames[i]));
  }

The array prints out with the following information:

Content-Length:788
Content-Type:application/json
Date:Fri, 18 Oct 2019 11:56:39 GMT
Server:Apache-Coyote/1.1

I would like to be able to filter this information, but am having difficulties parsing this array and keeping only specific response headers. For instance, I only want to keep "Content-Length", "Content-Type", and "Date." Can anyone assist with helping me figure out how to filter headers so that the array only contains what I want to keep?

0 7 1,768
7 REPLIES 7

am having difficulties parsing this array and keeping only specific response headers.

Not sure what you've tried. If I were doing this I would try something like this:

context.removeVariable('response.header.' + headerName); 

did you try that? Does it work?

Yes, that can be done, but that solution assumes you know the response headers in advance of them being sent. I am using the above JavaScript to generate a dynamic array so one does not have to know the response headers ahead of time. Another way to go about this would be to completely wipe all of the response headers and set only the headers I want, but I have yet to find a way to do this via the Apigee JavaScript Object Model.

that solution assumes you know the response headers in advance of them being sent.

Why? I don't understand.

function shouldRemove(headerName) {
  // anything you like here. One example is to check a "banned" list.
  return bannedHeaders.indexOf(headerName) >= 0;
}

var headerNames = context.getVariable('response.headers.names');
var headerCount = context.getVariable('response.headers.count');
headerNames = headerNames.toArray();
for (var i = 0; i < headerNames.length; i++) {
  if (shouldRemove(headerNames[i])) {
    context.removeVariable('response.header.' + headerNames[i]);
  }
}

Does that not work?

Also: I just re-read your original question,. It says

but am looking to see the latency differences between the two methods.

I think you will find negligible difference between the two. Performance and latency is affected by IO and network calls. Removing a header does neither. It's just writing memory. There may be a slight difference in the performance due to differing code paths, but generally the difference in speed will be irrelevant. Which one you choose is probably not going to make a material difference. It's probably not worth your time to study it.

A better criterion on which to make a decision is: which is more readable for your team?

Make it more maintainable, rather than spending the effort to evaluate what will likely be tiny differences in performance.

My $0.04.

Sorry for the delayed response on this;

Below is what I was looking to do (I am positive there are more eloquent ways to write the below code, but this is what I got):

var headers = [];

var headerNames = context.getVariable('response.headers.names');
headerNames = headerNames.toArray();
for (var i = 0; i < headerNames.length; i++) {
   headers.push(headerNames[i]);
   
}

var goodHeaders = ['Content-Length', 'Date', 'Content-Type', 'Transfer-Encoding', 'Connection'];

var headerDiff = [];

goodHeaders = goodHeaders.toString().split(',').map(String);

headers = headers.toString().split(',').map(String);

for (var i in headers) {
    if (goodHeaders.indexOf(headers[i]) === -1) headerDiff.push(headers[i]);
    
}


for (var i = 0; i < headerDiff.length; i++)
    if (headerDiff[i].indexOf() !== "") {
        context.removeVariable('response.header.' + headerDiff[i]);
    }

Adding this to a JS CallOut in the Proxy Response PostFlow will place all response headers (from the backend) into an array, then compare with the "goodHeaders" array. All of the differences, i.e. headers not specified in the "goodHeaders" array will be added to a "headerDiff" array and removed via "context.removeVariable"

Doing this will allow everything to happen dynamically and I will not need to already know what headers are being provided by the backend server. It will also mean I do not need to change the script each time the backend developers update their code and add more response headers which I do not want the client to see. The only thing needed is the list of acceptable response headers Apigee will send back to the client.

Looking at the latency between the 2 you are correct insofar as they are comparable.......

Thanks again for all your assistance with this effort!

I'm glad to be of assistance.

I think you could accomplish what you want more simply. You don't need to map the good array to a list and then back to an array (via tostring/split/map). And you don't need to store the difference.

var goodHeaders = [
      'Content-Length', 
      'Date', 
      'Content-Type', 
      'Transfer-Encoding',
      'Connection'
  ];

function notGood(headerName) { 
  return goodHeaders.indexOf(name) < 0;  
}

// get the list of headers in a JS array
var receivedHeaderNames = context.getVariable('response.headers.names').toArray();

// check each received header, remove any that is not on the 'good' list
receivedHeaderNames.forEach( function(name) {
  if (notGood(name)) {
    context.removeVariable('response.header.' + name);
  }
});

Did you test this code successfully?

I tried this approach to filter out response headers.  In my case 

goodHeaders.indexOf(headerName)

always result in -1, also for the headers that are both in "response.headers.names" and "goodHeaders".

It looks like the toArray() method doesn't return an array of strings and therefor  indexOf() can't match an element from receivedHeaderNames to an element from goodHeaders.

 

 

It's possible it's a matter of casing. HTTP Header names are case-insensitive. 

You may need to lowercase all the good headers and then use:

goodHeaders.indexOf(headerName.toLowerCase())