XML Field Logger Using HTTP Commands

Problem this snippet solves:

This iRule extracts XML field data from HTTP request data using only HTTP commands. (Useful for BIG-IP versions which have not implemented the XML iRules commands.)

This iRule was created to parse XML requests against a fairly simple schema and log a few relevant fields for each request. It obviously lacks any ability to effectively manage repeating sections, other than the potential of using of the tag name + the label data to find the starting point. Enhancements to this very basic offering are welcome. Please post additional iRules with explanation below.

This iRule collects 4 named XML fields by parsing the HTTP request payload for the data following the end of the field tag up to the start of the next tag using the findstr command:

set ProductClass [findstr [HTTP::payload] ":ProductClass>" 14 "<"]

In this case, only the end of the tag was required to be specified as the start value string for the findstr command. The skip value is the length of the start value string.

In other cases, the entire field tag or even some of the field data may be specified as the start value string for the findstr command, depending on the data. For example, the version data was extracted by looking for the string "VER:" in the data immediately following a tag end, rather than looking for the tag name itself:

set Version [findstr [HTTP::payload] ">VER:" 1 "<"]

The skip value is 1 to skip over only the tag end delimiter and capure the entire field value.

If a request does not contain a specified field, the variable is populated instead with the "##" character string to preserve the columnar data layout.

The dataset for the request is then logged to the local2 facility. (The local2 facility may be in use on your system and not appropriate for logging data of this nature. Other logging customizations are possible by modifying the default syslog configuration. Modifications to the syslog config should be made only if you are certain of the ramifications thereof.)

Code :

when RULE_INIT {

   log local0. "0000  Initializing XMLRequestLogging"
}

when HTTP_REQUEST {
   # collect first 1MB of POST data
   if { [HTTP::method] eq "POST" }{
      set clength 0
      if {[HTTP::header exists "Content-Length"] && [HTTP::header Content-Length] <= 1048576}{
         set clength [HTTP::header Content-Length]
      } else { 
         set clength 1048576
      }
      if { [info exists clength] && $clength > 0} {
         HTTP::collect $clength
      }
   }
}

when HTTP_REQUEST_DATA {

   # parse & save xml field data
   set ProductClass [findstr [HTTP::payload] ":ProductClass>" 14 "<"]
   set Serial [findstr [HTTP::payload] ":SerialNumber>" 14 "<"]
   set Retries [findstr [HTTP::payload] ":RetryCount>" 12 "<"]
   set Version [findstr [HTTP::payload] ">VER:" 1 "<"]

   # if no variable value is null, insert placeholder data to preserve column for parsing later
   foreach var {ProductClass Serial Retries Version}{
      log local0. "varname $var [set $var]"
      if {[set $var] == "" }{
         set $var ##
      }
   }
   # log the 4 columns with sourceIP
   log local2. "[IP::client_addr] $Serial $ProductClass $Version $Retries"
}
Published Mar 18, 2015
Version 1.0

Was this article helpful?

No CommentsBe the first to comment