Forum Discussion

DocSmooTH_23746's avatar
DocSmooTH_23746
Icon for Nimbostratus rankNimbostratus
Apr 07, 2008

regexp match HTTP::host to choose pool

I couldn't find anything in this forum searching for "regexp http::host" or "regexp matchclass" that fit my problem, but we did get the idea from one of Joe's blog posts.

 

 

We have a bunch of pools that are all essentially the same, from the pool setup, except for the member list and the content on those members. The nodes are the same, just not the backend service ports or members. What we want is the user to be able to hit a hostname, and be sent to the appropriate pool, like:

 

 

http://web-dev --> pool_web-dev (which is http://node:8081)

 

http://web-tst --> pool_web-tst (http://node:8082)

 

http://web-stg --> pool_web-stg (http://node:8083)

 

 

So we have a class defined as follows:

 

class class_web-sids {

 

"web-dev"

 

"web-tst"

 

"web-stg"

 

}

 

 

and an iRule:

 

when HTTP_REQUEST {

 

regexp {^([\w-]+)(\.)*} [HTTP::host] sid

 

if {"" ne $sid } {

 

if { [matchclass $sid equals $::class_web-sids] > 0 } {

 

pool pool_$sid

 

}

 

}

 

}

 

 

If the user hits "http://web-dev", they get passed to the right pool. The problem is if they hit "http://web-dev.domain.com" they get the default pool. With the regexp the way it's written, it should be stopping at the first "." and just giving me the pool matched to the 2nd level of the FQDN, right? What do I need to change to get either "http://web-dev" or "http://web-dev.domain.com" to match to "pool_web-dev" ?

 

 

Thanks!

 

Rob

3 Replies

  • as a note - turning on logging shows that $sid isn't empty.

     

     

    if I change the regexp to [^([\w-]+).*}, the log quits printing anything, at all, and the default pool is hit for all FQDN hits.

     

     

    when HTTP_REQUEST {

     

    regexp {^([\w-]+).*} [HTTP::host] sid

     

    if {"" ne $sid } {

     

    if { [matchclass $sid equals $::class_oracle_sids] > 0 } {

     

    pool pool_$sid

     

    } else {

     

    log local0. "[IP::client_addr]:[TCP::client_port] [HTTP::host] $sid no sid matched."

     

    }

     

    } else {

     

    log local0. "[IP::client_addr]:[TCP::client_port] [HTTP::host] sid empty."

     

    }

     

    }
  • Thanks, that helps, but right now I actually have 10 pools in the F5, with the possibility of more to come - that's why I was originally planning to use matchclass, since we could just add another string to the class, rather than having to edit the rule. This rule only fires on the user's initial connection, some idiot web logic redirects them to a NEW virtual server that requires a port in the backend and runs on java, so I can't just rewrite the stream live (stupid Oracle).

     

     

    so the speed of the rule isn't as important as being able to read it and edit it live later on in the F5's lifecycle.

     

     

    edit:

     

    typing "matchclass" properly helps search terms.

     

    I saw Joe's post about matchclass vs. switch and switch being way faster than if, but the edit-ability is more important in my case, I think. I hope that helps point why I am pushing this way, and just trying to fix my regex. unless I didn't read your answer clearly enough, Colin?

     

     

    Thanks in advance, guys!
  • Taking Joe's idea (Click here) of using catch to handle errors if the pool doesn't exist, you could try this:

    
    when HTTP_REQUEST {
        Get the string from the host header value up to the first period.  Set the string to lower case.
       set parsed_pool pool_[string tolower [getfield [HTTP::host] . 1]]
       log local0. "[IP::client_addr]:[TCP::client_port]: \$parsed_pool: $parsed_pool"
        Try to assign the parsed pool as the pool for this request.  Use catch to handle the error if the pool doesn't exist.
       if {[catch {pool $parsed_pool}]}{
           There was an error in trying to use the pool parsed from the hostname, so use the default pool
          pool default_pool
          log local0. "[IP::client_addr]:[TCP::client_port]: \$parsed_pool didn't exist: $parsed_pool.  Using default pool."
       }
    }

    Or here's a shorter version which expects that there is a default pool assigned to the VIP:

    
    when HTTP_REQUEST {
        Try to assign the parsed pool as the pool for this request.  Use catch to handle the error if the pool doesn't exist.
       if {[catch {pool pool_[string tolower [getfield [HTTP::host] . 1]]}]}{
           The pool parsed from the host header didn't exist.  Do nothing as the default pool will be used.
          log local0. "[IP::client_addr]:[TCP::client_port]: parsed pool didn't exist: pool_[string tolower [getfield [HTTP::host] . 1]].  Using default pool."
       }
    }

    And here's one more version that validates that the parsed pool is actually allowed (defined in a list in the iRule). This would prevent a user from being able to arbitrarily make requests to any pool defined on the BIG-IP:

    
    when RULE_INIT {
        Define a list of the pool names that can be accessed through this iRule.
       set ::allowed_pool_names [list \
          web-dev \
          web-tst \
          web-stg \
       ]
    }
    when HTTP_REQUEST {
        Get the string from the host header value up to the first period.  Set the string to lower case.
       set parsed_pool [string tolower [getfield [HTTP::host] . 1]]
        Check that the parsed pool name is defined in the list.
       if {[matchclass $parsed_pool equals $::allowed_pool_names]}{
           Assign the parsed pool for this request.
          pool pool_${parsed_pool}
          log local0. "[IP::client_addr]:[TCP::client_port]: Using pool pool_${parsed_pool}"
       }
    }

    Aaron