For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

HTTP method conversion

Problem this snippet solves:

This is one way that allows you to convert HTTP method from GET to POST or POST to GET transparently by LTM.

Why?

  • you want to hide something from users. (make it simpler for them)
  • create meta proxy that also proxy auth and does auto-logon (or limited SSO)
  • I don't know. I think it is fun.

What technique are used here?

  • vip targeting vip
  • HTTP::retry
  • HTTP::respond

How does it work?

  • configure 2 virtual servers one for normal HTTP load balance and another one to receive http request from virtual www1 and return back converted request. For example virtual www1 listens on 10.10.10.10:80 and point to pool webpool. Virtual www2 listen on 10.10.10.10:81 and has no pool assigned.
  • virtual www1 receives http request from external clients and send selected request to virtual www2
  • virtual www2 converts http request (for GET-to-POST, turn GET uri query string to POST data and for POST-to-GET, turn POST data to GET uri query string)
  • virtual www2 responds back with "converted request" as its content
  • virtual www1 receives HTTP_RESPONSE and do HTTP::retry with this "converted request"

Note: * This iRule is not perfect. It needs more development if you would like to use it. Feel free to put comment.

Code :

# rule for virtual www1
when HTTP_REQUEST {
    log local0. "[HTTP::request]"
    # send httprequest which you want to convert to another virtual
    # if [HTTP::uri] match what I want
    virtual www2
    # else do nothing
}
when HTTP_RESPONSE {
    # use special status and/or remote_addr to identify
    # whether it comes back from real server or virtual www2
    if { [HTTP::status] == 555 } {
        HTTP::collect [HTTP::header Content-Length]
        set reselect 1
    }
}
when HTTP_RESPONSE_DATA {
    # probably add routine to verify
    # send converted http_request to real server
    log local0. "[HTTP::payload]"
    HTTP::retry [HTTP::payload]
}
when LB_SELECTED {
    # if http_retry is called, same destination may be picked (which is virtual www2)
    # change the destination to real server (use node or pool command)
    if { [info exists reselect ] } {
        LB::reselect node 10.10.72.1 80
        unset reselect
    }
    log local0. "[LB::server]"   
}

# rule for virtual www2
when HTTP_REQUEST {
    switch [HTTP::method] {
        GET {
            log local0. "uri = [HTTP::uri]"
            log local0. "query = [HTTP::query]"
            set query [HTTP::query]
            # strip off query string
            HTTP::uri [HTTP::path]
            # add Content-Type and Content-Length header
            HTTP::header insert Content-Type "application/x-www-form-urlencoded"
            HTTP::header insert "Content-Length" [string length $query]
            # strip off "GET "
            set request [substr [HTTP::request] 4]
            # create new request starts with POST
            # follow by original header (with modified uri)
            # and POST data (derived from query string)
            # according to http encoding, some characters may need attention
            # for example, space is %20 in GET uri but + in POST data
            HTTP::respond 555 content "POST $request$query"
        }
        POST {
            log local0. "len = [HTTP::header Content-Length]"
            log local0. "req = [HTTP::request]"
            # collect POST data
            HTTP::collect [HTTP::header Content-Length]
        }
    }
}
when HTTP_REQUEST_DATA {
    # prepare GET uri query string
    set query [HTTP::payload]
    # according to http encoding, some characters may need attention
    # for example, space is %20 in GET uri but + in POST data
    HTTP::uri "[HTTP::uri]?$query"
    # we don't need Content-Type and Content-Length anymore
    HTTP::header remove Content-Type
    HTTP::header remove Content-Length
    # remove POST data
    HTTP::payload replace 0 [HTTP::payload length] ""
    # strip off "POST "
    set request [substr [HTTP::request] 5]
    HTTP::respond 555 content "GET $request"
}
Published Mar 18, 2015
Version 1.0

2 Comments

  • Hi there. Thanks for the idea for this - I've managed to streamline this into a single virtual/iRule and the code is here https://devcentral.f5.com/s/feed/0D51T00006j2lHBSAY at the bottom, but it's based on my reading this post.