Series Introduction

Let's face it: there aren't many people out there who have extensive experience with Tcl. Since iRules is a Tcl dialect, that means that finding a solid iRules solution can be challenging, even for otherwise experienced coders. And many times, those who architect, configure, troubleshoot and manage BIG-IPs don't normally code as part of their day job. For both groups, most problems can be solved by finding iRules code that tackles a problem similar to one they face, modifying it just a bit to suit the specific need. This series hopes to provide that sort of code: "recipes" that capture patterns used by BIG-IP practitioners to solve common problems.

Each article is a single recipe. The article begins by explaining a problem you might be trying to solve. It then presents an iRule that can solve it. After that, it provides an analysis of the iRule. Finally, it provides a more detailed background section to elaborate on how the supporting protocols work and any nuances regarding the problem or solution.

In some cases, there is a non-iRules method for solving a problem. When this the case, there will be one more section, called "How Else Could I Have Solved This?" This section outlines the alternative solution or solutions.

Recipe 1: Single URL Explicit Redirect

The Problem

You want to respond to a specific HTTP URL with an explicit HTTP Redirect, using a 301 Status Code and the Location header.

The Code

when HTTP_REQUEST {
  if { [string tolower [HTTP::host]] eq "www.example.com" and [HTTP::path] eq "/path1/abc" } {
    HTTP::respond 301 Location "http://www.new-example.com/abcd"
  }
}

Analysis

When this rule is applied to a Local Traffic Virtual Server with the http profile applied, then any request for www.example.com/path1/abc will be explicitly redirected to http://www.new-example.com/abcd. If the Virtual Server includes the clientssl profile -- which means the Virtual Server is offloading TLS/SSL -- then the Location value in this iRule should be changed to https://www.new-example.com/abcd. This is a simple iRule, and is intended to solve a simple problem.

iRules are triggered by events. The set of profiles applied to a Virtual Server determines the events that will fire. If a Virtual Server has the http profile applied, then -- among others -- the HTTP_REQUEST event will fire. It happens when an HTTP Request message is received, and the BIG-IP has completely parsed the Request message headers. Code attached to the HTTP_REQUEST event usually reads headers or the HTTP start-line, and may modify either of them.

HTTP::hostretrieves the value of the Host header (if one isn't present, then it is the empty string), while HTTP::path returns the path part of the Request Target (see below for a bit more detail on this). The string tolower is needed for the Host header, because hostnames are case-indifferent. That is, www.example.com is the same name as wWw.ExAmPlE.cOm and the name can be expressed either way (and, of course, many other ways besides). string tolower transforms all letters in the text that follows (HTTP::host, in this case) to all lower-case. By normalizing case, we can reliably compare its value.

Notice that I do not use string tolower on the HTTP::path The HTTP RFC says that the Request Target (including the path) is case-sensitive. This can get a bit messy because many web servers simply pass the path to the underlying filesystem, which may or may not be case-sensitive. However, assuming that your web server honors this requirement, then /some/path is not the same as /SOME/path, for example, so normalizing case is not only not valuable, it is not correct.

One more interesting point: there is an HTTP::redirect iRules command. Why didn't I just use that instead of HTTP::respond? HTTP::redirect uses a 302 Response Code. 302 is a Temporary Redirect, which encourages the user-agent to try and use the original URL on subsequent requests. Moreover, many user-agents improperly implemented the 301 (transforming any HTTP method to a GET on the redirect).  Most of the time, what you mean is a Permanent Redirect, which tells the user-agent not to try the original URL again. Perhaps it would have been better if HTTP::redirect used 301, or at least allowed you to specify which Response Code you wished, but alas, it does neither.

Elaboration

Remember! This section goes into more details about HTTP and how it relates to iRules. I believe it can be quite useful, but if all you needed was the recipe, feel free to skip this section.

HTTP transactions are stateless, and always consist of exactly two messages: a Request message followed by a Response message. Even if more than one transaction is conducted on a single TCP connection (called HTTP KeepAlive), each transaction is, at the level of HTTP, independent from all others.

An HTTP message consists of a start-line, zero or more headers, and a body. For some request message types, the body is empty. With HTTP/1.0, the header set can be empty, but with HTTP/1.1, at least the Host header is required in Request messages, and generally, Response messages always have at least one header.

When a request is made from a user-agent that looks like this:

http://www.example.com/some/path?here=there&this=that#section1

the http is the "scheme" and tells the user-agent how to treat the rest of the URL. www.example.com is sent as the value of the Host header. /some/path is the HTTP Request path, here=there&this=that is the HTTP query parameter set, and #section1 is the fragment. This is transformed into an HTTP Request message that starts like this:

GET /some/path?here=here&this=that HTTP/1.1
Host: www.example.com

The second element of the start-line (that is, /some/path?here=&there;this=that) is called the Request Target. Originally, the RFCs called it the Request URI. This is confusing because a fully-qualified URL is an instance of a URI, but subsets of a URL are not. So really, while http://www.example.com/some/path is a URI (and a URL), /some/path is not. iRules uses HTTP::uri to retrieve the Request Target, because again, that part used to be called the Request URI.  But don't get caught: HTTP::uri does not return the complete URL/URI.

Notice that the fragment (#section1) is not transmitted. A fragment has meaning only to the user-agent, and is a hint about where to center a rendered document.

How Else Could I Have Solved This?

Starting in BIG-IP version 11.4, Local Traffic Policies were introduced. Describing this feature is -- as they say -- outside the scope of this article, but in brief: it's a really cool way to do common HTTP transforms, including redirects. It has the advantage of not being code, and it is part of the built-in feature set. Local Traffic Policies can be modified using the BIG-IP web UI or tmsh.