HOWTO: Redirect the browser to a new URL based on Referer


While I do not recommend users to freely write ISAPI Filters and install them on IIS, there are still tasks that favor/require ISAPI Filter… even on IIS6 with HSE_REQ_EXEC_URL.


Question:


Hello David,


I came across your blog several times when I am searching for “ISAPI extensions and filters for IIS6”. As a newbie on ISAPI and C++, I am really confused and hope that you can provide me with some insights……


In IIS 6 default processing mode, it does not support filters which subsribed to the SF_NOTIFY_READ_RAW_DATA event and the MSDN recommandation would be to write an ISAPI extension that does wildcard application mapping and make use of the new HSE_REQ_EXEC_URL function.


I noticed that your blog also has some samples regarding ISAPI filters, do you have any samples\resources for creating ISAPI Extension? Especially how to use this HSE_REQ_EXEC_URL function? (All I want to do is to intercept the web request, check the referer info and modify a query string on the URL and output the new URL onto the client’s URL address bar.)


Before I go on and create this extension object, do you know anything that I should be aware of (like side impacts)?


Long email…… hope you don’t mind


Regards,


Answer:


Actually, what you want to do can be accomplished with either ISAPI Filter or ISAPI Extension. For just the tasks you stated, I recommend ISAPI Filter; if there are other related tasks, it may change my recommendation… but I do not have that information from you at the moment.


In other words, ISAPI Filter can easily read/modify all parts of the request except for entity body, and the entity-body is what requires SF_NOTIFY_READ_RAW_DATA. Since you only want to read the Referer header and then send back a 302 response with a Location header that has the querystring modified (there is no way for a server to change the client’s URL bar without the client making a new request – so that is why you send back a 302 response instead of directly changing the URL on the server), this can easily be done with an ISAPI Filter. SF_NOTIFY_READ_RAW_DATA is not involved here, so there is no prejudice against the ISAPI Filter approach.


Of course, you can also do this with an ISAPI Extension configured as a wildcard application mapping which reads the Referer header from the ALL_RAW server variable and then either sends back a 302 response or calls HSE_REQ_EXEC_URL to pass the request along.


Personally, the tasks you want to perform are easily done with an ISAPI Filter whereas you need to do parsing and know how to do asynchronous programming with an ISAPI Extension. Which one you prefer is completely up to you.


The ISAPI Filter Route


If you want to go the ISAPI Filter route, then I have a sample ISAPI Filter which uses the Referer header to determine whether to do some action. The action that you want to perform is to send back a 302 Redirection with the new Location header. My sample filter already has code showing how to send back a “403 Forbidden” with some custom headers – you just need to modify it send back a “302 Redirection” (easy) and then a “Location: <new-url-with-querystring>” header. Your code modifications should look something like:

#define MAX_URL_LENGTH 1024

CHAR szUrl[] = “url”;
CHAR szBuf[ MAX_PATH ];
CHAR * pszUrl = szBuf;
BOOL fRet = FALSE;
DWORD cbUrl = MAX_PATH;

//
// First try to retrieve the URL with max
// buffer size of MAX_PATH
//
fRet = pfc->GetHeader( szUrl, pszUrl, &cbUrl );
if ( FALSE == fRet )
{
//
// Failed to retrieve URL because it was
// longer than MAX_PATH. If the URL is
// less than MAX_URL_LENGTH, try again.
// Otherwise, fail.
//
if ( ERROR_INSUFFICIENT_BUFFER == GetLastError() &&
cbUrl < MAX_URL_LENGTH )
{
//
// Always check return value of memory
// allocation for failures so that
// you do not crash
//
pszUrl = new CHAR[ cbUrl ];
if ( NULL == pszUrl )
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
goto Finished;
}

fRet = pfc->GetHeader( szUrl, pszUrl, &cbUrl );
if ( FALSE == fRet )
{
goto Finished;
}
}
else
{
//
// Failed to retrieve URL — fail
// since we cannot redirect without it
//
goto Finished;
}
}

//
// At this point, pszUrl contains the desired URL.
// You can do whatever you want to manipulate the
// querystring to come up with the desired Location:
// header to send on SF_REQ_SEND_RESPONSE_HEADER
//

Finished:

if ( pszUrl != szBuff &&
pszUrl != NULL )
{
delete pszUrl;
pszUrl = NULL;
}


The ISAPI Extension Route


If you want to go the ISAPI Extension route, then there is already a Wildcardmap sample in the IIS Platform SDK that does the bulk of what you need. All you need to do is add the following logic prior to the HSE_REQ_EXEC_URL invocation:



  1. Call GetServerVariable to retrieve the ALL_RAW variable and parse it for the Referer: header
  2. If it matches your criteria:

    Call GetServerVariable to retrieve the URL, PATH_INFO, and QUERY_STRING variables, manipulate the querystring, and generate the necessary Location header.

    Call HSE_REQ_SEND_RESPONSE_HEADER to send the 302 redirection
  3. Otherwise, call HSE_REQ_EXEC_URL

See the following blog entry on how to use GetServerVariable correctly.


Conclusion


For your task of using the Referer request header to send back a 302 redirection with a custom Location header, both ISAPI Filter and ISAPI Extension can be used. The facilities for request manipulation are a bit better in ISAPI Filter in this regard, so it is my preferred approach. However, if there are other arbitrary tasks that need to be simultaneously performed, it may change my preference.


//David

Comments (4)

  1. Mike says:

    Using 301 redirects is much better, since search engines still respect them but are known to discount 302 redirects.

    My understanding of the ISAPI filter method is that it’s only a few percent (overall roundtrip time including DNS resolution & HTTP transfer) than using 301 redirects in IIS config (http://support.microsoft.com/kb/324000/EN-US/) or an .htaccess file (http://support.microsoft.com/?kbid=324064) – and only marginally faster than Response.status or Response.redirect from an ASP.Net page.

    Is this true, or does an ISAPI filter have additional advantages?

  2. David Wang says:

    Mike – The goal here is to send a redirection response based on a request header value, else do nothing and pass the request processing onward.

    Your classification of redirection performance is reasonable because ISAPI Filter can decide to short-circuit IIS request processing, thus shaving a few percentage points in timing vs IIS configured redirection. Both ASP and ASP.Net Response.Redirect are slower because their coded logic needs to be compiled and then cached.

    So, your understanding is valid from the perspective of sending the response.

    However, from the perspective of "do nothing and pass the request onward", only ISAPI Filter is going to give you that ability. As soon as your ASP or ASP.Net code gets the request to make the determination of whether to send a response or not, it cannot pass control of the request back to IIS.

    Of course, with ASP.Net 2.0 and IIS6, ASP.Net gets the ability to hand the request back to IIS, but in general, the ability to inject arbitrary logic into request handling without owning it is the key feature of ISAPI Filter. IIS6 blurs the line a little with HSE_REQ_EXEC_URL and wildcard application mapping (and ASP.Net 2.0 brings a small portion of HSE_REQ_EXEC_URL to managed code), but if you look at how they interact with IIS Request processing, the distinction between ISAPI Filter and Extension should be clear.

    //David

  3. Sydney Barnard says:

    David, I would like to view the sample code for the ISAPI Filter, however the link seems to be 404.

    Thanks

    -Sydney Barnard

Skip to main content