Mimic a Node.js HTTP response stream by copying properties and event handlers between streams
npx @tessl/cli install tessl/npm-mimic-response@4.0.0Mimic Response is a Node.js utility that copies HTTP response properties and event handlers from one stream to another, enabling the creation of proxy streams that behave like the original HTTP response stream. It preserves all HTTP-related properties (statusCode, headers, httpVersion, etc.) and forwards abort and close events appropriately.
npm install mimic-responseimport mimicResponse from "mimic-response";For CommonJS:
const mimicResponse = require("mimic-response");For TypeScript:
import mimicResponse from "mimic-response";
import type { IncomingMessage } from "node:http";import { PassThrough as PassThroughStream } from "node:stream";
import mimicResponse from "mimic-response";
// Get an HTTP response stream from a request
const responseStream = getHttpResponseStream();
// Create a new stream that will mimic the response
const myStream = new PassThroughStream({ autoDestroy: false });
// Copy all HTTP properties and event handlers
mimicResponse(responseStream, myStream);
// Now myStream has all the HTTP response properties
console.log(myStream.statusCode); // 200
console.log(myStream.headers); // Response headers object
console.log(myStream.complete); // Boolean indicating response completionCopies all HTTP response properties from a source stream to a target stream, making the target stream behave like an HTTP response.
/**
* Mimic a Node.js HTTP response stream by copying properties and event handlers
* @param fromStream - The HTTP response stream to copy properties from
* @param toStream - The target stream to copy properties to (must have autoDestroy: false)
* @returns The enhanced toStream with HTTP response properties
* @throws Error if toStream has autoDestroy: true
*/
function mimicResponse<T extends NodeJS.ReadableStream>(
fromStream: IncomingMessage,
toStream: T
): T & IncomingMessage;Parameters:
fromStream (IncomingMessage): The source HTTP response stream containing properties to copytoStream (NodeJS.ReadableStream): The target stream that will receive the copied properties. Must have autoDestroy: falseReturns: The toStream parameter enhanced with all HTTP response properties from fromStream
Throws: Error with message "The second stream must have the autoDestroy option set to false" if toStream._readableState.autoDestroy is true
Copied Properties: The function copies all enumerable properties from the source stream plus these known HTTP response properties:
const knownProperties = [
'aborted', // Boolean indicating if request was aborted
'complete', // Boolean indicating if response is complete
'headers', // Response headers object
'httpVersion', // HTTP version string (e.g., "1.1")
'httpVersionMinor', // HTTP version minor number
'httpVersionMajor', // HTTP version major number
'method', // HTTP method string
'rawHeaders', // Raw headers array
'rawTrailers', // Raw trailers array
'setTimeout', // Timeout method function
'socket', // Underlying socket object
'statusCode', // HTTP status code number
'statusMessage', // HTTP status message string
'trailers', // Response trailers object
'url' // Request URL string
];Event Forwarding:
Usage Examples:
Basic stream proxy:
import { PassThrough as PassThroughStream } from "node:stream";
import mimicResponse from "mimic-response";
const responseStream = getHttpResponseStream();
const proxyStream = new PassThroughStream({ autoDestroy: false });
mimicResponse(responseStream, proxyStream);
// Access HTTP properties on the proxy stream
console.log(proxyStream.statusCode); // Same as responseStream.statusCode
console.log(proxyStream.headers); // Same as responseStream.headersCustom stream with manual destroy handling:
import { PassThrough as PassThroughStream } from "node:stream";
import mimicResponse from "mimic-response";
const responseStream = getHttpResponseStream();
const myStream = new PassThroughStream({
autoDestroy: false,
destroy(error, callback) {
// Manually destroy the source stream
responseStream.destroy();
callback(error);
}
});
mimicResponse(responseStream, myStream);
// The streams are now linked for destruction
myStream.destroy();Stream transformation pipeline:
import { Transform } from "node:stream";
import mimicResponse from "mimic-response";
const responseStream = getHttpResponseStream();
const transformStream = new Transform({
autoDestroy: false,
transform(chunk, encoding, callback) {
// Transform the data while preserving HTTP properties
const transformed = chunk.toString().toUpperCase();
callback(null, transformed);
}
});
mimicResponse(responseStream, transformStream);
// Transform stream now has HTTP properties and transforms data
responseStream.pipe(transformStream);interface IncomingMessage extends NodeJS.ReadableStream {
aborted: boolean;
complete: boolean;
headers: { [key: string]: string | string[] | undefined };
httpVersion: string;
httpVersionMajor: number;
httpVersionMinor: number;
method?: string;
rawHeaders: string[];
rawTrailers: string[];
setTimeout(msecs: number, callback?: () => void): this;
socket: any;
statusCode?: number;
statusMessage?: string;
trailers: { [key: string]: string | undefined };
url?: string;
}The function validates that the target stream is properly configured:
toStream._readableState.autoDestroy is trueautoDestroy option set to false"This validation ensures that the target stream won't be automatically destroyed, which is necessary for proper event forwarding and property access.
AutoDestroy Requirement: The target stream must have autoDestroy: false to prevent automatic cleanup that would interfere with property access and event handling.
Property Binding: Function properties from the source stream are bound to maintain the correct this context when called on the target stream.
Non-Overwriting: Existing properties on the target stream are never overwritten, preserving the target stream's original functionality.
Manual Destroy Handling: The destroy() method is not automatically proxied. You must manually handle destruction if needed.
Event Timing: The 'close' event forwarding handles different timing scenarios based on whether the response is complete and whether the target stream is still readable.