const require_chunk = require('./chunk-CbDLau6x.cjs');
const require_fetchUtils = require('./fetchUtils-umV5xXBy.cjs');
const require_getRawRequest = require('./getRawRequest-B6znU6il.cjs');
const require_handleRequest = require('./handleRequest-C-cKYjIh.cjs');
const require_node = require('./node-DIKcnzhK.cjs');
let _open_draft_logger = require("@open-draft/logger");
let outvariant = require("outvariant");
let node_http = require("node:http");
node_http = require_chunk.__toESM(node_http);
let node_https = require("node:https");
node_https = require_chunk.__toESM(node_https);
let node_net = require("node:net");
node_net = require_chunk.__toESM(node_net);
let _http_common = require("_http_common");
let node_stream = require("node:stream");
let node_url = require("node:url");
let http = require("http");

//#region src/interceptors/Socket/utils/normalizeSocketWriteArgs.ts
/**
* Normalizes the arguments provided to the `Writable.prototype.write()`
* and `Writable.prototype.end()`.
*/
function normalizeSocketWriteArgs(args) {
	const normalized = [
		args[0],
		void 0,
		void 0
	];
	if (typeof args[1] === "string") normalized[1] = args[1];
	else if (typeof args[1] === "function") normalized[2] = args[1];
	if (typeof args[2] === "function") normalized[2] = args[2];
	return normalized;
}

//#endregion
//#region src/interceptors/Socket/MockSocket.ts
var MockSocket = class extends node_net.default.Socket {
	constructor(options) {
		super();
		this.options = options;
		this.connecting = false;
		this.connect();
		this._final = (callback) => {
			callback(null);
		};
	}
	connect() {
		this.connecting = true;
		return this;
	}
	write(...args) {
		const [chunk, encoding, callback] = normalizeSocketWriteArgs(args);
		this.options.write(chunk, encoding, callback);
		return true;
	}
	end(...args) {
		const [chunk, encoding, callback] = normalizeSocketWriteArgs(args);
		this.options.write(chunk, encoding, callback);
		return super.end.apply(this, args);
	}
	push(chunk, encoding) {
		this.options.read(chunk, encoding);
		return super.push(chunk, encoding);
	}
};

//#endregion
//#region src/interceptors/Socket/utils/baseUrlFromConnectionOptions.ts
function baseUrlFromConnectionOptions(options) {
	if ("href" in options) return new URL(options.href);
	const protocol = options.port === 443 ? "https:" : "http:";
	const host = options.host;
	const url = new URL(`${protocol}//${host}`);
	if (options.port) url.port = options.port.toString();
	if (options.path) url.pathname = options.path;
	if (options.auth) {
		const [username, password] = options.auth.split(":");
		url.username = username;
		url.password = password;
	}
	return url;
}

