Forum Discussion

Joe_Reifel's avatar
Joe_Reifel
Icon for Employee rankEmployee
Sep 13, 2013

Scripting: what is the response object used for when processing a request?

First post was a bit poorly formatted, trying again.

 

This may be a "RTM" question but here goes.

 

The docs on lineratesystems.com do a good job at explaining how to use the HTTP request object. And I believe that the response object is for manipulating (or setting) what the LineRate will send back to the client.

 

So my question is how do I get at the server response? Abridged code with my question in the middle:

 

var http = require('http');
var vsm = require('lrs/virtualServerModule');
var mysql = require('mysql');
var Cookies = require('cookies');

var vs; // will be assigned in onRequest
var vs_a; 
var vs_b; 
var logged_in = false;
var pidinfo = "(PID " + process.pid + "): ";


// Log to a database
var connection = mysql.createConnection({
  // Connection setup information
});


var onRequest = function(request, response, next ) {
  // do some processing

  // Here's I'd store the initial timestamp for the request
  connection.query('INSERT INTO urls ...', function(err, rows, fields) {
      if (err) throw err;

    // Decide which server to send to
    connection.query('SELECT opt_in from urls where uri="' + connection.escape(request.url) + '"', function(err, rows, fields) {
      if (err) throw err;
      var opt_in = rows[0].opt_in;
      if( !opt_in ) {

    
     !!! WHERE DO I PROCESS THE RESPONSE FROM THE SERVER?
    
        vs_a.newRequest(request, response, next);
        return;
      } else {
        vs_b.newRequest(request, response, next);
        return;
      }
    });
  });
}; 
// onRequest

var onExist = function(the_vs) {
    if(the_vs.id == 'Bugzilla') {
    connection.connect();
    logged_in = true;

    vs = the_vs;
    vs.on('request', onRequest);
  }
};

vsm.on('exist', 'Bugzilla', onExist);

1 Reply

  • Andrew_Jenkins_'s avatar
    Andrew_Jenkins_
    Historic F5 Account

    Hi Joe.

    Definitely not an RTM question. This is an advanced usage, combining newRequest for request redirection, and onResponse for response interception. We don't have an example to follow yet, and there's a lot of objects floating around. The key is to register for the response after you've done the request redirection: you use newRequest to send it to vs_a, and then it'll pop up in vs_a's request handler just like any other request. Once there, register for the response handler.

    Here's an example, based on yours but with the MySQL parts removed for clarity:

    vsm = require('lrs/virtualServerModule');
    
    var vs, vs_a, vs_b;
    
    vsm.on('exist', 'vs', function (vs) {
      vs.on('request', function redirectRequest(servReq, servResp, next) {
    
        // Pseudo-randomly redirect to vs_a or vs_b.
        var opt_in = (Date.now() % 2 === 0);
        if (!opt_in || (!vs_b && vs_a)) {
          vs_a.newRequest(servReq, servResp, next);
        } else if (vs_b) {
          vs_b.newRequest(servReq, servResp, next);
        } else {
          servResp.writeHead(502, { 'Content-Type' : 'text/plain' });
          servResp.end('neither vs_a nor vs_b configured, forwarding impossible.');
          next();
        }
      });
    });
    
    vsm.on('exist', 'vs_a', function (newVs) {
      vs_a = newVs;
      vs_a.on('request', function onVsARequest(servReq, servResp, next) {
        // REGISTER FOR RESPONSE CALLBACK HERE, IN THE TARGET VS
        servReq.on('response', function processResponseA(cliResp) {
          // PROCESS RESPONSE HERE, IN THE RESPONSE CALLBACK REGISTERED
          // ONCE THE REQUEST ARRIVED AT THE TARGET VS
          cliResp.bindHeaders(servResp);
          servResp.setHeader('X-Forwarded-Through', 'vs_a');
          cliResp.pipe(servResp);
        });
        next();
      });
    });
    
    vsm.on('exist', 'vs_b', function (newVs) {
      vs_b = newVs;
      vs_b.on('request', function onVsBRequest(servReq, servResp, next) {
        // REGISTER FOR RESPONSE CALLBACK HERE, IN THE TARGET VS
        servReq.on('response', function processResponseB(cliResp) {
          // PROCESS RESPONSE HERE, IN THE RESPONSE CALLBACK REGISTERED
          // ONCE THE REQUEST ARRIVED AT THE TARGET VS
          cliResp.bindHeaders(servResp);
          servResp.setHeader('X-Forwarded-Through', 'vs_b');
          cliResp.pipe(servResp);
        });
        next();
      });
    });
    

    For processing, all I did was insert an "X-Forwarded-Through" header to show that the response was processed.

    The first stage is request redirection with newRequest in the requestRedirect() function:

    1. When a server request arrives at vs, randomly choose whether to send it to vs_a or vs_b. You'd obviously replace this with your MySQL logic.
    2. Redirect the server request to vs_a or vs_b using .newRequest.
    3. Since we're done with the server request in vs, call next().

    In the second stage, response interception with onResponse, the request that we redirected with .newRequest pops up in vs_a's or vs_b's request handler. From the perspective of these request handlers, there's nothing special about the request: it could just as well have come off-the-wire from an external client. Now, you do the response interception in onVsARequest or onVsBRequest:

    1. Register an onResponse callback (a listener named processResponseA/B for the 'response' event on the server request).
    2. Call next() to send the request along the pipeline; the normal load balancing logic takes over and sends it to the best backend server for vs_a.
    3. When the response arrives from the server, the processResponseA/B function is invoked with cliResp as the response from the server.
    4. The processResponseA/B function binds the headers from cliResp to servResp
    5. The processResponseA/B function sets an 'X-Forwarded-Through' header (just a dummy header for the sake of example)
    6. The processResponseA/B function pipes the cliResp to servResp.

    Joe, I hope this helps you get your response interception script up and going.

    p.s. when working in these more complex scripts, I often expand "req" and "resp" variables so I can keep track of what kind of request/responses they are:

    • servReq: A node.js-style HTTP.ServerRequest. It is a request that came in from an external client; we are the "server" of this request though we may pass it along to a real server using next().
    • servResp: A node.js-style HTTP.ServerResponse. This is the response that we owe to the client: we have to make sure that we either write to this object ourselves, or pipe to it (as in this example), or forward it along using next() so that a real server can write to it.
    • cliReq: (not in this example) A node.js-style HTTP.ClientRequest where we are the client.
    • cliResp: A node.js-style HTTP.ClientResponse where we are the client. In this example, the cliResp comes from a backend real server, and we just modify it a bit and pipe it through to the external client.