# st A module for serving static files. Does etags, caching, etc. ## USAGE Here are some very simple usage examples. Just serve the files in the cwd at the root of the http server url: ```javascript var st = require('st') var http = require('http') http.createServer( st(process.cwd()) ).listen(1337) ``` Serve the files in static under the /static url. Otherwise do a different thing: ```javascript var mount = st({ path: __dirname + '/static', url: '/static' }) http.createServer(function(req, res) { var stHandled = mount(req, res); if (stHandled) return else res.end('this is not a static file') }).listen(1338) ``` The same sort of thing, but using an express middleware style: ```javascript var mount = st({ path: __dirname + '/static', url: '/static' }) http.createServer(function(req, res) { mount(req, res, function() { res.end('this is not a static file') }) }).listen(1339) ``` Serve the files in static under the / url, but only if not some doing other thing: ```javascript var mount = st({ path: __dirname + '/static', url: '/' }) http.createServer(function(req, res) { if (shouldDoThing(req)) { doTheThing(req, res) } else { mount(req, res) } }).listen(1340) ``` Serve the files in static under the / url, but don't serve a 404 if the file isn't found, so that the rest of the app can handle it: ```javascript var mount = st({ path: __dirname + '/static', url: '/', passthrough: true }) http.createServer(function(req, res) { mount(req, res, function() { res.end('this is not a static file'); }); }).listen(1341); ``` Serve the files with [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) enabled, to serve static files to any domain: ```javascript http.createServer( st({ path: process.cwd(), cors: true }) ).listen(1337) ``` Pass some options to the `st` function, and it returns a handler function. That handler function will return `true` if it handles the static request, or `false` if it doesn't. (This is so that you can only serve static files if they're in `/static` for example.) Here are some options if you want to configure stuff. All of these are optional except `path` which tells it where to get stuff from. If you pass a string instead of an object, then it'll use the string as the path. If you don't specify a `url`, then it'll mount on the `'/'` url, so `st({ path: './static/' })` will try to serve `./static/foo.html` when the user goes to `http://example.com/foo.html`. (Note: This behavior changed in st 0.2.0.) Here are all the options described with their defaults values and a few possible settings you might choose to use: ```javascript var st = require('st') var mount = st({ path: 'resources/static/', // resolved against the process cwd url: 'static/', // defaults to '/' cache: { // specify cache:false to turn off caching entirely fd: { max: 1000, // number of fd's to hang on to maxAge: 1000*60*60, // amount of ms before fd's expire }, stat: { max: 5000, // number of stat objects to hang on to maxAge: 1000 * 60, // number of ms that stats are good for }, content: { max: 1024*1024*64, // how much memory to use on caching contents maxAge: 1000 * 60 * 10, // how long to cache contents for // if `false` does not set cache control headers cacheControl: 'public, max-age=600' // to set an explicit cache-control // header value }, index: { // irrelevant if not using index:true max: 1024 * 8, // how many bytes of autoindex html to cache maxAge: 1000 * 60 * 10, // how long to store it for }, readdir: { // irrelevant if not using index:true max: 1000, // how many dir entries to cache maxAge: 1000 * 60 * 10, // how long to cache them for } }, // indexing options index: true, // auto-index, the default index: 'index.html', // use 'index.html' file as the index index: false, // return 404's for directories dot: false, // default: return 403 for any url with a dot-file part dot: true, // allow dot-files to be fetched normally passthrough: true, // calls next/returns instead of returning a 404 error passthrough: false, // returns a 404 when a file or an index is not found gzip: true, // default: compresses the response with gzip compression gzip: false, // does not compress the response, even if client accepts gzip cors: false, // default: static assets not accessible from other domains cors: true, // static assets can be accessed from any domain }) // with bare node.js http.createServer(function (req, res) { if (mount(req, res)) return // serving a static file myCustomLogic(req, res) }).listen(PORT) // with express app.use(mount) // or app.route('/static/:fooblz', function (req, res, next) { mount(req, res, next) // will call next() if it doesn't do anything }) ``` On the command line: ``` $ st -h st Static file server in node Options: -h --help Show this help -p --port PORT Listen on PORT (default=1337) -H --host HOST Bind address HOST (default=*) -l --localhost Same as "--host localhost" -d --dir DIRECTORY Serve the contents of DIRECTORY (default=cwd) -u --url MOUNTURL Serve the contents at MOUNTURL mount path (default=/) -i --index [INDEX] Use the specified INDEX filename as the result when a directory is requested. Set to "true" to turn autoindexing on, or "false" to turn it off. If no INDEX is provided, then it will turn autoindexing on. (default=true) -ni --no-index Same as "--index false" -. --dot [DOT] Allow .files to be served. Set to "false" to disable. -n. --no-dot Same as "--dot false" -co --cors Enable CORS to serve files to any domain. -nc --no-cache Turn off all caching. -a --age AGE Max age (in ms) of cache entries. ``` ## Range Requests Range requests are not supported. I'd love a patch to add support for them, but the spec is kind of confusing, and it's not always a clear win if you're not serving very large files, so it should come with some very comprehensive tests. Thankfully, as far as I can tell, it's always safe to serve the entire file to a request with a range header, so st does behave correctly, if not ideally in those situations. It'd be great to be able to do the better thing if the contents are cached, but still serve the full file if it's not in cache (so that it can be cached for subsequent requests). ## Memory Caching To make things go as fast as possible, it is a good idea to set the cache limits as high as you can afford, given the amount of memory on your server. Serving buffers out of process memory will generally always be faster than hitting the file system. ## Client Caching An etag header and last-modified will be attached to every request. If presented with an `if-none-match` or `if-modified-since`, then it'll return a 304 in the appropriate conditions. The etag is generated based on the dev, ino, and last modified date. Stat results are cached. ## Compression If the request header claims to enjoy gzip encoding, and the filename does not end in '.gz' or '.tgz', then the response will be gzipped. Gzipped bytes are not included in the calculation of cache sizes, so this utility will use a bit more memory than the cache.content.max and cache.index.max bytes would seem to allow. This will be less than double, and usually insignificant for normal web assets, but is important to consider if memory is at a premium. Gzip compression can be disabled by setting `gzip: false` on the options passed into `st()`. This is useful if your application already handles gzipping responses by other means. ## Filtering Output If you want to do some fancy stuff to the file before sending it, you can attach a `res.filter = myFilterStream` thing to the response object before passing it to the mount function. This is useful if you want to get the benefits of caching and gzipping and such, but serve stylus files as css, for example. ## Security Status Versions prior to 0.2.5 did not properly prevent folder traversal. Literal dots in a path were resolved out, but url encoded dots were not. Thus, a request like `/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd` would leak sensitive data from the server. As of version 0.2.5, any `'/../'` in the request path, urlencoded or not, will be replaced with `'/'`. If your application depends on url traversal, then you are encouraged to please refactor so that you do not depend on having `..` in url paths, as this tends to expose data that you may be surprised to be exposing. Consider using the `--localhost` setting if you don't want other people on your local network to read the files served by the command line server. This may become the default in a future major version.