CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-send

Better streaming static file server with Range and conditional-GET support

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

index.mddocs/

Send

Send is a Node.js library for streaming files from the file system as HTTP responses with comprehensive support for partial content delivery through HTTP Range requests, conditional-GET negotiation, and granular event handling. It provides configurable options for cache control, ETag generation, dotfile handling, and more.

Package Information

  • Package Name: send
  • Package Type: npm
  • Language: JavaScript (Node.js)
  • Installation: npm install send

Core Imports

const send = require('send');

For ES modules:

import send from 'send';

Basic Usage

const http = require('http');
const send = require('send');

const server = http.createServer(function(req, res) {
  // Basic file serving
  send(req, req.url, { root: '/public' })
    .pipe(res);
});

server.listen(3000);

Capabilities

Main Factory Function

Creates a SendStream instance for streaming files to HTTP responses.

/**
 * Create a new SendStream for the given path to send to a response
 * @param {object} req - HTTP request object  
 * @param {string} path - URL-encoded path to serve
 * @param {object} [options] - Configuration options
 * @returns {SendStream} SendStream instance
 */
function send(req, path, options);

Usage Examples:

// Basic usage
send(req, '/index.html').pipe(res);

// With options
send(req, pathname, {
  root: '/www/public',
  index: ['index.html'],
  dotfiles: 'deny',
  maxAge: '1d'
}).pipe(res);

// With event handling
send(req, pathname, options)
  .on('error', function(err) {
    res.statusCode = err.status || 500;
    res.end(err.message);
  })
  .on('directory', function(res, path) {
    res.statusCode = 301;
    res.setHeader('Location', req.url + '/');
    res.end('Redirecting to ' + req.url + '/');
  })
  .pipe(res);

SendStream Class

The main class returned by the send() factory function, providing file streaming capabilities.

/**
 * SendStream constructor (internal - use send() factory function)
 * @param {object} req - HTTP request object
 * @param {string} path - File path to serve
 * @param {object} [options] - Configuration options
 */
class SendStream extends Stream;

Core Methods

pipe()

Pipes the file content to an HTTP response with full request processing including Range requests, conditional-GET, error handling, and header management.

/**
 * Pipe to response object
 * @param {Stream} res - HTTP response object
 * @returns {Stream} The response object
 */
SendStream.prototype.pipe(res);
error()

Emits error or sends HTTP error response with appropriate status code and HTML document.

/**
 * Emit error with status code
 * @param {number} status - HTTP status code
 * @param {Error} [err] - Optional error object
 */
SendStream.prototype.error(status, err);

Conditional Request Methods

isConditionalGET()

Checks if the request includes conditional-GET headers.

/**
 * Check if this is a conditional GET request
 * @returns {boolean} True if conditional headers present
 */
SendStream.prototype.isConditionalGET();
isPreconditionFailure()

Validates If-Match and If-Unmodified-Since request headers against response ETag and Last-Modified.

/**
 * Check if the request preconditions failed
 * @returns {boolean} True if preconditions failed
 */
SendStream.prototype.isPreconditionFailure();
isFresh()

Checks if the cached response is fresh using conditional headers.

/**
 * Check if the cache is fresh
 * @returns {boolean} True if cache is fresh
 */
SendStream.prototype.isFresh();
isRangeFresh()

Validates If-Range header against ETag or Last-Modified for Range requests.

/**
 * Check if the range is fresh
 * @returns {boolean} True if range is fresh
 */
SendStream.prototype.isRangeFresh();

Response Methods

notModified()

Sends a 304 Not Modified response and removes content headers.

/**
 * Respond with 304 not modified
 */
SendStream.prototype.notModified();
redirect()

Performs directory redirect or emits directory event.

/**
 * Redirect to path
 * @param {string} path - Path to redirect to
 */
SendStream.prototype.redirect(path);

Utility Methods

hasTrailingSlash()

Checks if the pathname ends with a forward slash.

/**
 * Check if the pathname ends with "/"
 * @returns {boolean} True if ends with slash
 */
SendStream.prototype.hasTrailingSlash();
isCachable()

Determines if the response status code is cacheable (2xx or 304).

/**
 * Check if the request is cacheable
 * @returns {boolean} True if cacheable status code
 */
SendStream.prototype.isCachable();

Configuration Options

interface SendOptions {
  /** Enable or disable accepting ranged requests (default: true) */
  acceptRanges?: boolean;
  
  /** Enable or disable setting Cache-Control response header (default: true) */
  cacheControl?: boolean;
  
  /** How to treat dotfiles: 'allow', 'deny', or 'ignore' (default: 'ignore') */
  dotfiles?: string;
  
  /** Byte offset at which the stream ends (for Range request processing) */
  end?: number;
  
  /** Enable or disable etag generation (default: true) */
  etag?: boolean;
  
  /** File extensions to try if file doesn't exist */
  extensions?: string[];
  
  /** Enable immutable directive in Cache-Control (default: false) */
  immutable?: boolean;
  
  /** Index file names or disable with false (default: ['index.html']) */
  index?: string[] | string | boolean;
  
  /** Enable or disable Last-Modified header (default: true) */
  lastModified?: boolean;
  
