socketstream/socketstream

View on GitHub
lib/utils/serve-static.js

Summary

Maintainability
C
7 hrs
Test Coverage
/*!
 * serve-static
 * Copyright(c) 2010 Sencha Inc.
 * Copyright(c) 2011 TJ Holowaychuk
 * Copyright(c) 2014 Douglas Christopher Wilson
 * MIT Licensed
 *
 * This is really just for development. In production static files should be handled by static file server or cache.
 *
 * Added prefix path ability. If expressjs/serve-static will support this in the future this can be swapped out.
 */

/**
 * Module dependencies.
 */

'use strict';



var escapeHtml, merge, parseurl, resolve, send, url, setHeaders, originalUrl, path, hasTrailingSlash, stream;



escapeHtml  = require('escape-html');
merge       = require('utils-merge');
parseurl    = require('parseurl');
resolve     = require('path').resolve;
send        = require('send');
url         = require('url');

/**
 * @param {String} root
 * @param {Object} options
 * @return {Function}
 * @api public
 */

exports = module.exports = function serveStatic (prefix,root, options) {
  if (!root) {
    throw new TypeError('root path required')
  }

  if (typeof root !== 'string') {
    throw new TypeError('root path must be a string')
  }

  // copy options object
  options = merge({}, options)

  // resolve root to absolute
  root = resolve(root)

  // default redirect
  var redirect = options.redirect !== false

  // headers listener
  setHeaders = options.setHeaders;
  delete options.setHeaders

  if (setHeaders && typeof setHeaders !== 'function') {
    throw new TypeError('option setHeaders must be function')
  }

  // setup options for send
  options.maxage = options.maxage || options.maxAge || 0
  options.root = root

  return function serveStatic(req, res, next) {
    if (req.method !== 'GET' && req.method !== 'HEAD') {
      return next()
    }

    var opts = merge({}, options)
    originalUrl = parseurl.original(req)
    path = parseurl(req).pathname
    if (prefix) {
      if (path.substring(0,prefix.length) !== prefix) {
        return next();
      }
      path = path.substring(prefix.length);
    }
    hasTrailingSlash = originalUrl.pathname[originalUrl.pathname.length - 1] === '/'

    if (path === '/' && !hasTrailingSlash) {
      // make sure redirect occurs at mount
      path = ''
    }

    // create send stream
    stream = send(req, path, opts)

    if (redirect) {
      // redirect relative to originalUrl
      stream.on('directory', function redirect() {
        if (hasTrailingSlash) {
          return next()
        }

        originalUrl.pathname += '/'

        var target = url.format(originalUrl)

        res.statusCode = 303
        res.setHeader('Content-Type', 'text/html; charset=utf-8')
        res.setHeader('Location', target)

        //TODO translation string support
        res.end('Redirecting to <a href="' + escapeHtml(target) + '">' + escapeHtml(target) + '</a>\n')
      })
    } else {
      // forward to next middleware on directory
      stream.on('directory', next)
    }

    // add headers listener
    if (setHeaders) {
      stream.on('headers', setHeaders)
    }

    // forward non-404 errors
    stream.on('error', function error(err) {
      next(err.status === 404 ? null : err)
    })

    // pipe
    stream.pipe(res)
  }
}

/**
 * Expose mime module.
 *
 * If you wish to extend the mime table use this
 * reference to the "mime" module in the npm registry.
 */

exports.mime = send.mime