Simple iRulesLX JSON rewrite

Problem this snippet solves:

Like rewriting the Location field in a redirect, it's sometimes required to rewrite JSON data, returned in an HTTP response. Whilst it would be possible to write using traditional iRules, the task is made more simple (and less risky) by using iRulesLX.

In the following example, an HTTP response contains JSON data with fields containing external URLs. These need to be rewritten to an internal URL for the purpose of internal routing.

{  
    "firstUrl":"https://some.public.host.com/api/preauth/ABCDEFGHIJKLM",
    "secondUrl":"https://some.public.host.com/api/documents/{documentId}/discussion"
}

The concept can be used to rewrite any JSON data, however more complicated JSON containing arrays for example would need to be taken into consideration.

How to use this snippet:

use the following iRule to call iRulesLX and pass the necessary parameters

when CLIENT_CONNECTED {
    set newHost "internal.host.local"
    set jsonKeys "firstUrl secondUrl"
    set rpcHandle [ILX::init "json-parse-plugin" "json-parse-extension"]
}

when HTTP_RESPONSE {
    if {[HTTP::header "Content-Type"] eq "application/json"} {
        HTTP::collect [HTTP::header "Content-Length"]
    }
}

when HTTP_RESPONSE_DATA {
    set payload [HTTP::payload]
    set result [ILX::call $rpcHandle "setInternalUrl" $payload $jsonKeys $newHost]
    HTTP::payload replace 0 [HTTP::header "Content-Length"] $result
}

When used in combination with the iRulesLX code below the host portion of the URIs in the JSON data are rewritten and sent back to the origin by replacing the HTTP payload

  {  
    "firstUrl":"https://internal.host.local/api/preauth/ABCDEFGHIJKLM",
    "secondUrl":"https://internal.host.local/api/documents/{documentId}/discussion"
  }

Code :

const f5 = require('f5-nodejs');
const url = require('url');
const ilx = new f5.ILXServer();

function setInternalUrl(req, res) {

    var json = JSON.parse(req.params()[0]);
    var jsonObj = req.params()[1].split(' ');
    var newHost = req.params()[2];

    for (var i = 0; i < jsonObj.length; i++) {
        if (typeof json[jsonObj[i]] == "string") {
            var oldUrl = url.parse(json[jsonObj[i]]);
            oldUrl.host = newHost;
            var newUrl = decodeURI(url.format(oldUrl));
            json[jsonObj[i]] = newUrl;
        } else {
            json = {"error":"unable to rewrite"};
        }
    }
    res.reply(JSON.stringify(json));
}

ilx.addMethod('setInternalUrl', setInternalUrl);
ilx.listen();

Tested this on version:

12.1
Updated Jun 06, 2023
Version 2.0

Was this article helpful?

1 Comment

  • Great bit of code and excatly what I'm looking for, but as the response is gzip encoded the irule fails as there is no content length header! is there any other way of capturing the payload without content-length ?