//#endregion
//#region src/interceptors/ClientRequest/utils/recordRawHeaders.ts
const kRawHeaders = Symbol("kRawHeaders");
const kRestorePatches = Symbol("kRestorePatches");
function recordRawHeader(headers, args, behavior) {
	ensureRawHeadersSymbol(headers, []);
	const rawHeaders = Reflect.get(headers, kRawHeaders);
	if (behavior === "set") {
		for (let index = rawHeaders.length - 1; index >= 0; index--) if (rawHeaders[index][0].toLowerCase() === args[0].toLowerCase()) rawHeaders.splice(index, 1);
	}
	rawHeaders.push(args);
}
/**
* Define the raw headers symbol on the given `Headers` instance.
* If the symbol already exists, this function does nothing.
*/
function ensureRawHeadersSymbol(headers, rawHeaders) {
	if (Reflect.has(headers, kRawHeaders)) return;
	defineRawHeadersSymbol(headers, rawHeaders);
}
/**
* Define the raw headers symbol on the given `Headers` instance.
* If the symbol already exists, it gets overridden.
*/
function defineRawHeadersSymbol(headers, rawHeaders) {
	Object.defineProperty(headers, kRawHeaders, {
		value: rawHeaders,
		enumerable: false,
		configurable: true
	});
}
/**
* Patch the global `Headers` class to store raw headers.
* This is for compatibility with `IncomingMessage.prototype.rawHeaders`.
*
* @note Node.js has their own raw headers symbol but it
* only records the first header name in case of multi-value headers.
* Any other headers are normalized before comparing. This makes it
* incompatible with the `rawHeaders` format.
*
* let h = new Headers()
* h.append('X-Custom', 'one')
* h.append('x-custom', 'two')
* h[Symbol('headers map')] // Map { 'X-Custom' => 'one, two' }
*/
function recordRawFetchHeaders() {
	if (Reflect.get(Headers, kRestorePatches)) return Reflect.get(Headers, kRestorePatches);
	const { Headers: OriginalHeaders, Request: OriginalRequest, Response: OriginalResponse } = globalThis;
	const { set, append, delete: headersDeleteMethod } = Headers.prototype;
	Object.defineProperty(Headers, kRestorePatches, {
		value: () => {
			Headers.prototype.set = set;
			Headers.prototype.append = append;
			Headers.prototype.delete = headersDeleteMethod;
			globalThis.Headers = OriginalHeaders;
			globalThis.Request = OriginalRequest;
			globalThis.Response = OriginalResponse;
			Object.setPrototypeOf(require_fetchUtils.FetchRequest, OriginalRequest);
			Object.setPrototypeOf(require_fetchUtils.FetchRequest.prototype, OriginalRequest.prototype);
			Object.setPrototypeOf(require_fetchUtils.FetchResponse, OriginalResponse);
			Object.setPrototypeOf(require_fetchUtils.FetchResponse.prototype, OriginalResponse.prototype);
			Reflect.deleteProperty(Headers, kRestorePatches);
		},
		enumerable: false,
		configurable: true
	});
	Object.defineProperty(globalThis, "Headers", {
		enumerable: true,
		writable: true,
		value: new Proxy(Headers, { construct(target, args, newTarget) {
			const headersInit = args[0] || [];
			if (headersInit instanceof Headers && Reflect.has(headersInit, kRawHeaders)) {
				const sanitizedHeaders = Reflect.get(headersInit, kRawHeaders).map((tuple) => [tuple[0], tuple[1]]);
				const headers$1 = Reflect.construct(target, [sanitizedHeaders], newTarget);
				ensureRawHeadersSymbol(headers$1, [...sanitizedHeaders]);
				return headers$1;
			}
			const headers = Reflect.construct(target, args, newTarget);
			if (!Reflect.has(headers, kRawHeaders)) ensureRawHeadersSymbol(headers, Array.isArray(headersInit) ? headersInit : Object.entries(headersInit));
			return headers;
		} })
	});
	Headers.prototype.set = new Proxy(Headers.prototype.set, { apply(target, thisArg, args) {
		recordRawHeader(thisArg, [args[0], args[1]], "set");
		return Reflect.apply(target, thisArg, args);
	} });
	Headers.prototype.append = new Proxy(Headers.prototype.append, { apply(target, thisArg, args) {
		recordRawHeader(thisArg, [args[0], args[1]], "append");
		return Reflect.apply(target, thisArg, args);
	} });
	Headers.prototype.delete = new Proxy(Headers.prototype.delete, { apply(target, thisArg, args) {
		const rawHeaders = Reflect.get(thisArg, kRawHeaders);
		if (rawHeaders) {
			for (let index = rawHeaders.length - 1; index >= 0; index--) if (rawHeaders[index][0].toLowerCase() === args[0].toLowerCase()) rawHeaders.splice(index, 1);
		}
		return Reflect.apply(target, thisArg, args);
	} });
	Object.defineProperty(globalThis, "Request", {
		enumerable: true,
		writable: true,
		value: new Proxy(Request, { construct(target, args, newTarget) {
			const request = Reflect.construct(target, args, newTarget);
			const inferredRawHeaders = [];
			if (typeof args[0] === "object" && args[0].headers != null) inferredRawHeaders.push(...inferRawHeaders(args[0].headers));
			if (typeof args[1] === "object" && args[1].headers != null) inferredRawHeaders.push(...inferRawHeaders(args[1].headers));
			if (inferredRawHeaders.length > 0) ensureRawHeadersSymbol(request.headers, inferredRawHeaders);
			return request;
		} })
	});
	Object.defineProperty(globalThis, "Response", {
		enumerable: true,
		writable: true,
		value: new Proxy(Response, { construct(target, args, newTarget) {
			const response = Reflect.construct(target, args, newTarget);
			if (typeof args[1] === "object" && args[1].headers != null) ensureRawHeadersSymbol(response.headers, inferRawHeaders(args[1].headers));
			return response;
		} })
	});
	/**
	* Re-parent FetchRequest/FetchResponse so their `super()` calls go
	* through the proxied globalThis.Request/Response above. Without this,
	* FetchRequest extends the statically-captured (original) Request,
	* bypassing the construct proxy that records raw headers.
	*/
	Object.setPrototypeOf(require_fetchUtils.FetchRequest, globalThis.Request);
	Object.setPrototypeOf(require_fetchUtils.FetchRequest.prototype, globalThis.Request.prototype);
	Object.setPrototypeOf(require_fetchUtils.FetchResponse, globalThis.Response);
	Object.setPrototypeOf(require_fetchUtils.FetchResponse.prototype, globalThis.Response.prototype);
}
function restoreHeadersPrototype() {
	if (!Reflect.get(Headers, kRestorePatches)) return;
	Reflect.get(Headers, kRestorePatches)();
}
function getRawFetchHeaders(headers) {
	if (!Reflect.has(headers, kRawHeaders)) return Array.from(headers.entries());
	const rawHeaders = Reflect.get(headers, kRawHeaders);
	return rawHeaders.length > 0 ? rawHeaders : Array.from(headers.entries());
}
/**
* Infers the raw headers from the given `HeadersInit` provided
* to the Request/Response constructor.
*
* If the `init.headers` is a Headers instance, use it directly.
* That means the headers were created standalone and already have
* the raw headers stored.
* If the `init.headers` is a HeadersInit, create a new Headers
* instance out of it.
*/
function inferRawHeaders(headers) {
	if (headers instanceof Headers) return Reflect.get(headers, kRawHeaders) || [];
	return Reflect.get(new Headers(headers), kRawHeaders);
}

