text: 'use strict';
  const net = require('net');
  
  class TimeoutError extends Error {
  	constructor(threshold, event) {
  		super(`Timeout awaiting '${event}' for ${threshold}ms`);
  		this.name = 'TimeoutError';
  		this.code = 'ETIMEDOUT';
  		this.event = event;
  	}
  }
  
  const reentry = Symbol('reentry');
  
  const noop = () => {};
  
  module.exports = (request, delays, options) => {
  	/* istanbul ignore next: this makes sure timed-out isn't called twice */
  	if (request[reentry]) {
  		return;
  	}
  
  	request[reentry] = true;
  
  	let stopNewTimeouts = false;
  
  	const addTimeout = (delay, callback, ...args) => {
  		// An error had been thrown before. Going further would result in uncaught errors.
  		// See https://github.com/sindresorhus/got/issues/631#issuecomment-435675051
  		if (stopNewTimeouts) {
  			return noop;
  		}
  
  		// Event loop order is timers, poll, immediates.
  		// The timed event may emit during the current tick poll phase, so
  		// defer calling the handler until the poll phase completes.
  		let immediate;
  		const timeout = setTimeout(() => {
  			immediate = setImmediate(callback, delay, ...args);
  			/* istanbul ignore next: added in node v9.7.0 */
  			if (immediate.unref) {
  				immediate.unref();
  			}
  		}, delay);
  
  		/* istanbul ignore next: in order to support electron renderer */
  		if (timeout.unref) {
  			timeout.unref();
  		}
  
  		const cancel = () => {
  			clearTimeout(timeout);
  			clearImmediate(immediate);
  		};
  
  		cancelers.push(cancel);
  
  		return cancel;
  	};
  
  	const {host, hostname} = options;
  	const timeoutHandler = (delay, event) => {
  		request.emit('error', new TimeoutError(delay, event));
  		request.once('error', () => {}); // Ignore the `socket hung up` error made by request.abort()
  
  		request.abort();
  	};
  
  	const cancelers = [];
  	const cancelTimeouts = () => {
  		stopNewTimeouts = true;
  		cancelers.forEach(cancelTimeout => cancelTimeout());
  	};
  
  	request.once('error', cancelTimeouts);
  	request.once('response', response => {
  		response.once('end', cancelTimeouts);
  	});
  
  	if (delays.request !== undefined) {
  		addTimeout(delays.request, timeoutHandler, 'request');
  	}
  
  	if (delays.socket !== undefined) {
  		const socketTimeoutHandler = () => {
  			timeoutHandler(delays.socket, 'socket');
  		};
  
  		request.setTimeout(delays.socket, socketTimeoutHandler);
  
  		// `request.setTimeout(0)` causes a memory leak.
  		// We can just remove the listener and forget about the timer - it's unreffed.
  		// See https://github.com/sindresorhus/got/issues/690
  		cancelers.push(() => request.removeListener('timeout', socketTimeoutHandler));
  	}
  
  	if (delays.lookup !== undefined && !request.socketPath && !net.isIP(hostname || host)) {
  		request.once('socket', socket => {
  			/* istanbul ignore next: hard to test */
  			if (socket.connecting) {
  				const cancelTimeout = addTimeout(delays.lookup, timeoutHandler, 'lookup');
  				socket.once('lookup', cancelTimeout);
  			}
  		});
  	}
  
  	if (delays.connect !== undefined) {
  		request.once('socket', socket => {
  			/* istanbul ignore next: hard to test */
  			if (socket.connecting) {
  				const timeConnect = () => addTimeout(delays.connect, timeoutHandler, 'connect');
  
  				if (request.socketPath || net.isIP(hostname || host)) {
  					socket.once('connect', timeConnect());
  				} else {
  					socket.once('lookup', error => {
  						if (error === null) {
  							socket.once('connect', timeConnect());
  						}
  					});
  				}
  			}
  		});
  	}
  
  	if (delays.secureConnect !== undefined && options.protocol === 'https:') {
  		request.once('socket', socket => {
  			/* istanbul ignore next: hard to test */
  			if (socket.connecting) {
  				socket.once('connect', () => {
  					const cancelTimeout = addTimeout(delays.secureConnect, timeoutHandler, 'secureConnect');
  					socket.once('secureConnect', cancelTimeout);
  				});
  			}
  		});
  	}
  
  	if (delays.send !== undefined) {
  		request.once('socket', socket => {
  			const timeRequest = () => addTimeout(delays.send, timeoutHandler, 'send');
  			/* istanbul ignore next: hard to test */
  			if (socket.connecting) {
  				socket.once('connect', () => {
  					request.once('upload-complete', timeRequest());
  				});
  			} else {
  				request.once('upload-complete', timeRequest());
  			}
  		});
  	}
  
  	if (delays.response !== undefined) {
  		request.once('upload-complete', () => {
  			const cancelTimeout = addTimeout(delays.response, timeoutHandler, 'response');
  			request.once('response', cancelTimeout);
  		});
  	}
  };
  
  module.exports.TimeoutError = TimeoutError;
  