Forum Discussion

Brad_53264's avatar
Brad_53264
Icon for Nimbostratus rankNimbostratus
Nov 03, 2008

Web Analytics iRule Style

I am attempting to implement Joe's Urchin solution described here http://devcentral.f5.com/Default.aspx?tabid=63&articleType=ArticleView&articleId=174

The iRule described on the above link is working on 99% of my application. I have one specific page, which just hangs the browser when this iRule is present. Included below is the exact iRule I am using. My modifications include making the urchin.js be local, and added additional logging for troubleshooting. Also included is the logging output (scrubbed) from the problem page. Notice how the CLIENT_CLOSED is not triggered until the 5 minute timeout kicks in.

Any assistance with troubleshooting this issue is greatly appreciated.

 
 when RULE_INIT { 
    replace the value of the _uacct variable with your Google Analytics account. 
     set analytics " 
    
  
  
 " 
   set exist_search "urchinTracker()" 
 } 
 when HTTP_REQUEST { 
  Get time for start of HTTP request 
    set http_request_time [clock clicks -milliseconds] 
  
     Log the start of a new HTTP request 
    set LogString "Client [IP::client_addr]:[TCP::client_port] -> [HTTP::host][HTTP::uri]" 
    log local0.info "$LogString (request)" 
  
  
    Don't allow data to be chunked 
   if { [HTTP::version] eq "1.1" } { 
 log local0.info "HTTP::version = 1.1"   
     if { [HTTP::header is_keepalive] } { 
 log local0.info "is_keepalive = true" 
        HTTP::header replace "Connection" "Keep-Alive" 
 log local0.info "Connection is now Keep-Alive" 
     } 
     HTTP::version "1.0" 
  
 log local0.info "HTTP::version is now 1.0" 
   } 
 } 
 when HTTP_RESPONSE { 
    Received the response headers from the server.  Log the pool name, IP and port, status and time delta 
    log local0.info "$LogString (response) - pool info: [LB::server] - status: [HTTP::status] (request/response delta: [expr [clock clicks -milliseconds] - $http_request_time]ms)" 
  
   if { [HTTP::header Content-Type] starts_with "text/html" } { 
     if { [HTTP::header exists "Content-Length"] } { 
       log local0.info "content length: [HTTP::header {Content-Length}]" 
       set content_length [HTTP::header "Content-Length"] 
     } else { 
       set content_length 1000000000 
     } 
     log local0.info "Collecting $content_length bytes" 
     if { $content_length > 0 } { 
        HTTP::collect $content_length 
     } 
   } 
 } 
 when HTTP_RESPONSE_DATA { 
   log local0.info "Content Type: [HTTP::header Content-Type]" 
   if { ! ([HTTP::payload] contains $::exist_search) } { 
    log local0.info "Payload didn't contain $::exist_search!" 
     set idx [string last "" [HTTP::payload]] 
     if { -1 == $idx } { 
       set idx [string last "" [HTTP::payload]] 
     } 
     log local0.info "html end tag found at $idx" 
     if { -1 == $idx } { 
 log local0.info "-1 == $idx" 
       set offset [HTTP::payload length] 
     } else { 
 log local0.info "-1 != $idx" 
       set offset $idx 
     } 
  
 log local0.info "before HTTP::payload replace" 
     HTTP::payload replace $offset 0 $::analytics 
 log local0.info "after HTTP::payload replace" 
   } 
 } 
 when CLIENT_CLOSED { 
     Log the end time of the TCP connection 
    log local0.info "Closed TCP connection from [IP::client_addr]:[TCP::client_port] to [IP::local_addr]:[TCP::local_port] (open for: [expr [clock clicks -milliseconds] - $tcp_start_time]ms)" 
 } 
 when CLIENT_ACCEPTED { 
     Get time for start of TCP connection in milleseconds 
    set tcp_start_time [clock clicks -milliseconds] 
  
     Log the start of a new TCP connection 
    log local0.info "New TCP connection from [IP::client_addr]:[TCP::client_port] to [IP::local_addr]:[TCP::local_port]" 
 } 
 

Logging

 
  
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : New TCP connection from XXX.XXX.XXX.XXX:20747 to XXX.XXX.XXX.XXX:443 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : Client XXX.XXX.XXX.XXX:20747 -> www.hostname.com/my/uri.do?action=action1 (request) 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : HTTP::version = 1.1 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : is_keepalive = true 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : Connection is now Keep-Alive 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : HTTP::version is now 1.0 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : Client XXX.XXX.XXX.XXX:20747 -> www.hostname.com/my/uri.do?action=action1 (response) - pool info: was XXX.XXX.XXX.XXX 9443 - status: 200 (request/response delta: 247ms) 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : Collecting 1000000000 bytes 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : Content Type: text/html;charset=UTF-8 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : Payload didn't contain urchinTracker()! 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : html end tag found at 60866 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : -1 != 60866 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : before HTTP::payload replace 
 Nov  3 18:55:06 tmm tmm[2931]: Rule urchin_logger : after HTTP::payload replace 
 Nov  3 19:00:06 tmm tmm[2931]: Rule urchin_logger : Closed TCP connection from XXX.XXX.XXX.XXXX:20747 to XXX.XXX.XXX.XXX:443 (open for: 300568ms) 
  
  
 

3 Replies

  • I found the root cause of my issue.

     

     

    "SOL8648: An HTTP connection can stall if collecting a response using HTTP::collect and the response has no Content-Length header"

     

     

    I am running version 9.4.3 with ASM. I've disabled ASM and the problem is now resolved. This known issue has been resolved in 9.4.5.
  • Does anyone know a way to implement the same functionality without using HTTP::collect?
  • That issue is fixed in 9.4.5 so upgrading should resolve the issue. As there have been a lot of significant issues fixed in 9.4.5HF2, it would be good to upgrade regardless of this issue.

     

     

    I'm not sure why the server is responding with no content length header though if you're forcing HTTP 1.0. I thought the only time a server would use no content length header was if the HTTP version was 1.1 and the encoding was chunked.

     

     

    You could insert the HTML using a stream profile and STREAM::expression iRule. It would probably be more efficient than buffering the response payload using HTTP::collect.

     

     

    You could look for in the STREAM::expression and then replace the matched string with plus the HTML for the urchin. However, I'm not sure whether you would be able to only do the insertion if the "urchinTracker()" string isn't already present.

     

     

    Take a look at the STREAM::expression (Click here) and STREAM_MATCHED (Click here) wiki pages for some examples to see if this could work for your scenario.

     

     

    Aaron