//#endregion
//#region src/interceptors/ClientRequest/utils/parserUtils.ts
/**
* @see https://github.com/nodejs/node/blob/f3adc11e37b8bfaaa026ea85c1cf22e3a0e29ae9/lib/_http_common.js#L180
*/
function freeParser(parser, socket) {
	if (parser._consumed) parser.unconsume();
	parser._headers = [];
	parser._url = "";
	parser.socket = null;
	parser.incoming = null;
	parser.outgoing = null;
	parser.maxHeaderPairs = 2e3;
	parser._consumed = false;
	parser.onIncoming = null;
	parser[_http_common.HTTPParser.kOnHeaders] = null;
	parser[_http_common.HTTPParser.kOnHeadersComplete] = null;
	parser[_http_common.HTTPParser.kOnMessageBegin] = null;
	parser[_http_common.HTTPParser.kOnMessageComplete] = null;
	parser[_http_common.HTTPParser.kOnBody] = null;
	parser[_http_common.HTTPParser.kOnExecute] = null;
	parser[_http_common.HTTPParser.kOnTimeout] = null;
	parser.remove();
	parser.free();
	if (socket)
 /**
	* @note Unassigning the socket's parser will fail this assertion
	* if there's still some data being processed on the socket:
	* @see https://github.com/nodejs/node/blob/4e1f39b678b37017ac9baa0971e3aeecd3b67b51/lib/_http_client.js#L613
	*/
	if (socket.destroyed) socket.parser = null;
	else socket.once("end", () => {
		socket.parser = null;
	});
}

