Isapi Filter for Drupal Clean Urls
Do you need to create a drupal site on IIS and would also like to use clean urls? There are other solutions, including installing isapi_rewrite. But if you are looking to write your own isapi filter (which is not that hard), you have come to the right place. This filter was tested on IIS6.
You can download the source and dll here: http://awoprojects.googlecode.com/files/dru-isapi-filter-1.0.0.zip
The first thing you would need to do is familiarize yourself with isapi filters. Take a look at this posting for a good introduction: http://www.codeproject.com/KB/ISAPI/isapiredirector.aspx.
How it Works
We are going to want to edit the OnPreprocHeaders function. This function is called after the server has preprocessed the client headers. Take for example a drupal site at test.com. Somebody visits the site and goes to http://www.test.com/forum. The urls sent to this function include the url typed and the many includes within the page. So a list of headers entering this function could look like this:
Break down the source code
Right, now that we are all caught up, lets edit the OnPreprocHeaders function.
The first thing we need to do is retreive the url from the header info:
DWORD buffSize = sizeof(buffer);
BOOL bHeader = pHeaderInfo->GetHeader(pCtxt->m_pFC, "url", buffer, &buffSize);
Your server is probably set to go to index.php if no specific page is chosen. So if I pulled up test.com, the url does not need to be cleaned as it defaults to index.php. In this step, we verify its not the home page before moving on:
// default handler if index.php
if(urlOrig == "" || urlOrig == "/") return SF_STATUS_REQ_NEXT_NOTIFICATION;
Next, we parse the url by making it lowercase, removing any "?" and anything after it, and grabbing the last 3 and 4 characters. We remove the "?" and anything after it, because drupal has a habit of grabbing files like this: "/modules/node/node.css?p". In this scenerio, we do not want to parse as a clean url, we want to return the actual file.
string tmpstr = urlOrig;
if(urlOrig.Find("?") != -1) tmpstr.erase(urlOrig.Find("?"));
string ext3 = tmpstr.substr(tmpstr.length()-4);
std::transform(ext3.begin(), ext3.end(),ext3.begin(), ::tolower); // lower case
string ext2 = ext3.substr(1);
Now we do the validation and verify that the url is not a file. If it is, we let the default handler handle it. The list of file types can probably be alot more extensive, but for my site this worked just fine.
// default handler if calling a file directly
if( ext3 == ".php"
|| ext2 == ".js"
|| ext3 == ".css"
|| ext3 == ".gif"
|| ext3 == ".png"
|| ext3 == ".jpg"
|| ext3 == ".ico"
|| ext3 == ".txt"
) return SF_STATUS_REQ_NEXT_NOTIFICATION;
And finally, if we determine that the url is clean, we just prefix it with index.php?q=. In our above test, when the user enters test.com/forum, that is what he/she would see. But what the preprocessor will return to pull up would be test.com/index.php?q=/forum. This is what we want.
// otherwise assume clean url and parse
CString urlString = "/index.php?q="+urlOrig;
char * newUrlString = urlString.GetBuffer(urlString.GetLength());
pHeaderInfo->SetHeader(pCtxt->m_pFC, "url", newUrlString);
Compile everything and apply it to the website, and your site should run clean.
The source can be found here: http://awoprojects.googlecode.com/files/dru-isapi-filter-1.0.0.zip