HTTP status code equivalents for file:// operations - compat with xhr

Currently, xhr's this.status model doesn't work too well with file://.

One big reason for this is that browser implementations don't simulate  
HTTP status codes for file:// operations. In short, this.status always  
equals 0, which means you have to do something like this:

var req = new XMLHttpRequest();
req.onreadystatechange = function() {
     if (this.readyState === 4) {
         if (this.status === 200 || document.location.protocol === "file:")  
{

         }
     }
};
req.open("GET", "local_file.txt");
req.send();

You could also try (this.status === 200 || this.status === 0) if you  
assume that the status will only be 0 for "file:" and no other condition.

Even then, depending on the browser implementation, if the local file is  
not found, you might get an exception thrown in one browser or no  
exception thrown and just this.responseText set to "" in another.

Further, for file://, you can't tell the difference between a file not  
found error and a cross-origin attempt error (or an empty file in some  
cases). You also can't specifically tell if the error is because access to  
the file is denied.

Although XHR wasn't designed for file:// and its behavior remains  
undefined in the spec, I believe simulating HTTP status codes for file  
operations could help big time with these problems.

I'm not saying file: support should be added to the XHR spec, but there  
should be some 'file:// for XHR' guidelines that browsers could use.

file:// should really be a first class citizen, imo.

Example:

var req = new XMLHttpRequest();
req.onreadstatechange = function() {
     switch (this.readyState) {
         case 0: // Unsent state
             break;
         case 1: // Open state
             break;
         case 2: // Headers_Received state
             break;
         case 3: // Loading state
             break;
         case 4: // Done state
             switch(this.status) {
                 case 200: // OK
                     break;
                 case 404: // Not Found
                     break;
                 case 401: // Authorization required
                     break;
                 case 403: // Forbidden
                     break;
                 case 405: // Method not allowed
                     break;
                 case 501: // Not implemented
                     break;
                 case 414: // Request URI too long
                     break;
                 case 415: // Unsupported mime type
                     break;
                 case 0: // Done state error flag is true for some reason
                     break;
                 default: // Unsupported status codes
             }
             break;
         default: // Unsupported states
     }
};
try {
     req.open("GET", "local_file.txt");
     req.send();
} catch(error) {
     alert(error);
}

1. For file:// access, make sure this.readyState always reaches 4.

2. For file:// access, simulate the appropriate HTTP status code for the  
result so that this.status and this.statusText can be used properly.

3. Don't have open() throw for "file not found" and use 404 for  
this.status instead.

4. If file:// access isn't implemented (like in IE), don't have open()  
throw. Instead, make this.status be 501.

5. If the request URI result in too long of a path, use status code 414.

6. If the request URI points to a certain type of file the *browser*  
doesn't want to allow access to (maybe an .exe), use status code 415.

7. If a method besides "GET" is used for open(), don't throw and use  
status code 405.

8. Don't have open() throw for cross-domain security restriction. Instead,  
use status code 403.

9. If the user doesn't have access to the file, don't have open() throw  
and instead use status code 401.

10. responseText and responseXML should be null unless the status is 200.

11. For any other errors on .open(), SYNTAX_ERR or SECURITY_ERR should be  
throw.

Easier Alternative:

1. Make sure this.readyState always makes it to 4.

2. Make open() throw for everything. The only time it would not throw is  
if the file is found and accessible.

3. Make this.status equal 200.

4. For #1, throw different types of errors for each case. File not found,  
access denied, cross-origin attempt etc.

Ulimately though, what I think would be great is if you could use XHR on  
HTTP or locally (on file://) while using the exact same code to load a  
file.

For example, the following should work exactly the same on both file and  
http.

var req = new XMLHttpRequest();
req.onreadystatechange = function() {
     if (this.readyState === 4) {
         if (this.status === 200) {
             alert(this.responseText);
         } else if (this.status === 404) {
             alert("file not found");
         }
     }
};
req.open("GET", "file_in_same_dir.txt");
req.send();

Also, some have suggested that DOM3 Load & Save and document.load are not  
needed if you have XHR (and DOMParser for the XML part). However, current  
browser implementions of XHR file:// support can't simulate DOM3 Load &  
Save's local file loading support completely. The error handling just  
isn't there.

I realize XHR isn't made for file://, but browsers are already shoehorning  
file:// into XHR. Might as well make things better.

If XHR and file:// work nicely, then one could wrap things into a nice  
localLocalFile() function. Currently though, file:// + XHR doesn't make  
for a very good low-level API that you can wrap.

-- 
Michael

Received on Tuesday, 18 August 2009 07:19:55 UTC