  /** Max-age in milliseconds for http caching (default: 0). Alternative: maxage */
  maxAge?: number | string;
  
  /** Root directory for relative paths */
  root?: string;
  
  /** Byte offset at which the stream starts (default: 0, for Range request processing) */
  start?: number;
}

Configuration Examples:

// Disable dotfiles and enable caching
const options = {
  root: '/public',
  dotfiles: 'deny',
  maxAge: '1 day',
  immutable: true
};

// Custom index files and extensions
const options = {
  root: '/public',
  index: ['index.html', 'default.html'],
  extensions: ['html', 'htm']
};

// Range request configuration
const options = {
  acceptRanges: true,
  start: 100,      // Start at byte 100
  end: 1000        // End at byte 1000
};

Events

The SendStream instance emits the following events:

interface SendStreamEvents {
  /** Error occurred during processing */
  'error': (err: Error) => void;
  
  /** Directory was requested */
  'directory': (res: Response, path: string) => void;
  
  /** File was requested and found */
  'file': (path: string, stat: fs.Stats) => void;
  
  /** Headers are about to be set on a file */
  'headers': (res: Response, path: string, stat: fs.Stats) => void;
  
  /** File streaming has started */
  'stream': (stream: fs.ReadStream) => void;
  
  /** Streaming has completed */
  'end': () => void;
}

Event Handling Examples:

send(req, pathname, options)
  .on('error', function(err) {
    // Handle errors (404, 403, 500, etc.)
    res.statusCode = err.status || 500;
    res.end(err.message);
  })
  .on('directory', function(res, path) {
    // Handle directory requests
    res.statusCode = 301;
    res.setHeader('Location', req.url + '/');
    res.end('Redirecting to ' + req.url + '/');
  })
  .on('file', function(path, stat) {
    // Log file access
    console.log('Serving file:', path, 'Size:', stat.size);
  })
  .on('headers', function(res, path, stat) {
    // Customize headers before they're sent
    if (path.endsWith('.pdf')) {
      res.setHeader('Content-Disposition', 'attachment');
    }
  })
  .on('stream', function(stream) {
    // Handle streaming start
    console.log('Started streaming file');
  })
  .on('end', function() {
    // Handle streaming completion
    console.log('Finished streaming file');
  })
  .pipe(res);

Error Handling

Send provides comprehensive error handling with appropriate HTTP status codes:

interface SendErrors {
  /** 400 Bad Request - Malformed URI, null bytes */
  BadRequest: 400;
  
  /** 403 Forbidden - Malicious path, denied dotfiles, directory without trailing slash */
  Forbidden: 403;
  
  /** 404 Not Found - File not found, ignored dotfiles */
  NotFound: 404;
  
  /** 412 Precondition Failed - Conditional request preconditions failed */
  PreconditionFailed: 412;
  
  /** 416 Range Not Satisfiable - Invalid range request */
  RangeNotSatisfiable: 416;
  
  /** 500 Internal Server Error - File system errors, headers already sent */
  InternalServerError: 500;
}

interface SendError extends Error {
  /** HTTP status code */
  status: number;
  /** Error code */
  code?: string;
  /** File system path that caused the error */
  path?: string;
}

Error Handling Patterns:

// Automatic error handling (default)
send(req, pathname, options).pipe(res);

// Custom error handling
send(req, pathname, options)
  .on('error', function(err) {
    if (err.status === 404) {
      // Serve custom 404 page
      res.statusCode = 404;
      res.end('Custom not found page');
    } else {
      // Handle other errors
      res.statusCode = err.status || 500;
      res.end(err.message);
    }
  })
  .pipe(res);

Advanced Usage

Custom File Types

const path = require('path');

send(req, pathname, { root: '/public' })
  .on('headers', function(res, filePath) {
    const ext = path.extname(filePath);
    switch (ext) {
      case '.x-custom':
        res.setHeader('Content-Type', 'application/x-custom-type');
        break;
    }
  })
  .pipe(res);

Directory Listing

const fs = require('fs');

send(req, pathname, { index: false, root: '/public' })
  .once('directory', function(res, dirPath) {
    const stream = this;
    
    // Redirect to trailing slash for consistent URLs
    if (!stream.hasTrailingSlash()) {
      return stream.redirect(dirPath);
    }
    
    // Custom directory listing
    fs.readdir(dirPath, function(err, list) {
      if (err) return stream.error(500, err);
      
      res.setHeader('Content-Type', 'text/plain; charset=UTF-8');
      res.end(list.join('\\n') + '\\n');
    });
  })
  .pipe(res);

Range Request Handling

// Enable range requests for video streaming
send(req, pathname, {
  root: '/media',
  acceptRanges: true
})
.on('headers', function(res, path, stat) {
  // Add custom headers for media files
  if (path.match(/\\.(mp4|webm)$/)) {
    res.setHeader('Accept-Ranges', 'bytes');
  }
})
.pipe(res);

Conditional Caching

// Aggressive caching for static assets
send(req, pathname, {
  root: '/static',
  maxAge: '1 year',
  immutable: true,
  etag: true,
  lastModified: true
})
.pipe(res);

docs

index.md

tile.json