//#endregion
//#region src/interceptors/ClientRequest/MockHttpSocket.ts
const kRequestId = Symbol("kRequestId");
var MockHttpSocket = class extends MockSocket {
	constructor(options) {
		super({
			write: (chunk, encoding, callback) => {
				if (this.socketState !== "passthrough") this.writeBuffer.push([
					chunk,
					encoding,
					callback
				]);
				if (chunk) {
					/**
					* Forward any writes to the mock socket to the underlying original socket.
					* This ensures functional duplex connections, like WebSocket.
					* @see https://github.com/mswjs/interceptors/issues/682
					*/
					if (this.socketState === "passthrough") this.originalSocket?.write(chunk, encoding, callback);
					this.requestParser.execute(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding));
				}
			},
			read: (chunk) => {
				if (chunk !== null)
 /**
				* @todo We need to free the parser if the connection has been
				* upgraded to a non-HTTP protocol. It won't be able to parse data
				* from that point onward anyway. No need to keep it in memory.
				*/
				this.responseParser.execute(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
			}
		});
		this.requestRawHeadersBuffer = [];
		this.responseRawHeadersBuffer = [];
		this.writeBuffer = [];
		this.socketState = "unknown";
		this.onRequestHeaders = (rawHeaders) => {
			this.requestRawHeadersBuffer.push(...rawHeaders);
		};
		this.onRequestStart = (versionMajor, versionMinor, rawHeaders, _, path, __, ___, ____, shouldKeepAlive) => {
			this.shouldKeepAlive = shouldKeepAlive;
			const url = new URL(path || "", this.baseUrl);
			const method = this.connectionOptions.method?.toUpperCase() || "GET";
			const headers = require_fetchUtils.FetchResponse.parseRawHeaders([...this.requestRawHeadersBuffer, ...rawHeaders || []]);
			this.requestRawHeadersBuffer.length = 0;
			const canHaveBody = method !== "GET" && method !== "HEAD";
			if (url.username || url.password) {
				if (!headers.has("authorization")) headers.set("authorization", `Basic ${url.username}:${url.password}`);
				url.username = "";
				url.password = "";
			}
			this.requestStream = new node_stream.Readable({ read: () => {
				this.flushWriteBuffer();
			} });
			const requestId = require_fetchUtils.createRequestId();
			this.request = new require_fetchUtils.FetchRequest(url, {
				method,
				headers,
				credentials: "same-origin",
				duplex: canHaveBody ? "half" : void 0,
				body: canHaveBody ? node_stream.Readable.toWeb(this.requestStream) : null
			});
			Reflect.set(this.request, kRequestId, requestId);
			require_getRawRequest.setRawRequest(this.request, Reflect.get(this, "_httpMessage"));
			require_node.setRawRequestBodyStream(this.request, this.requestStream);
			/**
			* @fixme Stop relying on the "X-Request-Id" request header
			* to figure out if one interceptor has been invoked within another.
			* @see https://github.com/mswjs/interceptors/issues/378
			*/
			if (this.request.headers.has(require_fetchUtils.INTERNAL_REQUEST_ID_HEADER_NAME)) {
				this.passthrough();
				return;
			}
			this.onRequest({
				requestId,
				request: this.request,
				socket: this
			});
		};
		this.onResponseHeaders = (rawHeaders) => {
			this.responseRawHeadersBuffer.push(...rawHeaders);
		};
		this.onResponseStart = (versionMajor, versionMinor, rawHeaders, method, url, status, statusText) => {
			const headers = require_fetchUtils.FetchResponse.parseRawHeaders([...this.responseRawHeadersBuffer, ...rawHeaders || []]);
			this.responseRawHeadersBuffer.length = 0;
			const response = new require_fetchUtils.FetchResponse(
				/**
				* @note The Fetch API response instance exposed to the consumer
				* is created over the response stream of the HTTP parser. It is NOT
				* related to the Socket instance. This way, you can read response body
				* in response listener while the Socket instance delays the emission
				* of "end" and other events until those response listeners are finished.
				*/
				require_fetchUtils.FetchResponse.isResponseWithBody(status) ? node_stream.Readable.toWeb(this.responseStream = new node_stream.Readable({ read() {} })) : null,
				{
					url,
					status,
					statusText,
					headers
				}
			);
			(0, outvariant.invariant)(this.request, "Failed to handle a response: request does not exist");
			require_fetchUtils.FetchResponse.setUrl(this.request.url, response);
			/**
			* @fixme Stop relying on the "X-Request-Id" request header
			* to figure out if one interceptor has been invoked within another.
			* @see https://github.com/mswjs/interceptors/issues/378
			*/
			if (this.request.headers.has(require_fetchUtils.INTERNAL_REQUEST_ID_HEADER_NAME)) return;
			this.responseListenersPromise = this.onResponse({
				response,
				isMockedResponse: this.socketState === "mock",
				requestId: Reflect.get(this.request, kRequestId),
				request: this.request,
				socket: this
			});
		};
		this.connectionOptions = options.connectionOptions;
		this.createConnection = options.createConnection;
		this.onRequest = options.onRequest;
		this.onResponse = options.onResponse;
		this.baseUrl = baseUrlFromConnectionOptions(this.connectionOptions);
		this.requestParser = new _http_common.HTTPParser();
		this.requestParser.initialize(_http_common.HTTPParser.REQUEST, {});
		this.requestParser[_http_common.HTTPParser.kOnHeaders] = this.onRequestHeaders.bind(this);
		this.requestParser[_http_common.HTTPParser.kOnHeadersComplete] = this.onRequestStart.bind(this);
		this.requestParser[_http_common.HTTPParser.kOnBody] = this.onRequestBody.bind(this);
		this.requestParser[_http_common.HTTPParser.kOnMessageComplete] = this.onRequestEnd.bind(this);
		this.responseParser = new _http_common.HTTPParser();
		this.responseParser.initialize(_http_common.HTTPParser.RESPONSE, {});
		this.responseParser[_http_common.HTTPParser.kOnHeaders] = this.onResponseHeaders.bind(this);
		this.responseParser[_http_common.HTTPParser.kOnHeadersComplete] = this.onResponseStart.bind(this);
		this.responseParser[_http_common.HTTPParser.kOnBody] = this.onResponseBody.bind(this);
		this.responseParser[_http_common.HTTPParser.kOnMessageComplete] = this.onResponseEnd.bind(this);
		this.once("finish", () => freeParser(this.requestParser, this));
		if (this.baseUrl.protocol === "https:") {
			Reflect.set(this, "encrypted", true);
			Reflect.set(this, "authorized", false);
			Reflect.set(this, "getProtocol", () => "TLSv1.3");
			Reflect.set(this, "getSession", () => void 0);
			Reflect.set(this, "isSessionReused", () => false);
			Reflect.set(this, "getCipher", () => ({
				name: "AES256-SHA",
				standardName: "TLS_RSA_WITH_AES_256_CBC_SHA",
				version: "TLSv1.3"
			}));
		}
	}
	emit(event, ...args) {
		const emitEvent = super.emit.bind(this, event, ...args);
		if (this.responseListenersPromise) {
			this.responseListenersPromise.finally(emitEvent);
			return this.listenerCount(event) > 0;
		}
		return emitEvent();
	}
	destroy(error) {
		freeParser(this.responseParser, this);
		if (error) this.emit("error", error);
		return super.destroy(error);
	}
	/**
	* Establish this Socket connection as-is and pipe
	* its data/events through this Socket.
	*/
	passthrough() {
		this.socketState = "passthrough";
		if (this.destroyed) return;
		const socket = this.createConnection();
		this.originalSocket = socket;
		/**
		* @note Inherit the original socket's connection handle.
		* Without this, each push to the mock socket results in a
		* new "connection" listener being added (i.e. buffering pushes).
		* @see https://github.com/nodejs/node/blob/b18153598b25485ce4f54d0c5cb830a9457691ee/lib/net.js#L734
		*/
		if ("_handle" in socket) Object.defineProperty(this, "_handle", {
			value: socket._handle,
			enumerable: true,
			writable: true
		});
		this.once("close", () => {
			socket.removeAllListeners();
			if (!socket.destroyed) socket.destroy();
			this.originalSocket = void 0;
		});
		this.address = socket.address.bind(socket);
		let writeArgs;
		let headersWritten = false;
		while (writeArgs = this.writeBuffer.shift()) if (writeArgs !== void 0) {
			if (!headersWritten) {
				const [chunk, encoding, callback] = writeArgs;
				const chunkString = chunk.toString();
				const chunkBeforeRequestHeaders = chunkString.slice(0, chunkString.indexOf("\r\n") + 2);
				const chunkAfterRequestHeaders = chunkString.slice(chunk.indexOf("\r\n\r\n"));
				const headersChunk = `${chunkBeforeRequestHeaders}${getRawFetchHeaders(this.request.headers).filter(([name]) => {
					return name.toLowerCase() !== require_fetchUtils.INTERNAL_REQUEST_ID_HEADER_NAME;
				}).map(([name, value]) => `${name}: ${value}`).join("\r\n")}${chunkAfterRequestHeaders}`;
				socket.write(headersChunk, encoding, callback);
				headersWritten = true;
				continue;
			}
			socket.write(...writeArgs);
		}
		if (Reflect.get(socket, "encrypted")) [
			"encrypted",
			"authorized",
			"getProtocol",
			"getSession",
			"isSessionReused",
			"getCipher"
		].forEach((propertyName) => {
			Object.defineProperty(this, propertyName, {
				enumerable: true,
				get: () => {
					const value = Reflect.get(socket, propertyName);
					return typeof value === "function" ? value.bind(socket) : value;
				}
			});
		});
		socket.on("lookup", (...args) => this.emit("lookup", ...args)).on("connect", () => {
			this.connecting = socket.connecting;
			this.emit("connect");
		}).on("secureConnect", () => this.emit("secureConnect")).on("secure", () => this.emit("secure")).on("session", (session) => this.emit("session", session)).on("ready", () => this.emit("ready")).on("drain", () => this.emit("drain")).on("data", (chunk) => {
			this.push(chunk);
		}).on("error", (error) => {
			Reflect.set(this, "_hadError", Reflect.get(socket, "_hadError"));
			this.emit("error", error);
		}).on("resume", () => this.emit("resume")).on("timeout", () => this.emit("timeout")).on("prefinish", () => this.emit("prefinish")).on("finish", () => this.emit("finish")).on("close", (hadError) => this.emit("close", hadError)).on("end", () => this.emit("end"));
	}
	/**
	* Convert the given Fetch API `Response` instance to an
	* HTTP message and push it to the socket.
	*/
	async respondWith(response) {
		if (this.destroyed) return;
		(0, outvariant.invariant)(this.socketState !== "mock", "[MockHttpSocket] Failed to respond to the \"%s %s\" request with \"%s %s\": the request has already been handled", this.request?.method, this.request?.url, response.status, response.statusText);
		if (require_handleRequest.isPropertyAccessible(response, "type") && response.type === "error") {
			this.errorWith(/* @__PURE__ */ new TypeError("Network error"));
			return;
		}
		this.mockConnect();
		this.socketState = "mock";
		this.flushWriteBuffer();
		const serverResponse = new node_http.ServerResponse(new node_http.IncomingMessage(this));
		/**
		* Assign a mock socket instance to the server response to
		* spy on the response chunk writes. Push the transformed response chunks
		* to this `MockHttpSocket` instance to trigger the "data" event.
		* @note Providing the same `MockSocket` instance when creating `ServerResponse`
		* does not have the same effect.
		* @see https://github.com/nodejs/node/blob/10099bb3f7fd97bb9dd9667188426866b3098e07/test/parallel/test-http-server-response-standalone.js#L32
		*/
		serverResponse.assignSocket(new MockSocket({
			write: (chunk, encoding, callback) => {
				this.push(chunk, encoding);
				callback?.();
			},
			read() {}
		}));
		/**
		* @note Remove the `Connection` and `Date` response headers
		* injected by `ServerResponse` by default. Those are required
		* from the server but the interceptor is NOT technically a server.
		* It's confusing to add response headers that the developer didn't
		* specify themselves. They can always add these if they wish.
		* @see https://www.rfc-editor.org/rfc/rfc9110#field.date
		* @see https://www.rfc-editor.org/rfc/rfc9110#field.connection
		*/
		serverResponse.removeHeader("connection");
		serverResponse.removeHeader("date");
		const rawResponseHeaders = getRawFetchHeaders(response.headers);
		/**
		* @note Call `.writeHead` in order to set the raw response headers
		* in the same case as they were provided by the developer. Using
		* `.setHeader()`/`.appendHeader()` normalizes header names.
		*/
		serverResponse.writeHead(response.status, response.statusText || node_http.STATUS_CODES[response.status], rawResponseHeaders);
		this.once("error", () => {
			serverResponse.destroy();
		});
		if (response.body) try {
			const reader = response.body.getReader();
			while (true) {
				const { done, value } = await reader.read();
				if (done) {
					serverResponse.end();
					break;
				}
				serverResponse.write(value);
			}
		} catch (error) {
			if (error instanceof Error) {
				serverResponse.destroy();
				/**
				* @note Destroy the request socket gracefully.
				* Response stream errors do NOT produce request errors.
				*/
				this.destroy();
				return;
			}
			serverResponse.destroy();
			throw error;
		}
		else serverResponse.end();
		if (!this.shouldKeepAlive) {
			this.emit("readable");
			/**
			* @todo @fixme This is likely a hack.
			* Since we push null to the socket, it never propagates to the
			* parser, and the parser never calls "onResponseEnd" to close
			* the response stream. We are closing the stream here manually
			* but that shouldn't be the case.
			*/
			this.responseStream?.push(null);
			this.push(null);
		}
	}
	/**
	* Close this socket connection with the given error.
	*/
	errorWith(error) {
		this.destroy(error);
	}
	mockConnect() {
		this.connecting = false;
		const isIPv6 = node_net.default.isIPv6(this.connectionOptions.hostname) || this.connectionOptions.family === 6;
		const addressInfo = {
			address: isIPv6 ? "::1" : "127.0.0.1",
			family: isIPv6 ? "IPv6" : "IPv4",
			port: this.connectionOptions.port
		};
		this.address = () => addressInfo;
		this.emit("lookup", null, addressInfo.address, addressInfo.family === "IPv6" ? 6 : 4, this.connectionOptions.host);
		this.emit("connect");
		this.emit("ready");
		if (this.baseUrl.protocol === "https:") {
			this.emit("secure");
			this.emit("secureConnect");
			this.emit("session", this.connectionOptions.session || Buffer.from("mock-session-renegotiate"));
			this.emit("session", Buffer.from("mock-session-resume"));
		}
	}
	flushWriteBuffer() {
		for (const writeCall of this.writeBuffer) if (typeof writeCall[2] === "function") {
			writeCall[2]();
			/**
			* @note Remove the callback from the write call
			* so it doesn't get called twice on passthrough
			* if `request.end()` was called within `request.write()`.
			* @see https://github.com/mswjs/interceptors/issues/684
			*/
			writeCall[2] = void 0;
		}
	}
	onRequestBody(chunk) {
		(0, outvariant.invariant)(this.requestStream, "Failed to write to a request stream: stream does not exist");
		this.requestStream.push(chunk);
	}
	onRequestEnd() {
		if (this.requestStream) this.requestStream.push(null);
	}
	onResponseBody(chunk) {
		(0, outvariant.invariant)(this.responseStream, "Failed to write to a response stream: stream does not exist");
		this.responseStream.push(chunk);
	}
	onResponseEnd() {
		if (this.responseStream) this.responseStream.push(null);
	}
};

