JavaScript response filter - HTTP

The JavaScript response filter is the most flexible of all HTTP response filters. It can potentially be called for any response, and can modify traffic in any way it sees fit.

Parameters

URL pattern

Optional. Specifies for which URL(s) the filter should be executed. This does not include the protocol, host and port number. If specified, it can be either:

  • a URL as a string for which this filter should get executed. For instance: /rest/state

  • a regular expression (starting with regex: ) that matches the URL. For instance: regex:/rest/state/[0-9]+


Method

Optional. If specified, the HTTP method (e.g. GET, POST, PUT, etc...) for which this filter should get executed. If not specified, all methods will match.


Client IPs

Optional. If specified, a comma-separated (or line-break separated) list of IP4 or IP6 addresses or regular expressions for IP addresses. If specified, only requests from matching IP addresses will cause the execution of the filter. For example:

  • 201.43.1.33, 88.49.9.199, regex:88\.48\.23\.\d{1,3}

  • 0:0:0:0:0:0:0:1, regex:2600:1700:6270:6aa0:51:92ca:\d+:\d+


Header patterns

Optional. If specified, a newline-separated list of header specifications which must match the response's headers. The name of the header is a simple string (case-insensitive), followed by a colon, and a regular expression for the header value. For instance:

Content-type: ((application/json)|(text/json))

If more than one pattern is specified (separated by line breaks), then the filter will be executed if at least one of the patterns matches at least one of the headers in the response.


Status code patterns

Optional. If specified, this filter will be invoked only if the response's HTTP status code matches one of the patterns specified. Examples:

  • 200

  • 200, 201, 202

  • regex:20\d


Content pattern

A regular expression that must match the body of the response. This is only valid for text responses, and for request methods that can contain a body, normally POST, PUT, and PATCH. If the response's body is not text, this parameter should not be set.

For example, if the body of a response contains the following:

[

{

"color": "red",

"value": "#f00"

},

{

"color": "green",

"value": "#0f0"

}

]

then any of the following regular expressions would match:

\[.*green.*\]

.*"color": "red".*

Basic Examples

Replace text in response

A common use for this type of response filter is to translate URLs in JSON responses in one go, so that they point to the Gallium Data instance rather than to the HTTP server. This is easily done with:

let payload = context.response.payloadString;

context.response.payloadString = payload.replaceAll("http://myserver:8080", "http://myproxy:8079");


Rewrite a 404 response

if (context.response.responseCode === 404) {

context.response.responseCode = 200;

context.response.payloadString = "<html><body>Sorry, this URL does not exist: " +

context.request.url + "</html></body>";

}


Remove a header from a response

Setting a header's value to null removes it.

context.response.setResponseHeader("X-Content-Type-Options", null);

Intermediate example: modifying an application's behavior

In this example, we'll create a response filter to modify a specific HTML response.

The filter must be set to execute only for the right response, so we might set the parameters to something like:

  • URL pattern: /ui/login.html

  • Method: GET

  • Header patterns: Content-type:text/html

The code for the filter can then be:

// Replace a specific string in the HTML code

let p = context.response.payloadString

p = p.replaceAll("Password not allowed", "Gallium Data says Hello!");


// Insert a bit of code in the page after a specific button

let lookFor = "Log In</button>";

let idx = p.search(new RegExp(lookFor, 'g'));

if (idx > 0) {

let helloButton = `<button type="button" class="btn btn-block"

style="background-color: red; color: white;"

onclick="alert('Welcome to Gallium Data!')">Click me!</button>`;

context.response.payloadString = p.substring(0, idx + lookFor.length) +

helloButton + p.substring(idx + lookFor.length);

}

You can do this with HTML, JavaScript, CSS, etc... but be careful! It's easy to break an application by modifying something you did not expect.

Of course, you can just turn off the filter to get back to the normal behavior.

Advanced example: modifying an image on the fly

In this example, we'll set a response filter that will read an image as it come from the server, overlay some text on the image using Java's ImageIO library, and send the result to the client.

First, the filter must be set so that it gets executed only for actual images and not anything else. For instance, we might set the parameters to:

  • URL pattern: regex:.*/images/.*\.png

  • Method: GET

  • Header patterns: Content-type:image/png


With that in place, we can then change the image with:

// Important to let Java know that there is no UI

let System = Java.type("java.lang.System");

System.setProperty("java.awt.headless", "true");


// Read the image from the response's payload

let ImageIO = Java.type("javax.imageio.ImageIO");

let img = ImageIO.read(context.response.payloadStream);


// Don't do anything if the image is small

if (img.getWidth() < 50 || img.getHeight() < 30) {

return;

}


// Set up the color, font and alpha level

let g = img.createGraphics();

let Color = Java.type("java.awt.Color");

g.setColor(Color.RED);

let Font = Java.type("java.awt.Font");

let font = new Font("sans-serif", Font.BOLD, 36);

g.setFont(font);

let AlphaComposite = Java.type("java.awt.AlphaComposite");

g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5));


// Add some text to the image

let txt = "Gallium Data";

let rect = font.getStringBounds(txt, g.getFontRenderContext());

g.drawString(txt, (img.getHeight() / 2) - (rect.getWidth() / 2),

(img.getHeight() / 2) + 24);


let outStream = context.response.payloadWriteStream;

ImageIO.write(img, "png", outStream);


Try it out

If you want to see this in action, you can set a connection with:

  • Server address: www.parks.ca.gov

  • Server port: 443

  • Use HTTPS with server: Yes

  • Local address: <whatever is appropriate for your machine>

  • Local port: 8097 (or any other port that is not already taken)

Then create a JavaScript response filter with parameters:

  • URL pattern: regex:(.*\.jpg)|(.*\.png)

  • Method: GET

  • Header patterns: Content-type:regex:(image/png)|(image/jpeg)

Make sure the filter is active and published.

Pick a photo on that website, such as https://www.parks.ca.gov/pages/531/images/Park_image090-P92745.jpg

Now visit it through your filter: http://localhost:8097/pages/531/images/Park_image090-P92745.jpg