iRule Recipe 1: Single URL Explicit Redirect
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::host
retrieves 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
.- SubrunCirrostratus
Nicely explained...