//#endregion
//#region src/interceptors/ClientRequest/agents.ts
var MockAgent = class extends node_http.default.Agent {
	constructor(options) {
		super();
		this.customAgent = options.customAgent;
		this.onRequest = options.onRequest;
		this.onResponse = options.onResponse;
	}
	createConnection(options, callback) {
		const createConnection = this.customAgent instanceof node_http.default.Agent ? this.customAgent.createConnection : super.createConnection;
		const createConnectionOptions = this.customAgent instanceof node_http.default.Agent ? {
			...options,
			...this.customAgent.options
		} : options;
		return new MockHttpSocket({
			connectionOptions: options,
			createConnection: createConnection.bind(this.customAgent || this, createConnectionOptions, callback),
			onRequest: this.onRequest.bind(this),
			onResponse: this.onResponse.bind(this)
		});
	}
};
var MockHttpsAgent = class extends node_https.default.Agent {
	constructor(options) {
		super();
		this.customAgent = options.customAgent;
		this.onRequest = options.onRequest;
		this.onResponse = options.onResponse;
	}
	createConnection(options, callback) {
		const createConnection = this.customAgent instanceof node_http.default.Agent ? this.customAgent.createConnection : super.createConnection;
		const createConnectionOptions = this.customAgent instanceof node_http.default.Agent ? {
			...options,
			...this.customAgent.options
		} : options;
		return new MockHttpSocket({
			connectionOptions: options,
			createConnection: createConnection.bind(this.customAgent || this, createConnectionOptions, callback),
			onRequest: this.onRequest.bind(this),
			onResponse: this.onResponse.bind(this)
		});
	}
};

