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.


Introduction

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:
  • /forum
  • /modules/node/node.css?p
  • /misc/jquery.js?p
...etc etc The only item we want to parse from this list is "/forum". So a good rule of thumb is anytime an actual file is called, Do nothing. Our script focuses on figuring out if the url passed is an actual file. If it is, then we let it work normally, and if its not, then we prepend the correct parameters to return the correct page.

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:

char buffer[256];
DWORD buffSize = sizeof(buffer);
BOOL bHeader = pHeaderInfo->GetHeader(pCtxt->m_pFC, "url", buffer, &buffSize); 
CString urlOrig(buffer);

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
    urlOrig.Replace("?","&");
    CString urlString = "/index.php?q="+urlOrig;
    char * newUrlString = urlString.GetBuffer(urlString.GetLength());
    pHeaderInfo->SetHeader(pCtxt->m_pFC, "url", newUrlString);
    return SF_STATUS_REQ_HANDLED_NOTIFICATION;

Compile everything and apply it to the website, and your site should run clean.



Comments (0)