STREAM_MATCHED

Description

Triggered when the stream profile matches some data-stream octets* to a match value (regular expresssion) specified in the Stream Profile or by an iRule using STREAM::expression.
(*See STREAM::encoding for details on how data-stream octets are matched.)
This event may be used to examine the matched string (see STREAM::match) and optionally use STREAM::replace to specify a replacement string or avoid replacing the matched data at all.

Examples

This example shows how you can use STREAM::match in the STREAM_MATCHED event to check if the matched string meets some condition that can’t easily be checked for using a regular expression in STREAM::expression.
when HTTP_REQUEST {
  # Disable the stream filter for all requests
  STREAM::disable
}

when HTTP_RESPONSE {
  # Check if response type is text
  if {[HTTP::header value Content-Type] starts_with "text"} {

    # Match an "http://imageserver-NNNN.example.com" URL (and replace
    # it with nothing yet.  We'll figure out the replacement later)
    STREAM::expression {=http://imageserver-[0-9]{1,4}\.example\.com==}

    # Enable the stream filter for this response only
    STREAM::enable
  }
}

when STREAM_MATCHED {
  # Check if the matched string meets some condition that can't easily be checked
  # for using a single regex in STREAM::expression.  For example, regex's are not
  # very good at arithmetic

  # extract the imageserver number
  regexp {http://imageserver-([0-9]+)[.]example[.]com} [STREAM::match] url server_nbr

  # want to switch protocol to HTTPS only for newer imageservers, numbers over 379
  if {$server_nbr > 379} {
    set url [string replace $url 0 3 "https"]
  }

  # now put the URL back into the data stream (otherwise it will be deleted by the
  # empty replacement value we set with STREAM::expression)
  STREAM::replace $url

  log local0.info "[IP::client_addr]_[TCP::local_port]: matched [STREAM::match], replaced with: ${url}"
}

Log output:
Rule stream_expression_rule <STREAM_MATCHED>: 10.0.0.1_3413: matched: http://imageserver-99.example.com, replaced with: http://imageserver-99.example.com
Rule stream_expression_rule <STREAM_MATCHED>: 10.0.0.1_3413: matched: http://imageserver-425.example.com, replaced with: https://imageserver-425.example.com

This example just reads the stream match in the STREAM_MATCHED event and adds a persistence record.
# Look for a JSESSIONID cookie in response content and add a persistence record when it is found.
# To use STREAM commands a Stream Profile must be attached to the virtual server.  Use /Common/stream.

when HTTP_RESPONSE {
  # Clear the jsessionid if it exists already on this TCP connection
  if {[info exists jsessionid]} {
    unset jsessionid
  }

  # Only look for the jsessionid in text responses
  if {[HTTP::header value "Content-Type"] starts_with "text"} {
    log local0.info "[IP::client_addr]_[TCP::client_port]: text response, enabling stream profile"

    # match the JSESSIONID string and value
    # We'll parse out the value in STREAM_MATCHED
    # Assume the jsessionid is 1 to 100 alphameric characters
    STREAM::expression {/Set-Cookie: [^\s]+;JSESSIONID=[A-Za-z0-9]{1,100}//}
    STREAM::enable

    # Enable the STREAM_MATCHED event as it could have been disabled if there was a prior
    # response on this TCP connection
    event STREAM_MATCHED enable

  } else {
    # Disable the stream filter as this wasn't a text response
    log local0. "[IP::client_addr]_[TCP::client_port]: No Content-Type match, disabling stream filter"
    STREAM::disable
  }
}

when STREAM_MATCHED {
  # Save the matched value (example: ;JSESSIONID=a9bh35uy)
  regexp {Set-Cookie: [^\s]+;JSESSIONID=([A-Za-z0-9]{1,100})} [STREAM::match] junk jsessionid
  log local0.info "[IP::client_addr]_[TCP::client_port]: Found JSESSIONID=${jsessionid}"

  # Prevent deletion of matched data which will occur if we don't do something (because
  # the stream expression replacement value is empty)
  # (STREAM::replace with no argument means don't replace anything, just leave it alone)
  STREAM::replace

  # Not sure why, but the parser doesn't allow the persist command in some TMOS versions
  # It works though, so hide the command from the parser
  set persist_cmd "persist add uie ${jsessionid}"
  log local0.info "[IP::client_addr]_[TCP::client_port]: Will persist on JSESSIONID (command '${persist_cmd}')"

  eval $persist_cmd

  # Assume the first match is the same as any other jsessionids, so stop checking for them
  log local0.info "[IP::client_addr]_[TCP::client_port]: Added persistence record. No longer scanning this response for JSESSIONID"
  event STREAM_MATCHED disable
}

Log output:
Rule persist_on_response_content_stream_rule <HTTP_RESPONSE>: 1.2.3.4_2418: text response, enabling stream profile
Rule persist_on_response_content_stream_rule <STREAM_MATCHED>: 1.2.3.4_2418: Found JSESSIONID=a111111111111111111z
Rule persist_on_response_content_stream_rule <STREAM_MATCHED>: 1.2.3.4_2418: Will persist on JSESSIONID (command 'persist add uie a111111111111111111z')
Rule persist_on_response_content_stream_rule <STREAM_MATCHED>: 1.2.3.4_2418: Added persistence record. No longer scanning this response for JSESSIONID