//#endregion
//#region src/utils/getUrlByRequestOptions.ts
const logger$2 = new _open_draft_logger.Logger("utils getUrlByRequestOptions");
const DEFAULT_PATH = "/";
const DEFAULT_PROTOCOL = "http:";
const DEFAULT_HOSTNAME = "localhost";
const SSL_PORT = 443;
function getAgent(options) {
	return options.agent instanceof http.Agent ? options.agent : void 0;
}
function getProtocolByRequestOptions(options) {
	if (options.protocol) return options.protocol;
	const agentProtocol = getAgent(options)?.protocol;
	if (agentProtocol) return agentProtocol;
	const port = getPortByRequestOptions(options);
	return options.cert || port === SSL_PORT ? "https:" : options.uri?.protocol || DEFAULT_PROTOCOL;
}
function getPortByRequestOptions(options) {
	if (options.port) return Number(options.port);
	const agent = getAgent(options);
	if (agent?.options.port) return Number(agent.options.port);
	if (agent?.defaultPort) return Number(agent.defaultPort);
}
function getAuthByRequestOptions(options) {
	if (options.auth) {
		const [username, password] = options.auth.split(":");
		return {
			username,
			password
		};
	}
}
/**
* Returns true if host looks like an IPv6 address without surrounding brackets
* It assumes any host containing `:` is definitely not IPv4 and probably IPv6,
* but note that this could include invalid IPv6 addresses as well.
*/
function isRawIPv6Address(host) {
	return host.includes(":") && !host.startsWith("[") && !host.endsWith("]");
}
function getHostname(options) {
	let host = options.hostname || options.host;
	if (host) {
		if (isRawIPv6Address(host)) host = `[${host}]`;
		return new URL(`http://${host}`).hostname;
	}
	return DEFAULT_HOSTNAME;
}
/**
* Creates a `URL` instance from a given `RequestOptions` object.
*/
function getUrlByRequestOptions(options) {
	logger$2.info("request options", options);
	if (options.uri) {
		logger$2.info("constructing url from explicitly provided \"options.uri\": %s", options.uri);
		return new URL(options.uri.href);
	}
	logger$2.info("figuring out url from request options...");
	const protocol = getProtocolByRequestOptions(options);
	logger$2.info("protocol", protocol);
	const port = getPortByRequestOptions(options);
	logger$2.info("port", port);
	const hostname = getHostname(options);
	logger$2.info("hostname", hostname);
	const path = options.path || DEFAULT_PATH;
	logger$2.info("path", path);
	const credentials = getAuthByRequestOptions(options);
	logger$2.info("credentials", credentials);
	const authString = credentials ? `${credentials.username}:${credentials.password}@` : "";
	logger$2.info("auth string:", authString);
	const portString = typeof port !== "undefined" ? `:${port}` : "";
	const url = new URL(`${protocol}//${hostname}${portString}${path}`);
	url.username = credentials?.username || "";
	url.password = credentials?.password || "";
	logger$2.info("created url:", url);
	return url;
}

