HTTP based read-only files system
I've become thoroughly fed up with the lack of progress in the space of web-based IDEs and text editors.
While there are several editors which can run in the browser, all of the ones I've seen lack a pretty basic feature: save. I've seen one project which can save files by uploading them to your own server via (S)FTP, which would merely be horridly slow if it worked, but it doesn't. So much for that.
So I've decided to write as dumb an editor as possible on AppEngine. The editing part I'll just rip off from one of the above editors (probably CodeMirror), the saving is trivial - POST data and shove it in data store, but the reading is a little tricky. How do I expose the saved files in a meaningful, but simple way
I've looked at the following protocols:
This is also quite extensible (e.g. I could easy add a Stat method), and could very easily be given a FUSE front-end.
Any comments? Worries? A list of solutions I'm reinventing?
While there are several editors which can run in the browser, all of the ones I've seen lack a pretty basic feature: save. I've seen one project which can save files by uploading them to your own server via (S)FTP, which would merely be horridly slow if it worked, but it doesn't. So much for that.
So I've decided to write as dumb an editor as possible on AppEngine. The editing part I'll just rip off from one of the above editors (probably CodeMirror), the saving is trivial - POST data and shove it in data store, but the reading is a little tricky. How do I expose the saved files in a meaningful, but simple way
I've looked at the following protocols:
Know what I see when I read that? "Blah, blah, blah, blah, blah." It's too complicated! Give me something stupid!
So I'm making my own protocol. Let's call it... the Hypertext File Interface, or HFI for now.
- File System Root (denoted $ROOT) is any valid URI.
- File Name ($FILENAME):
- Any string of URL-safe characters.
- A server receiving a file name from a client MUST canonicalize it according to the following procedure:
- Strip off all leading and trailing "/"s.
- Replace any subsequence of "/"s of length greater than one with a single "/".
- Note that this means that potentially "unclean" File Names like "." or ".." are allowed, though nonsensical ones like the empty string or "/" are not.
- Servers MAY ignore case in File Names, but clients SHOULD NOT assume two Canonicalized File Names which differ only in terms of case refer to the same file.
- A File or a Directory are what you expect. Both are denoted by File Names.
- Info method (for housekeeping):
- When an HTTP GET is called on "$ROOT/info", the server MUST reply with status 200, and a body exactly equal to the string "{'version' : 1}".
- Read method: an HTTP GET to "$ROOT/read/$FILENAME" calls the Read method.
- If $FILENAME Exists and is a File according to the server, MUST return status 200 and the exact contents of the File as the body.
- If $FILENAME does not Exist or refers to a Directory, the server MUST return a 404 code.
- List method: an HTTP GET to "$ROOT/list/$FILENAME" calls the List method.
- If $FILENAME does not Exist, the server MUST return 404.
- If $FILENAME refers to a File, the server MUST return with status 200, with the body consisting of a single line: the canonicalized version of $FILENAME (no $ROOT or "/list/").
- If $FILENAME refers to a Directory object, the server MUST reply with status 200, with body listing the Directory's contents.
- Each line of the response consists of a single File Name denoting a file existing in that directory.
- Each File Name in the body MUST be canonicalized, and start with the canonicalized version of $FILENAME followed by a "/".
- File Names corresponding to Directories are denoted by the addition of a single trailing "/".
- A Listing of a Directory MUST NOT include the Directory's $FILENAME itself (a consequence of (2)).
- An empty response indicates an Existing but empty Directory, which the server MAY allow if it chooses.
- The appearance of a File Name ($FILE2) in a Listing indicates that if a List or Read call (whichever is appropriate) was made at the same time this List call was made, $FILE2 MUST have Existed. Of course, if the underlying File System is mutable, all bets are off when it comes to subsequent calls any amount of time in the future.
- Furthermore, every Existing File Name SHOULD appear in EXACTLY one directory listing, except for "", which corresponds to the Root Directory. Hidden File and Directories are allowed, but may be annoying/confusing.
- Security/authentication:
- HTTPS
- A server MAY provide an HFI service over HTTPS. Besides the fact that $ROOT will begin with "https://", the protocol remains the same.
- Given two valid file system roots, $ROOT and $ROOTS, such that the only difference is that $ROOTS has "https://" instead of "http://", they MUST refer to the same underlying file structure.
- A client can ascertain whether HTTP and/or HTTPS is supported by calling the Info method on both the "http" and "https" versions of the file system root.
- Authentication
- The server MAY decide whether a user has access to a given filesystem based on Cookies (or other HTTP headers), but not based on query parameters (query parameters are treated as part of the file name).
- If a server decides the user does not have access to the file system, all HTTP GETs on any URL beginning with $ROOT MUST return 403.
- If a server decides the user does have access to the file system, all HTTP GETs on any URL beginning with $ROOT MUST NOT return 403.
- I.E., Authentication is all or nothing for a given $ROOT.
One thing that I really like about this is the fact that you don't even need a real servlet to
implement it - a little Bash script could easily be written to copy/symlink files into a "read/"
directory, and print the output of "ls" into index.html files in a "list/" directory.
This is also quite extensible (e.g. I could easy add a Stat method), and could very easily be given a FUSE front-end.