Lazy import proxy-agent

This commit is contained in:
Fedor Indutny 2024-03-20 11:05:10 -07:00 committed by GitHub
parent 83e8f4b59d
commit 091b50c414
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 60 additions and 39 deletions

View file

@ -338,7 +338,7 @@ describe('updater/differential', () => {
await assert.isRejected( await assert.isRejected(
download(outFile, data, { download(outFile, data, {
gotOptions: { gotOptions: {
...getGotOptions(), ...(await getGotOptions()),
timeout: { timeout: {
connect: 0.5 * durations.SECOND, connect: 0.5 * durations.SECOND,
lookup: 0.5 * durations.SECOND, lookup: 0.5 * durations.SECOND,

View file

@ -19,6 +19,7 @@ import * as durations from '../util/durations';
import { sleep } from '../util/sleep'; import { sleep } from '../util/sleep';
import { drop } from '../util/drop'; import { drop } from '../util/drop';
import { createProxyAgent } from '../util/createProxyAgent'; import { createProxyAgent } from '../util/createProxyAgent';
import type { ProxyAgent } from '../util/createProxyAgent';
import { SocketStatus } from '../types/SocketStatus'; import { SocketStatus } from '../types/SocketStatus';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import * as Bytes from '../Bytes'; import * as Bytes from '../Bytes';
@ -84,7 +85,7 @@ export class SocketManager extends EventListener {
private credentials?: WebAPICredentials; private credentials?: WebAPICredentials;
private readonly proxyAgent?: ReturnType<typeof createProxyAgent>; private proxyAgent?: ProxyAgent;
private status = SocketStatus.CLOSED; private status = SocketStatus.CLOSED;
@ -105,10 +106,6 @@ export class SocketManager extends EventListener {
constructor(private readonly options: SocketManagerOptions) { constructor(private readonly options: SocketManagerOptions) {
super(); super();
if (options.proxyUrl) {
this.proxyAgent = createProxyAgent(options.proxyUrl);
}
this.hasStoriesDisabled = options.hasStoriesDisabled; this.hasStoriesDisabled = options.hasStoriesDisabled;
} }
@ -338,6 +335,11 @@ export class SocketManager extends EventListener {
url: string; url: string;
extraHeaders?: Record<string, string>; extraHeaders?: Record<string, string>;
}): Promise<WebSocket> { }): Promise<WebSocket> {
// Create proxy agent lazily
if (this.options.proxyUrl && !this.proxyAgent) {
this.proxyAgent = await createProxyAgent(this.options.proxyUrl);
}
return connectWebSocket({ return connectWebSocket({
name: 'art-creator-provisioning', name: 'art-creator-provisioning',
url, url,

View file

@ -31,6 +31,7 @@ import { toWebSafeBase64, fromWebSafeBase64 } from '../util/webSafeBase64';
import { getBasicAuth } from '../util/getBasicAuth'; import { getBasicAuth } from '../util/getBasicAuth';
import { createHTTPSAgent } from '../util/createHTTPSAgent'; import { createHTTPSAgent } from '../util/createHTTPSAgent';
import { createProxyAgent } from '../util/createProxyAgent'; import { createProxyAgent } from '../util/createProxyAgent';
import type { ProxyAgent } from '../util/createProxyAgent';
import type { SocketStatus } from '../types/SocketStatus'; import type { SocketStatus } from '../types/SocketStatus';
import { VerificationTransport } from '../types/VerificationTransport'; import { VerificationTransport } from '../types/VerificationTransport';
import { toLogFormat } from '../types/errors'; import { toLogFormat } from '../types/errors';
@ -120,7 +121,7 @@ const GET_ATTACHMENT_CHUNK_TIMEOUT = 10 * durations.SECOND;
type AgentCacheType = { type AgentCacheType = {
[name: string]: { [name: string]: {
timestamp: number; timestamp: number;
agent: ReturnType<typeof createProxyAgent> | Agent; agent: ProxyAgent | Agent;
}; };
}; };
const agents: AgentCacheType = {}; const agents: AgentCacheType = {};
@ -259,7 +260,7 @@ async function _promiseAjax(
} }
agents[cacheKey] = { agents[cacheKey] = {
agent: proxyUrl agent: proxyUrl
? createProxyAgent(proxyUrl) ? await createProxyAgent(proxyUrl)
: createHTTPSAgent({ : createHTTPSAgent({
keepAlive: !options.disableSessionResumption, keepAlive: !options.disableSessionResumption,
maxCachedSessions: options.disableSessionResumption ? 0 : undefined, maxCachedSessions: options.disableSessionResumption ? 0 : undefined,
@ -1382,17 +1383,23 @@ export function initialize({
log.warn(`${logId}: Done`); log.warn(`${logId}: Done`);
} }
let fetchAgent: Agent; let fetchAgent: Agent | undefined;
if (proxyUrl) { const fetchForLinkPreviews: linkPreviewFetch.FetchFn = async (
fetchAgent = createProxyAgent(proxyUrl); href,
} else { init
fetchAgent = createHTTPSAgent({ ) => {
keepAlive: false, if (!fetchAgent) {
maxCachedSessions: 0, if (proxyUrl) {
}); fetchAgent = await createProxyAgent(proxyUrl);
} } else {
const fetchForLinkPreviews: linkPreviewFetch.FetchFn = (href, init) => fetchAgent = createHTTPSAgent({
fetch(href, { ...init, agent: fetchAgent }); keepAlive: false,
maxCachedSessions: 0,
});
}
}
return fetch(href, { ...init, agent: fetchAgent });
};
// Thanks, function hoisting! // Thanks, function hoisting!
return { return {

View file

@ -9,7 +9,7 @@ import { strictAssert } from '../util/assert';
import { explodePromise } from '../util/explodePromise'; import { explodePromise } from '../util/explodePromise';
import { getUserAgent } from '../util/getUserAgent'; import { getUserAgent } from '../util/getUserAgent';
import * as durations from '../util/durations'; import * as durations from '../util/durations';
import type { createProxyAgent } from '../util/createProxyAgent'; import type { ProxyAgent } from '../util/createProxyAgent';
import { createHTTPSAgent } from '../util/createHTTPSAgent'; import { createHTTPSAgent } from '../util/createHTTPSAgent';
import * as log from '../logging/log'; import * as log from '../logging/log';
import * as Timers from '../Timers'; import * as Timers from '../Timers';
@ -28,7 +28,7 @@ export type ConnectOptionsType<Resource extends IResource> = Readonly<{
url: string; url: string;
certificateAuthority?: string; certificateAuthority?: string;
version: string; version: string;
proxyAgent?: ReturnType<typeof createProxyAgent>; proxyAgent?: ProxyAgent;
timeout?: number; timeout?: number;
extraHeaders?: Record<string, string>; extraHeaders?: Record<string, string>;

View file

@ -10,6 +10,7 @@ import type { LoggerType } from '../../types/Logging';
import { isOlderThan } from '../../util/timestamp'; import { isOlderThan } from '../../util/timestamp';
import { HOUR } from '../../util/durations'; import { HOUR } from '../../util/durations';
import { createProxyAgent } from '../../util/createProxyAgent'; import { createProxyAgent } from '../../util/createProxyAgent';
import type { ProxyAgent } from '../../util/createProxyAgent';
// It is 24 hours, but we don't want latency between server and client to be // It is 24 hours, but we don't want latency between server and client to be
// count. // count.
@ -30,15 +31,11 @@ export abstract class CDSBase<
Options extends CDSBaseOptionsType = CDSBaseOptionsType Options extends CDSBaseOptionsType = CDSBaseOptionsType
> { > {
protected readonly logger: LoggerType; protected readonly logger: LoggerType;
protected readonly proxyAgent?: ReturnType<typeof createProxyAgent>; protected proxyAgent?: ProxyAgent;
protected cachedAuth?: CachedAuthType; protected cachedAuth?: CachedAuthType;
constructor(protected readonly options: Options) { constructor(protected readonly options: Options) {
this.logger = options.logger; this.logger = options.logger;
if (options.proxyUrl) {
this.proxyAgent = createProxyAgent(options.proxyUrl);
}
} }
public abstract request( public abstract request(
@ -46,6 +43,11 @@ export abstract class CDSBase<
): Promise<CDSResponseType>; ): Promise<CDSResponseType>;
protected async getAuth(): Promise<CDSAuthType> { protected async getAuth(): Promise<CDSAuthType> {
// Lazily create proxy agent
if (!this.proxyAgent && this.options.proxyUrl) {
this.proxyAgent = await createProxyAgent(this.options.proxyUrl);
}
if (this.cachedAuth) { if (this.cachedAuth) {
if (isOlderThan(this.cachedAuth.timestamp, CACHED_AUTH_TTL)) { if (isOlderThan(this.cachedAuth.timestamp, CACHED_AUTH_TTL)) {
this.cachedAuth = undefined; this.cachedAuth = undefined;

View file

@ -602,7 +602,7 @@ export abstract class Updater {
this.logger.info(`downloadUpdate: Downloading signature ${signatureUrl}`); this.logger.info(`downloadUpdate: Downloading signature ${signatureUrl}`);
const signature = Buffer.from( const signature = Buffer.from(
await got(signatureUrl, getGotOptions()).text(), await got(signatureUrl, await getGotOptions()).text(),
'hex' 'hex'
); );
@ -614,7 +614,10 @@ export abstract class Updater {
this.logger.info( this.logger.info(
`downloadUpdate: Downloading blockmap ${blockMapUrl}` `downloadUpdate: Downloading blockmap ${blockMapUrl}`
); );
const blockMap = await got(blockMapUrl, getGotOptions()).buffer(); const blockMap = await got(
blockMapUrl,
await getGotOptions()
).buffer();
await writeFile(tempBlockMapPath, blockMap); await writeFile(tempBlockMapPath, blockMap);
} catch (error) { } catch (error) {
this.logger.warn( this.logger.warn(
@ -751,7 +754,7 @@ export abstract class Updater {
targetUpdatePath: string, targetUpdatePath: string,
updateOnProgress = false updateOnProgress = false
): Promise<void> { ): Promise<void> {
const downloadStream = got.stream(updateFileUrl, getGotOptions()); const downloadStream = got.stream(updateFileUrl, await getGotOptions());
const writeStream = createWriteStream(targetUpdatePath); const writeStream = createWriteStream(targetUpdatePath);
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
@ -930,7 +933,7 @@ export function parseYaml(yaml: string): JSONUpdateSchema {
async function getUpdateYaml(): Promise<string> { async function getUpdateYaml(): Promise<string> {
const targetUrl = getUpdateCheckUrl(); const targetUrl = getUpdateCheckUrl();
const body = await got(targetUrl, getGotOptions()).text(); const body = await got(targetUrl, await getGotOptions()).text();
if (!body) { if (!body) {
throw new Error('Got unexpected response back from update check'); throw new Error('Got unexpected response back from update check');

View file

@ -15,6 +15,7 @@ import { strictAssert } from '../util/assert';
import { wrapEventEmitterOnce } from '../util/wrapEventEmitterOnce'; import { wrapEventEmitterOnce } from '../util/wrapEventEmitterOnce';
import type { LoggerType } from '../types/Logging'; import type { LoggerType } from '../types/Logging';
import { getGotOptions } from './got'; import { getGotOptions } from './got';
import type { GotOptions } from './got';
import { checkIntegrity } from './util'; import { checkIntegrity } from './util';
const gunzip = promisify(nativeGunzip); const gunzip = promisify(nativeGunzip);
@ -74,7 +75,7 @@ export type DownloadOptionsType = Readonly<{
logger?: LoggerType; logger?: LoggerType;
// Testing // Testing
gotOptions?: ReturnType<typeof getGotOptions>; gotOptions?: GotOptions;
}>; }>;
export type DownloadRangesOptionsType = Readonly<{ export type DownloadRangesOptionsType = Readonly<{
@ -86,7 +87,7 @@ export type DownloadRangesOptionsType = Readonly<{
chunkStatusCallback: (chunkSize: number) => void; chunkStatusCallback: (chunkSize: number) => void;
// Testing // Testing
gotOptions?: ReturnType<typeof getGotOptions>; gotOptions?: GotOptions;
}>; }>;
export function getBlockMapFileName(fileName: string): string { export function getBlockMapFileName(fileName: string): string {
@ -212,7 +213,7 @@ export async function prepareDownload({
const newBlockMapData = await got( const newBlockMapData = await got(
getBlockMapFileName(newUrl), getBlockMapFileName(newUrl),
getGotOptions() await getGotOptions()
).buffer(); ).buffer();
const newBlockMap = await parseBlockMap(newBlockMapData); const newBlockMap = await parseBlockMap(newBlockMapData);
@ -343,7 +344,7 @@ export async function downloadRanges(
logger, logger,
abortSignal, abortSignal,
chunkStatusCallback, chunkStatusCallback,
gotOptions = getGotOptions(), gotOptions = await getGotOptions(),
} = options; } = options;
logger?.info('updater/downloadRanges: downloading ranges', ranges.length); logger?.info('updater/downloadRanges: downloading ranges', ranges.length);

View file

@ -24,13 +24,15 @@ export function getCertificateAuthority(): string {
return config.get('certificateAuthority'); return config.get('certificateAuthority');
} }
export function getGotOptions(): GotOptions { export type { GotOptions };
export async function getGotOptions(): Promise<GotOptions> {
const certificateAuthority = getCertificateAuthority(); const certificateAuthority = getCertificateAuthority();
const proxyUrl = getProxyUrl(); const proxyUrl = getProxyUrl();
const agent = proxyUrl const agent = proxyUrl
? { ? {
http: createProxyAgent(proxyUrl), http: await createProxyAgent(proxyUrl),
https: createProxyAgent(proxyUrl), https: await createProxyAgent(proxyUrl),
} }
: { : {
http: new HTTPAgent(), http: new HTTPAgent(),

View file

@ -1,8 +1,8 @@
// Copyright 2023 Signal Messenger, LLC // Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { ProxyAgent } from 'proxy-agent';
import net from 'net'; import net from 'net';
import type { ProxyAgent } from 'proxy-agent';
import { URL } from 'url'; import { URL } from 'url';
import type { LookupOptions, LookupAddress } from 'dns'; import type { LookupOptions, LookupAddress } from 'dns';
import { lookup } from 'dns/promises'; import { lookup } from 'dns/promises';
@ -25,7 +25,9 @@ const SOCKS_PROTOCOLS = new Set([
'socks5h:', 'socks5h:',
]); ]);
export function createProxyAgent(proxyUrl: string): ProxyAgent { export type { ProxyAgent };
export async function createProxyAgent(proxyUrl: string): Promise<ProxyAgent> {
const { port: portStr, hostname: proxyHost, protocol } = new URL(proxyUrl); const { port: portStr, hostname: proxyHost, protocol } = new URL(proxyUrl);
let defaultPort: number | undefined; let defaultPort: number | undefined;
if (protocol === 'http:') { if (protocol === 'http:') {
@ -96,6 +98,8 @@ export function createProxyAgent(proxyUrl: string): ProxyAgent {
} }
} }
const { ProxyAgent } = await import('proxy-agent');
return new ProxyAgent({ return new ProxyAgent({
lookup: lookup:
port !== undefined port !== undefined