Learn F5 Technologies, Get Answers & Share Community Solutions Join DevCentral

Filter by:
  • Solution
  • Technology
Answers

iRule ReWrite - Inserting JSON Parameter into Request + Removing Same JSON Parameter from Response

Looking to leverage an iRule to Dynamically insert a JSON Parameter into an HTTP payload and then scrub it in the response back to the client.

However, we want to inspect the Payload and only perform this action when a specific JSON Parameter value exsits.

Example:

When JSON Key/Value of "Animal": "Dog" exists, Dynamically insert new Parameter of "Color": "Brown".

The server(pool member) would see this new parameter and act on it, but in the HTTP response to the client, the new parameter is not in the payload.

Do iRules know how to handle and work with JSON specifically?

Is an LX iRule going to be need to do this advanced rewrite?

Thanks!

0
Rate this Question

Answers to this Question

placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

Yes, iRules LX would be a good option as it can handle JSON objects natively.

Here's a sample code. It adds the key-value {"newKey":"new"} to the request data (POST), and removed the key "aoc" from the response body. This is just a skeleton code. You can add any conditions you want (such as checking the presence of a certain key etc).

iRule part

when HTTP_REQUEST {
  set RPC_HANDLE [ILX::init RemoveKeyPlugin RemoveKeyExt]

  if { [HTTP::method] eq "POST"} {
    HTTP::collect [HTTP::header "Content-Length"]
  }
}

when HTTP_REQUEST_DATA {
  set payload [HTTP::payload]
  set req [ILX::call $RPC_HANDLE req $payload]
  log local0. "$payload > $req"
  HTTP::payload replace 0 [HTTP::header "Content-Length"] $req
}

when HTTP_RESPONSE {
    HTTP::collect [HTTP::header "Content-Length"]
}

when HTTP_RESPONSE_DATA {
  set payload [HTTP::payload]
  set res [ILX::call $RPC_HANDLE res $payload]
  log local0. "$payload > $res"
  HTTP::payload replace 0 [HTTP::header "Content-Length"] $res
}

Node (extension) part

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

var parser = function(str) {
  var json = {"error":"unable to parse"};
  try {
    json = JSON.parse(str);
  }
  catch(e) {
    console.log('Got non-Json data: ' + e + ' >>> ' + str);
  }
  return json;
}

// Adding an entry
var addEntry = function(req, res) {
  var json = parser(req.params()[0]);
  json['newKey'] = 'new';      // Adding a new entry
  res.reply(JSON.stringify(json));
};

// Removing an entry
var removeEntry = function(req, res) {
  var json = parser(req.params()[0]);
  delete json['aoc'];
  res.reply(JSON.stringify(json));
}

ilx.addMethod('req', addEntry);
ilx.addMethod('res', removeEntry);
ilx.listen();

Example output

Jul 17 16:59:04 ltm1310 info tmm[17714]: Rule /Common/RemoveKeyPlugin/RemoveKeyRule <HTTP_REQUEST_DATA>: {"hello":"world"} > {"hello":"world","newKey":"new"}
Jul 17 16:59:04 ltm1310 info tmm[17714]: Rule /Common/RemoveKeyPlugin/RemoveKeyRule <HTTP_RESPONSE_DATA>: {   "name":"Chateau Mouton Rothchild",   "aoc":"Pauillac",   "year":1978 }  > {"name":"Chateau Mouton Rothchild","year":1978}
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

iRules LX comes with two flavour. One is RPC style as above. It uses both iRules (TCL) and node. The other one is Streaming. It's pure Node without TCL, which became available from v13.0.

Here's a Streaming example. The specification is same as the RPC one.

var f5 = require('f5-nodejs');
var plugin = new f5.ILXPlugin();


// string to json parser (same as RPC version)
var parser = function(str) {
  var json = {"error":"unable to parse"};
  try {
    json = JSON.parse(str);
  }
  catch(e) {
    console.log('Got non-Json data: ' + e + ' >>> ' + str);
  }
  return json;
}


// Reading buffer and return string
var readMe = function(ilxTransaction) {
  var chunk = [];
  while(true) {
    var buffer = ilxTransaction.read();
    if (buffer !== null) {
      chunk.push(buffer);
    }
    else {
      break;
    }
  }
  return chunk.join('').toString() || '{"error":"no data"}';
}


// Main loop
plugin.on('connect', function(flow) {

  // Started to receive client POST data
  var postData = '';
  flow.client.on('readable', function(req) {
    postData = readMe(flow.client);
  });
  
  // Got all the client POST data. Send it over to poolMember
  flow.client.on('requestComplete', function(req) {
    console.log('client req (POST): ' + postData);
    var postDataJson = parser(postData);
    postDataJson['newKey'] = 'new';
    console.log('req to poolmember: ' + JSON.stringify(postDataJson));
    flow.server.write(JSON.stringify(postDataJson));
    req.complete();
    console.log('res done');
  });
  
  // Started to receive poolMember body
  var body = '';
  flow.server.on('readable', function(res) {
    body = readMe(flow.server);
  });
  
  // Got all the server data. Push it to client
  flow.server.on('responseComplete', function(res) {
    console.log('server res (body): ' + body);
    var bodyJson = parser(body);
    delete bodyJson['aoc'];
    console.log('res to client: ' + JSON.stringify(bodyJson));
    flow.client.write(JSON.stringify(bodyJson));
    res.complete();
  });

  // Error notification
  flow.client.on('error', function(errorText) {
    console.log("client error event: " + errorText);
  });
  flow.server.on('error', function(errorText) {
    console.log("server error event: " + errorText);
  });
  flow.on("error", function(errorText) {
    console.log("flow error event: " + errorText);
  });

});
  
var options = new f5.ILXPluginOptions();
// No specific option here
plugin.start(options);
0