コード例 #1
0
ファイル: stream.ts プロジェクト: sindresorhus/got
test('check for pipe method', withServer, (t, server, got) => {
	server.get('/', defaultHandler);

	const stream = got.stream('');
	t.true(is.function_(stream.pipe));
	t.true(is.function_(stream.on('foobar', () => {}).pipe));

	stream.destroy();
});
コード例 #2
0
ファイル: get-response.ts プロジェクト: sindresorhus/got
export default (response: IncomingMessage, options: Options, emitter: EventEmitter) => {
	const downloadBodySize = Number(response.headers['content-length']) || undefined;

	const progressStream: TransformStream = downloadProgress(response, emitter, downloadBodySize);

	mimicResponse(response, progressStream);

	const newResponse = (
		options.decompress === true &&
		is.function_(decompressResponse) &&
		options.method !== 'HEAD' ? decompressResponse(progressStream as unknown as IncomingMessage) : progressStream
	) as Response;

	if (!options.decompress && ['gzip', 'deflate', 'br'].includes(response.headers['content-encoding'] || '')) {
		options.encoding = null;
	}

	emitter.emit('response', newResponse);

	emitter.emit('downloadProgress', {
		percent: 0,
		transferred: 0,
		total: downloadBodySize
	});

	response.pipe(progressStream);
};
コード例 #3
0
						get: (target, name) => {
							if (name === 'trailers' || name === 'rawTrailers') {
								return [];
							}

							const value = target[name];
							return is.function_(value) ? value.bind(target) : value;
						}
