HTTP requests are not explicit, as they only provide a relative path to the resource that is being requested. For example, if a request is made to [citronautconsulting.com/blog/] there is no filename or extension. Additionally, what if [blog] is a plaintext file itself? Or if it is a directory, what is the file being requested? In Node.js, it is fairly simple to discern the necessary resource, and see if it exists.
In Apache, if a directory is requested by a HTTP GET request, it will take the relative location and append the default DirectoryIndex variable to the end of it. For example, a few of the typically configured DirectoryIndex files are: index.htm, index.html, index.php, home.htm, home.html, etc. In this case, we have several cases to check for:
- Does the provided path exist (as either file or directory)?
- If it does, is it a directory?
- If it is a directory, does the Directory + DirectoryIndex exist?
If the filepath does not exist, then we need to respond to the HTTP client with a 404 error. If it does exist, then we need to respond with a 200 status, and return the requested resource.
Before we can start checking if the path exists, we need to first transform the requested relative path into an absolute path. We can do this by having a configuration object specify the relative home directory, such as a JSON object:
"DirectoryIndex": "index.html",
"virtualNodes" : {
"citronautconsulting.com" :
{ "DocumentRoot" : "/home/citronau/public_html/",
"Username" : "citronau",
"ServerAdmin" : "webmaster@citronautconsulting.com" }
}
}
In the above JSON object, we have defined not only our server’s default DirectoryIndex, but we have defined the DocumentRoot of the domain [citronautconsulting.com]. When a request for [citronautconsulting.com] comes to the sever, we will now know the absolute file path is [/home/citronau/public_html]. As such, if a request for [citronautconsulting.com/blog] comes in, the file path is [/home/citronau/public_html/blog]. From here, we can define the following variables:
docRoot = config.virtualNodes[reqHost].DocumentRoot, // get the document root directory
urlPath = path.normalize(request.url), // normalize input url before joining
filePath = path.join(docRoot + urlPath), // the input is now sanitized from /..
dirIndex = config.directoryIndex, // get the DirectoryIndex
indexedPath = path.join(filePath, dirIndex); // append the DirectoryIndex to the filePath
Now that we have the file path variables available to work with, we can begin checking if the requested resource exists, and we will do so in the order of aforementioned cases:
if(exists){ // So filePath exists - in what form?
fs.stat(filePath, function(stat) {
if(stat.isDirectory(filePath)){ // Is filePath a directory?
path.exists(indexedPath, function(exists) { // It is; try the indexedPath
if(exists)
// Read the file & send if no error (200); send (500) if error
else
// Send 404 error message: File Not Found
});
}
else
// Read the file & send if no error (200); send (500) if error
});
}
else
// Send 404 error message: File Not Found
});
So there you have it – this will correctly provide the requested resource to the client, given that it exists – if not, it will return a 404 message. For good programming, use separate functions for each HTTP status response code, in order to prevent logic repetition.
Tags: directory index, node.js, router, routing, url
it was very interesting to read joshdulac.com
I want to quote your post in my blog. It can?
And you et an account on Twitter?
Yes you can quote me, just leave a link back =).
Follow me at twitter.com/joshdulac