or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-send

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

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/send@1.2.x

To install, run

npx @tessl/cli install tessl/npm-send@1.2.0

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);