コード例 #4
0
export function findScripts(
  scriptPath: string,
  config: ResolvedDebugAgentConfig,
  fileStats: ScanStats,
  logger: consoleLogLevel.Logger
): string[] {
  // (path: string, knownFiles: string[], resolved: string[]) => string[]
  const resolved = resolveScripts(scriptPath, config, fileStats);
  if (config.pathResolver) {
    if (!is.function_(config.pathResolver)) {
      logger.warn(
        `The 'pathResolver' config must be a function.  Continuing ` +
          `with the agent's default behavior.`
      );
      return resolved;
    }

    const knownFiles = Object.keys(fileStats);
    const calculatedPaths = config.pathResolver(
      scriptPath,
      knownFiles,
      resolved
    );
    if (calculatedPaths === undefined) {
      return resolved;
    }

    if (!calculatedPaths || !Array.isArray(calculatedPaths)) {
      logger.warn(
        `The 'pathResolver' config function returned a value ` +
          `other than 'undefined' or an array of strings. Continuing with ` +
          `the agent's default behavior.`
      );
      return resolved;
    }

    for (const path of calculatedPaths) {
      if (knownFiles.indexOf(path) === -1) {
        logger.warn(
          `The 'pathResolver' config function returned a path ` +
            `'${path}' that is not in the list of paths known to the debug agent ` +
            JSON.stringify(knownFiles, null, 2) +
            ` only known paths can be returned. Continuing with the agent's ` +
            `default behavior.`
        );
        return resolved;
      }
    }

    return calculatedPaths;
  }
  return resolved;
}
コード例 #5
0
ファイル: is-form-data.ts プロジェクト: sindresorhus/got
export default (body: unknown): body is FormData => is.nodeStream(body) && is.function_((body as FormData).getBoundary);
コード例 #6
0
	const get = async (options: Options) => {
		const currentUrl = redirectString || requestUrl;

		if (options.protocol !== 'http:' && options.protocol !== 'https:') {
			throw new UnsupportedProtocolError(options);
		}

		decodeURI(currentUrl);

		let requestFn: RequestFunction;
		if (is.function_(options.request)) {
			requestFn = options.request;
		} else {
			requestFn = options.protocol === 'https:' ? https.request : http.request;
		}

		if (agents) {
			const protocolName = options.protocol === 'https:' ? 'https' : 'http';
			options.agent = agents[protocolName] || options.agent;
		}

		/* istanbul ignore next: electron.net is broken */
		// No point in typing process.versions correctly, as
		// process.version.electron is used only once, right here.
		if (options.useElectronNet && (process.versions as any).electron) {
			// @ts-ignore
			const electron = dynamicRequire(module, 'electron') as any; // Trick webpack
			requestFn = electron.net.request || electron.remote.net.request;
		}

		if (options.cookieJar) {
			const cookieString = await getCookieString(currentUrl, {});

			if (is.nonEmptyString(cookieString)) {
				options.headers.cookie = cookieString;
			}
		}

		let timings: Timings;
		// TODO: Properly type this.
		const handleResponse = async response => {
			try {
				/* istanbul ignore next: fixes https://github.com/electron/electron/blob/cbb460d47628a7a146adf4419ed48550a98b2923/lib/browser/api/net.js#L59-L65 */
				if (options.useElectronNet) {
					response = new Proxy(response, {
						get: (target, name) => {
							if (name === 'trailers' || name === 'rawTrailers') {
								return [];
							}

							const value = target[name];
							return is.function_(value) ? value.bind(target) : value;
						}
					});
				}

				const {statusCode} = response;
				response.statusMessage = response.statusMessage || http.STATUS_CODES[statusCode];
				response.url = currentUrl;
				response.requestUrl = requestUrl;
				response.retryCount = retryCount;
				response.timings = timings;
				response.redirectUrls = redirects;
				response.request = {options};
				response.isFromCache = response.fromCache || false;
				delete response.fromCache;

				const rawCookies = response.headers['set-cookie'];
				if (options.cookieJar && rawCookies) {
					await Promise.all(rawCookies.map(rawCookie => setCookie(rawCookie, response.url)));
				}

				if (options.followRedirect && 'location' in response.headers) {
					if (allMethodRedirectCodes.has(statusCode) || (getMethodRedirectCodes.has(statusCode) && (options.method === 'GET' || options.method === 'HEAD'))) {
						response.resume(); // We're being redirected, we don't care about the response.

						if (statusCode === 303) {
							// Server responded with "see other", indicating that the resource exists at another location,
							// and the client should request it from that location via GET or HEAD.
							options.method = 'GET';
						}

						if (redirects.length >= 10) {
							throw new MaxRedirectsError(response, options);
						}

						// Handles invalid URLs. See https://github.com/sindresorhus/got/issues/604
						const redirectBuffer = Buffer.from(response.headers.location, 'binary').toString();
						const redirectURL = new URL(redirectBuffer, currentUrl);
						redirectString = redirectURL.toString();

						redirects.push(redirectString);

						const redirectOptions = {
							...options,
							port: null,
							auth: null,
							...urlToOptions(redirectURL)
						};

						for (const hook of options.hooks.beforeRedirect) {
							// eslint-disable-next-line no-await-in-loop
							await hook(redirectOptions);
						}

						emitter.emit('redirect', response, redirectOptions);

						await get(redirectOptions);
						return;
					}
				}

				getResponse(response, options, emitter);
			} catch (error) {
				emitError(error);
			}
		};

		const handleRequest = (request: http.ClientRequest) => {
			if (shouldAbort) {
				request.abort();
				return;
			}

			currentRequest = request;

			request.on('error', error => {
				if (request.aborted || error.message === 'socket hang up') {
					return;
				}

				if (error instanceof TimedOutTimeoutError) {
					error = new TimeoutError(error, timings, options);
				} else {
					error = new RequestError(error, options);
				}

				if (emitter.retry(error) === false) {
					emitError(error);
				}
			});

			timings = timer(request);

			uploadProgress(request, emitter, uploadBodySize);

			if (options.gotTimeout) {
				// TODO: Properly type this. `preNormalizeArguments` coerces `gotTimeout` to `Delays`.
				timedOut(request, options.gotTimeout as Delays, options);
			}

			emitter.emit('request', request);

			const uploadComplete = () => {
				request.emit('upload-complete');
			};

			try {
				if (is.nodeStream(options.body)) {
					options.body.once('end', uploadComplete);
					options.body.pipe(request);
					options.body = undefined;
				} else if (options.body) {
					request.end(options.body, uploadComplete);
				} else if (input && (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH')) {
					input.once('end', uploadComplete);
					input.pipe(request);
				} else {
					request.end(uploadComplete);
				}
			} catch (error) {
				emitError(new RequestError(error, options));
			}
		};

		if (options.cache) {
			const cacheableRequest = new CacheableRequest(requestFn, options.cache);
			// TODO: Properly type this.
			const cacheRequest = cacheableRequest(options as https.RequestOptions, handleResponse);

			cacheRequest.once('error', error => {
				if (error instanceof CacheableRequest.RequestError) {
					emitError(new RequestError(error, options));
				} else {
					emitError(new CacheError(error, options));
				}
			});

			cacheRequest.once('request', handleRequest);
		} else {
			// Catches errors thrown by calling requestFn(...)
			try {
				// TODO: Properly type this.
				handleRequest(requestFn(options as https.RequestOptions, handleResponse));
			} catch (error) {
				emitError(new RequestError(error, options));
			}
		}
	};
コード例 #7
0
export const normalizeArguments = (url, options, defaults?: any) => {
	if (is.plainObject(url)) {
		options = {...url, ...options};
		url = options.url || '';
		delete options.url;
	}

	if (defaults) {
		options = merge({}, defaults.options, options ? preNormalizeArguments(options, defaults.options) : {});
	} else {
		options = merge({}, preNormalizeArguments(options));
	}

	if (!is.string(url) && !is.object(url)) {
		throw new TypeError(`Parameter \`url\` must be a string or object, not ${is(url)}`);
	}

	if (is.string(url) && !(url === '' && !options.baseUrl)) {
		if (options.baseUrl) {
			if (url.startsWith('/')) {
				url = url.slice(1);
			}
		} else {
			url = url.replace(/^unix:/, 'http://$&');
		}

		url = urlToOptions(new URL(url, options.baseUrl));
	} else if (is(url) === 'URL') {
		url = urlToOptions(url);
	}

	// Override both null/undefined with default protocol
	options = merge({path: ''}, url, {protocol: url.protocol || 'https:'}, options);

	for (const hook of options.hooks.init) {
		const called = hook(options);

		if (is.promise(called)) {
			throw new TypeError('The `init` hook must be a synchronous function');
		}
	}

	const {baseUrl} = options;
	Object.defineProperty(options, 'baseUrl', {
		set: () => {
			throw new Error('Failed to set baseUrl. Options are normalized already.');
		},
		get: () => baseUrl
	});

	let {searchParams} = options;
	delete options.searchParams;

	if (options.query) {
		if (!shownDeprecation) {
			console.warn('`options.query` is deprecated. We support it solely for compatibility - it will be removed in Got 11. Use `options.searchParams` instead.');
			shownDeprecation = true;
		}

		searchParams = options.query;
		delete options.query;
	}

	// TODO: This should be used in the `options` type instead
	interface SearchParams {
		[key: string]: string | number | boolean | null;
	}

	if (is.nonEmptyString(searchParams) || is.nonEmptyObject(searchParams) || searchParams instanceof URLSearchParams) {
		if (!is.string(searchParams)) {
			if (!(searchParams instanceof URLSearchParams)) {
				validateSearchParams(searchParams);
				searchParams = searchParams as SearchParams;
			}

			searchParams = (new URLSearchParams(searchParams)).toString();
		}

		options.path = `${options.path.split('?')[0]}?${searchParams}`;
	}

	if (options.hostname === 'unix') {
		const matches = /(.+?):(.+)/.exec(options.path);

		if (matches) {
			const [, socketPath, path] = matches;
			options = {
				...options,
				socketPath,
				path,
				host: null
			};
		}
	}

	const {headers} = options;
	for (const [key, value] of Object.entries(headers)) {
		if (is.nullOrUndefined(value)) {
			delete headers[key];
		}
	}

	if (options.decompress && is.undefined(headers['accept-encoding'])) {
		headers['accept-encoding'] = supportsBrotli ? 'gzip, deflate, br' : 'gzip, deflate';
	}

	if (options.method) {
		options.method = options.method.toUpperCase();
	}

	if (!is.function_(options.retry.retries)) {
		const {retries} = options.retry;

		options.retry.retries = (iteration, error) => {
			if (iteration > retries) {
				return 0;
			}

			const hasCode = Reflect.has(error, 'code') && options.retry.errorCodes.has(error.code);
			const hasMethod = Reflect.has(error, 'options') && options.retry.methods.has(error.options.method);
			const hasStatusCode = Reflect.has(error, 'response') && options.retry.statusCodes.has(error.response.statusCode);
			if ((!error || !hasCode) && (!hasMethod || !hasStatusCode)) {
				return 0;
			}

			const {response} = error;
			if (response && Reflect.has(response.headers, 'retry-after') && retryAfterStatusCodes.has(response.statusCode)) {
				let after = Number(response.headers['retry-after']);
				if (is.nan(after)) {
					after = Date.parse(response.headers['retry-after']) - Date.now();
				} else {
					after *= 1000;
				}

				if (after > options.retry.maxRetryAfter) {
					return 0;
				}

				return after;
			}

			if (response && response.statusCode === 413) {
				return 0;
			}

			const noise = Math.random() * 100;
			return ((2 ** (iteration - 1)) * 1000) + noise;
		};
	}

	return options;
};