Example #1
0
  fs.stat(path, function(err, stat) {
    // ENOENT = file/directory does not exist
    if (err) {
      return ('ENOENT' == err.code) ? next() : next(err);
    } else
    if (stat.isDirectory()) {
      return next({isDirectory: true});
    }

    // mime type
    var type = mime.lookup(path);

    var opts = {};

    if (ranges) {
      ranges = utils.parseRange(stat.size, ranges);
      if (ranges) {
        // TODO: stream options
        // TODO: multiple support
        opts.start = ranges[0].start;
        opts.end = ranges[0].end;
        res.statusCode = 206;
        res.setHeader('Content-Range', 'bytes ' + opts.start + '-' + opts.end + '/' + stat.size);
      } else {
        return next({error: 'http', reason: 'invalid-range'});
      }
    } else {
      res.setHeader('Content-Length', stat.size);
      if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + (maxAge / 1000));
      if (!res.getHeader('Last-Modified')) res.setHeader('Last-Modified', stat.mtime.toUTCString());
      if (!res.getHeader('ETag')) res.setHeader('ETag', utils.etag(stat));

      // conditional GET support
      if (utils.conditionalGET(req)) {
        if (!utils.modified(req, res)) {
          return utils.notModified(res);
        }
      }
    }

    // header fields
    if (!res.getHeader('content-type')) {
      var charset = mime.charsets.lookup(type);
      res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : ''));
    }
    res.setHeader('Accept-Ranges', 'bytes');

    // transfer
    if (req.method == 'HEAD') return res.end();

    // stream
    var stream = fs.createReadStream(path, opts);
    req.emit('sendFile', stream);
    stream.pipe(res);

  });
Example #2
0
/**
 * Sets the headers for a resource request.
 *
 * @param {Request} req the request object
 * @param {Response} res the response object
 * @param {Number} maxAge the max time to cache the resource in seconds
 * @param {Date} lastModified the time at which the resource was modified
 * @param {String} contentType the content type of the resource
 * @param {Number} len the length of the resource in bytes
 */
function setResourceHeaders(req, res, maxAge, lastModified, contentType, len) {
    var opts = {sendBody: true};
    var ranges = req.headers.range;

    setHeader(res, 'Date', new Date().toUTCString());
    setHeader(res, 'Cache-Control', maxAge);
    setHeader(res, 'Last-Modified', lastModified.toUTCString());
    setHeader(res, 'Content-Type', contentType);
    setHeader(res, 'Accept-Ranges', 'bytes');
    setHeader(res, 'ETag', connectUtils.etag({
        size: len,
        mtime: lastModified
    }));

    // conditional GET support
    if (connectUtils.conditionalGET(req)) {
        if (!connectUtils.modified(req, res)) {
            connectUtils.notModified(res);
            opts.sendBody = false;
            return opts;
        }
    }

    // we have a Range request
    if (ranges) {
        ranges = connectUtils.parseRange(len, ranges);

        // valid
        if (ranges) {
            opts.start = ranges[0].start;
            opts.end = ranges[0].end;

            // unsatisfiable range
            if (opts.start > len - 1) {
                res.setHeader('Content-Range', 'bytes */' + len);
                var err = new RainError('The specified range is invalid', RainError.ERROR_HTTP, 416);
                routerUtils.handleError(err, request, response);
                opts.sendBody = false;
                return opts;
            }

            // limit last-byte-pos to current length
            if (opts.end > len - 1) {
                opts.end = len - 1;
            }

            // Content-Range
            len = opts.end - opts.start + 1;
            res.statusCode = 206;
            res.setHeader('Content-Range', 'bytes ' + opts.start + '-' + opts.end + '/' + len);
        }
    }

    res.setHeader('Content-Length', len);

    // transfer
    if (req.method === 'HEAD') {
        res.end();
        opts.sendBody = false;
        return opts;
    }

    return opts;
}