HTTP POST redirect preserving POST data

Problem this snippet solves:

Use Javascript in an iRule to redirect HTTP POST requests to HTTPS. When an HTTP 30x redirect it sent to a client that has sent a POST request, the user-agent transparently issues a new GET request. As a result, the original POST request payload is lost.

The idea behind this iRule is when client sends a POST to an HTTP virtual server, LTM replies with an HTML page which contains a form. The form contains post-data that the client just sent. The form will be auto-submitted by Javascript via HTTPS.

Here is how this iRule works:

  1. if method = post, it issues HTTP::collect, which will invoke HTTP_REQUEST_DATA
  2. then it scans POST-data and prepares new content to respond to client. the new content will include
    • form with all INPUT field names retrieved from POST-data
    • the "action" parameter in the form points to external server
    • all input fields are set as hidden (so client won't see the data during the process)
    • Javascript that submits the form automatically
  3. then iRule replies to client with HTTP::respond command with content prepared in step 2

Code :

when RULE_INIT {
    set static::ext_url "https://10.10.71.3/test.post"
}
when HTTP_REQUEST {

   # Check if request was a POST
   if { [string tolower [HTTP::method]] eq "post" } {

      # Check if there is a Content-Length header
      if { [HTTP::header exists "Content-Length"] } {

         if { [HTTP::header "Content-Length"] > 1048000 }{

    # Content-Length over 1Mb so collect 1Mb
            set content_length 1048000

 } else {

    # Content-Length under 1Mb so collect actual length
            set content_length [HTTP::header "Content-Length"]
         }
      } else {

 # Response did not have Content-Length header, so use default of 1Mb
         set content_length 1048000
       }
       # Don't collect content if Content-Length header value was 0
       if { $content_length > 0 } {
          HTTP::collect $content_length
       }
    }    
}
when HTTP_REQUEST_DATA {
    set content "< script type=text/javascript language=javascript> \
        function s(){ document.f.submit(); }  \
        
" foreach p [split [HTTP::payload] &] { set name [URI::decode [getfield $p = 1]] set value [URI::decode [getfield $p = 2]] set content "${content}" } set content "${content}
" HTTP::respond 200 content $content }
Published Mar 18, 2015
Version 1.0

Was this article helpful?

4 Comments

  • Hi,

     

    A 307 status code is created to ask the client to preserve the method and the content on the new location

     

  • @victor Plohod, try this irule

    when HTTP_REQUEST {    
         Check if the client used an SSL cipher 
        if {not ([catch {SSL::cipher version} result]) && [string tolower $result] ne "none"}{    
             Client did use a cipher 
            log local0. "\$result: $result. Allowing encrypted request." 
        } else { 
             Client did not use a cipher 
            log local0. "\$result: $result. Redirecting unencrypted request."
            if { [HTTP::method] eq "POST" } {
                HTTP::respond 307 "https://[HTTP::host]/"
            } else {
                HTTP::respond 302 "https://[HTTP::host]/"
            }
        } 
    }
    
  • Hello,

     

    Am curios about the statement:

     

    set static::ext_url ";

     

    is test.post generic and able to be used anywhere, or would I need to replace it with an entry specific to my environment?

     

    Thanks in advance,

     

    Chris