//#endregion
//#region src/utils/cloneObject.ts
const logger$1 = new _open_draft_logger.Logger("cloneObject");
function isPlainObject(obj) {
	logger$1.info("is plain object?", obj);
	if (obj == null || !obj.constructor?.name) {
		logger$1.info("given object is undefined, not a plain object...");
		return false;
	}
	logger$1.info("checking the object constructor:", obj.constructor.name);
	return obj.constructor.name === "Object";
}
function cloneObject(obj) {
	logger$1.info("cloning object:", obj);
	const enumerableProperties = Object.entries(obj).reduce((acc, [key, value]) => {
		logger$1.info("analyzing key-value pair:", key, value);
		acc[key] = isPlainObject(value) ? cloneObject(value) : value;
		return acc;
	}, {});
	return isPlainObject(obj) ? enumerableProperties : Object.assign(Object.getPrototypeOf(obj), enumerableProperties);
}

//#endregion
//#region src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts
const logger = new _open_draft_logger.Logger("http normalizeClientRequestArgs");
function resolveRequestOptions(args, url) {
	if (typeof args[1] === "undefined" || typeof args[1] === "function") {
		logger.info("request options not provided, deriving from the url", url);
		return (0, node_url.urlToHttpOptions)(url);
	}
	if (args[1]) {
		logger.info("has custom RequestOptions!", args[1]);
		const requestOptionsFromUrl = (0, node_url.urlToHttpOptions)(url);
		logger.info("derived RequestOptions from the URL:", requestOptionsFromUrl);
		/**
		* Clone the request options to lock their state
		* at the moment they are provided to `ClientRequest`.
		* @see https://github.com/mswjs/interceptors/issues/86
		*/
		logger.info("cloning RequestOptions...");
		const clonedRequestOptions = cloneObject(args[1]);
		logger.info("successfully cloned RequestOptions!", clonedRequestOptions);
		return {
			...requestOptionsFromUrl,
			...clonedRequestOptions
		};
	}
	logger.info("using an empty object as request options");
	return {};
}
/**
* Overrides the given `URL` instance with the explicit properties provided
* on the `RequestOptions` object. The options object takes precedence,
* and will replace URL properties like "host", "path", and "port", if specified.
*/
function overrideUrlByRequestOptions(url, options) {
	url.host = options.host || url.host;
	url.hostname = options.hostname || url.hostname;
	url.port = options.port ? options.port.toString() : url.port;
	if (options.path) {
		const parsedOptionsPath = (0, node_url.parse)(options.path, false);
		url.pathname = parsedOptionsPath.pathname || "";
		url.search = parsedOptionsPath.search || "";
	}
	return url;
}
function resolveCallback(args) {
	return typeof args[1] === "function" ? args[1] : args[2];
}
/**
* Normalizes parameters given to a `http.request` call
* so it always has a `URL` and `RequestOptions`.
*/
function normalizeClientRequestArgs(defaultProtocol, args) {
	let url;
	let options;
	let callback;
	logger.info("arguments", args);
	logger.info("using default protocol:", defaultProtocol);
	if (args.length === 0) {
		const url$1 = new node_url.URL("http://localhost");
		return [url$1, resolveRequestOptions(args, url$1)];
	}
	if (typeof args[0] === "string") {
		logger.info("first argument is a location string:", args[0]);
		url = new node_url.URL(args[0]);
		logger.info("created a url:", url);
		const requestOptionsFromUrl = (0, node_url.urlToHttpOptions)(url);
		logger.info("request options from url:", requestOptionsFromUrl);
		options = resolveRequestOptions(args, url);
		logger.info("resolved request options:", options);
		callback = resolveCallback(args);
	} else if (args[0] instanceof node_url.URL) {
		url = args[0];
		logger.info("first argument is a URL:", url);
		if (typeof args[1] !== "undefined" && require_handleRequest.isObject(args[1])) url = overrideUrlByRequestOptions(url, args[1]);
		options = resolveRequestOptions(args, url);
		logger.info("derived request options:", options);
		callback = resolveCallback(args);
	} else if ("hash" in args[0] && !("method" in args[0])) {
		const [legacyUrl] = args;
		logger.info("first argument is a legacy URL:", legacyUrl);
		if (legacyUrl.hostname === null) {
			/**
			* We are dealing with a relative url, so use the path as an "option" and
			* merge in any existing options, giving priority to existing options -- i.e. a path in any
			* existing options will take precedence over the one contained in the url. This is consistent
			* with the behaviour in ClientRequest.
			* @see https://github.com/nodejs/node/blob/d84f1312915fe45fe0febe888db692c74894c382/lib/_http_client.js#L122
			*/
			logger.info("given legacy URL is relative (no hostname)");
			return require_handleRequest.isObject(args[1]) ? normalizeClientRequestArgs(defaultProtocol, [{
				path: legacyUrl.path,
				...args[1]
			}, args[2]]) : normalizeClientRequestArgs(defaultProtocol, [{ path: legacyUrl.path }, args[1]]);
		}
		logger.info("given legacy url is absolute");
		const resolvedUrl = new node_url.URL(legacyUrl.href);
		return args[1] === void 0 ? normalizeClientRequestArgs(defaultProtocol, [resolvedUrl]) : typeof args[1] === "function" ? normalizeClientRequestArgs(defaultProtocol, [resolvedUrl, args[1]]) : normalizeClientRequestArgs(defaultProtocol, [
			resolvedUrl,
			args[1],
			args[2]
		]);
	} else if (require_handleRequest.isObject(args[0])) {
		options = { ...args[0] };
		logger.info("first argument is RequestOptions:", options);
		options.protocol = options.protocol || defaultProtocol;
		logger.info("normalized request options:", options);
		url = getUrlByRequestOptions(options);
		logger.info("created a URL from RequestOptions:", url.href);
		callback = resolveCallback(args);
	} else throw new Error(`Failed to construct ClientRequest with these parameters: ${args}`);
	options.protocol = options.protocol || url.protocol;
	options.method = options.method || "GET";
	/**
	* Ensure that the default Agent is always set.
	* This prevents the protocol mismatch for requests with { agent: false },
	* where the global Agent is inferred.
	* @see https://github.com/mswjs/msw/issues/1150
	* @see https://github.com/nodejs/node/blob/418ff70b810f0e7112d48baaa72932a56cfa213b/lib/_http_client.js#L130
	* @see https://github.com/nodejs/node/blob/418ff70b810f0e7112d48baaa72932a56cfa213b/lib/_http_client.js#L157-L159
	*/
	if (!options._defaultAgent) {
		logger.info("has no default agent, setting the default agent for \"%s\"", options.protocol);
		options._defaultAgent = options.protocol === "https:" ? node_https.globalAgent : node_http.globalAgent;
	}
	logger.info("successfully resolved url:", url.href);
	logger.info("successfully resolved options:", options);
	logger.info("successfully resolved callback:", callback);
	/**
	* @note If the user-provided URL is not a valid URL in Node.js,
	* (e.g. the one provided by the JSDOM polyfills), case it to
	* string. Otherwise, this throws on Node.js incompatibility
	* (`ERR_INVALID_ARG_TYPE` on the connection listener)
	* @see https://github.com/node-fetch/node-fetch/issues/1376#issuecomment-966435555
	*/
	if (!(url instanceof node_url.URL)) url = url.toString();
	return [
		url,
		options,
		callback
	];
}

//#endregion
//#region src/interceptors/ClientRequest/index.ts
var ClientRequestInterceptor = class ClientRequestInterceptor extends require_fetchUtils.Interceptor {
	static {
		this.symbol = Symbol.for("client-request-interceptor");
	}
	constructor() {
		super(ClientRequestInterceptor.symbol);
		this.onRequest = async ({ request, socket }) => {
			const controller = new require_fetchUtils.RequestController(request, {
				passthrough() {
					socket.passthrough();
				},
				async respondWith(response) {
					await socket.respondWith(response);
				},
				errorWith(reason) {
					if (reason instanceof Error) socket.errorWith(reason);
				}
			});
			await require_handleRequest.handleRequest({
				request,
				requestId: Reflect.get(request, kRequestId),
				controller,
				emitter: this.emitter
			});
		};
		this.onResponse = async ({ requestId, request, response, isMockedResponse }) => {
			return require_handleRequest.emitAsync(this.emitter, "response", {
				requestId,
				request,
				response,
				isMockedResponse
			});
		};
	}
	setup() {
		const { ClientRequest: OriginalClientRequest, get: originalGet, request: originalRequest } = node_http.default;
		const { get: originalHttpsGet, request: originalHttpsRequest } = node_https.default;
		const onRequest = this.onRequest.bind(this);
		const onResponse = this.onResponse.bind(this);
		node_http.default.ClientRequest = new Proxy(node_http.default.ClientRequest, { construct: (target, args) => {
			const [url, options, callback] = normalizeClientRequestArgs("http:", args);
			options.agent = new (options.protocol === "https:" ? MockHttpsAgent : MockAgent)({
				customAgent: options.agent,
				onRequest,
				onResponse
			});
			return Reflect.construct(target, [
				url,
				options,
				callback
			]);
		} });
		node_http.default.request = new Proxy(node_http.default.request, { apply: (target, thisArg, args) => {
			const [url, options, callback] = normalizeClientRequestArgs("http:", args);
			options.agent = new MockAgent({
				customAgent: options.agent,
				onRequest,
				onResponse
			});
			return Reflect.apply(target, thisArg, [
				url,
				options,
				callback
			]);
		} });
		node_http.default.get = new Proxy(node_http.default.get, { apply: (target, thisArg, args) => {
			const [url, options, callback] = normalizeClientRequestArgs("http:", args);
			options.agent = new MockAgent({
				customAgent: options.agent,
				onRequest,
				onResponse
			});
			return Reflect.apply(target, thisArg, [
				url,
				options,
				callback
			]);
		} });
		node_https.default.request = new Proxy(node_https.default.request, { apply: (target, thisArg, args) => {
			const [url, options, callback] = normalizeClientRequestArgs("https:", args);
			options.agent = new MockHttpsAgent({
				customAgent: options.agent,
				onRequest,
				onResponse
			});
			return Reflect.apply(target, thisArg, [
				url,
				options,
				callback
			]);
		} });
		node_https.default.get = new Proxy(node_https.default.get, { apply: (target, thisArg, args) => {
			const [url, options, callback] = normalizeClientRequestArgs("https:", args);
			options.agent = new MockHttpsAgent({
				customAgent: options.agent,
				onRequest,
				onResponse
			});
			return Reflect.apply(target, thisArg, [
				url,
				options,
				callback
			]);
		} });
		recordRawFetchHeaders();
		this.subscriptions.push(() => {
			node_http.default.ClientRequest = OriginalClientRequest;
			node_http.default.get = originalGet;
			node_http.default.request = originalRequest;
			node_https.default.get = originalHttpsGet;
			node_https.default.request = originalHttpsRequest;
			restoreHeadersPrototype();
		});
	}
};

//#endregion
Object.defineProperty(exports, 'ClientRequestInterceptor', {
  enumerable: true,
  get: function () {
    return ClientRequestInterceptor;
  }
});
//# sourceMappingURL=ClientRequest-2loAH7xM.cjs.map