-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added logic to auto detect system level proxy (#1665)
* Added logic to auto detect system level proxy * Fix import * Added tests + some refactoring * Fix coverage * Fix coverage * Add test for coverage * break into smaller functions * Adressed comments
- Loading branch information
1 parent
e6b3ec4
commit ed3868d
Showing
7 changed files
with
334 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { exec } from 'node:child_process'; | ||
import { promisify } from 'node:util'; | ||
import logger from '@percy/logger'; | ||
|
||
export default class DetectProxy { | ||
constructor() { | ||
this.execPromise = promisify(exec); | ||
this.platform = process.platform; | ||
// There are sock proxies as well which we don't need | ||
this.filter = ['HTTP', 'HTTPS']; | ||
} | ||
|
||
async getSystemProxy() { | ||
if (this.platform === 'darwin') { | ||
return await this.getProxyFromMac(); | ||
} else if (this.platform === 'win32') { | ||
return await this.getProxyFromWindows(); | ||
} else if (this.platform !== 'linux') { | ||
logger('client:detect-proxy').debug(`Not able to auto detect system proxy for ${this.platform} platform`); | ||
} | ||
return []; | ||
} | ||
|
||
async getProxyFromMac() { | ||
// Sample output | ||
/* | ||
HTTPEnable : 1 | ||
HTTPProxy : proxy.example.com | ||
HTTPPort : 8080 | ||
HTTPSEnable : 1 | ||
HTTPSProxy : secureproxy.example.com | ||
HTTPSPort : 8443 | ||
*/ | ||
const { stdout } = await this.execPromise('scutil --proxy'); | ||
const dictionary = {}; | ||
const lines = stdout.split('\n'); | ||
lines.forEach(line => { | ||
let [key, value] = line.split(' : '); | ||
if (key && value) { | ||
key = key.trim(); | ||
value = value.trim(); | ||
if (key.endsWith('Enable')) { | ||
dictionary[key] = value === '1'; | ||
} else if (key.endsWith('Port')) { | ||
dictionary[key] = parseInt(value); | ||
} else { | ||
dictionary[key] = value; | ||
} | ||
} | ||
}); | ||
const proxies = []; | ||
for (const type of this.filter) { | ||
if (dictionary[`${type}Enable`] && dictionary[`${type}Proxy`] && dictionary[`${type}Port`]) { | ||
proxies.push({ type: type, host: dictionary[`${type}Proxy`], port: dictionary[`${type}Port`] }); | ||
} | ||
} | ||
return proxies; | ||
} | ||
|
||
async getProxyFromWindows() { | ||
// Sample output | ||
/* | ||
HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings | ||
User Agent REG_SZ Mozilla/4.0 (compatible; MSIE 8.0; Win32) | ||
IE5_UA_Backup_Flag REG_SZ 5.0 | ||
ZonesSecurityUpgrade REG_BINARY ABCD | ||
EmailName REG_SZ User@ | ||
AutoConfigProxy REG_SZ wininet.dll | ||
MimeExclusionListForCache REG_SZ multipart/mixed multipart/x-mixed-replace multipart/x-byteranges | ||
WarnOnPost REG_BINARY 01000000 | ||
UseSchannelDirectly REG_BINARY 01000000 | ||
EnableHttp1_1 REG_DWORD 0x1 | ||
UrlEncoding REG_DWORD 0x0 | ||
SecureProtocols REG_DWORD 0xa0 | ||
PrivacyAdvanced REG_DWORD 0x0 | ||
DisableCachingOfSSLPages REG_DWORD 0x1 | ||
WarnonZoneCrossing REG_DWORD 0x1 | ||
CertificateRevocation REG_DWORD 0x1 | ||
EnableNegotiate REG_DWORD 0x1 | ||
MigrateProxy REG_DWORD 0x1 | ||
ProxyEnable REG_DWORD 0x0 | ||
*/ | ||
const { stdout } = await this.execPromise( | ||
'reg query "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"' | ||
); | ||
const lines = stdout.split('\n'); | ||
const dictionary = {}; | ||
lines.forEach(line => { | ||
const [key, type, value] = line.trim().split(/\s+/); | ||
if (key && type && value) { | ||
if (type === 'REG_DWORD') { | ||
dictionary[key] = value === '0x1'; | ||
} else if (type === 'REG_SZ') { | ||
dictionary[key] = value; | ||
} | ||
} | ||
}); | ||
if (this.filter.includes('HTTP') && dictionary.ProxyEnable && dictionary.ProxyServer) { | ||
const [host, port] = dictionary.ProxyServer.split(':'); | ||
return [{ type: 'HTTP', host, port: parseInt(port) }]; | ||
} | ||
return []; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
|
||
import DetectProxy from '../../src/detect-proxy.js'; | ||
import logger from '@percy/logger/test/helpers'; | ||
|
||
const detectProxy = new DetectProxy(); | ||
|
||
describe('getSystemProxy', () => { | ||
let mockExecPromise; | ||
|
||
beforeAll(async () => { | ||
mockExecPromise = spyOn(detectProxy, 'execPromise'); | ||
await logger.mock({ level: 'debug' }); | ||
}); | ||
|
||
describe('on macOS', () => { | ||
beforeAll(() => { | ||
detectProxy.platform = 'darwin'; | ||
}); | ||
|
||
it('should return proxies if they are enabled and present', async () => { | ||
const mockOutput = ` | ||
HTTPEnable : 1 | ||
HTTPProxy : proxy.example.com | ||
HTTPPort : 8080 | ||
HTTPSEnable : 1 | ||
HTTPSProxy : secureproxy.example.com | ||
HTTPSPort : 8443 | ||
`; | ||
mockExecPromise.and.returnValue(Promise.resolve({ stdout: mockOutput })); | ||
|
||
const proxies = await detectProxy.getSystemProxy(); | ||
expect(proxies).toEqual([ | ||
{ type: 'HTTP', host: 'proxy.example.com', port: 8080 }, | ||
{ type: 'HTTPS', host: 'secureproxy.example.com', port: 8443 } | ||
]); | ||
}); | ||
|
||
it('should return an empty array if proxies are not enabled', async () => { | ||
const mockOutput = ` | ||
HTTPEnable : 0 | ||
HTTPProxy : proxy.example.com | ||
HTTPPort : 8080 | ||
`; | ||
mockExecPromise.and.returnValue(Promise.resolve({ stdout: mockOutput })); | ||
|
||
const proxies = await detectProxy.getSystemProxy(); | ||
expect(proxies).toEqual([]); | ||
}); | ||
|
||
it('should return an empty array empty response', async () => { | ||
const mockOutput = ''; | ||
mockExecPromise.and.returnValue(Promise.resolve({ stdout: mockOutput })); | ||
|
||
const proxies = await detectProxy.getSystemProxy(); | ||
expect(proxies).toEqual([]); | ||
}); | ||
}); | ||
|
||
describe('on Windows', () => { | ||
beforeAll(() => { | ||
detectProxy.platform = 'win32'; | ||
}); | ||
|
||
it('should return proxy if it is enabled and present', async () => { | ||
const mockOutput = ` | ||
ProxyEnable REG_DWORD 0x1 | ||
ProxyServer REG_SZ proxy.example.com:8080 | ||
`; | ||
mockExecPromise.and.returnValue(Promise.resolve({ stdout: mockOutput })); | ||
|
||
const proxy = await detectProxy.getSystemProxy(); | ||
expect(proxy).toEqual([{ | ||
type: 'HTTP', | ||
host: 'proxy.example.com', | ||
port: 8080 | ||
}]); | ||
}); | ||
|
||
it('should return undefined if proxy is not enabled', async () => { | ||
const mockOutput = ` | ||
HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings | ||
User Agent REG_SZ Mozilla/4.0 (compatible; MSIE 8.0; Win32) | ||
IE5_UA_Backup_Flag REG_SZ 5.0 | ||
ZonesSecurityUpgrade REG_BINARY ABCD | ||
EmailName REG_SZ User@ | ||
AutoConfigProxy REG_SZ wininet.dll | ||
MimeExclusionListForCache REG_SZ multipart/mixed multipart/x-mixed-replace multipart/x-byteranges | ||
WarnOnPost REG_BINARY 01000000 | ||
UseSchannelDirectly REG_BINARY 01000000 | ||
EnableHttp1_1 REG_DWORD 0x1 | ||
UrlEncoding REG_DWORD 0x0 | ||
SecureProtocols REG_DWORD 0xa0 | ||
PrivacyAdvanced REG_DWORD 0x0 | ||
DisableCachingOfSSLPages REG_DWORD 0x1 | ||
WarnonZoneCrossing REG_DWORD 0x1 | ||
CertificateRevocation REG_DWORD 0x1 | ||
EnableNegotiate REG_DWORD 0x1 | ||
MigrateProxy REG_DWORD 0x1 | ||
ProxyEnable REG_DWORD 0x0 | ||
`; | ||
mockExecPromise.and.returnValue(Promise.resolve({ stdout: mockOutput })); | ||
|
||
const proxy = await detectProxy.getSystemProxy(); | ||
expect(proxy).toEqual([]); | ||
}); | ||
}); | ||
|
||
describe('on linux platforms', () => { | ||
beforeAll(() => { | ||
detectProxy.platform = 'linux'; | ||
}); | ||
|
||
it('should log a debug message and return empty array', async () => { | ||
const proxy = await detectProxy.getSystemProxy(); | ||
expect(proxy).toEqual([]); | ||
expect(logger.stderr).toEqual([]); | ||
}); | ||
}); | ||
|
||
describe('on unsupported platforms', () => { | ||
beforeAll(() => { | ||
detectProxy.platform = 'aix'; | ||
}); | ||
|
||
it('should log a debug message and return empty array', async () => { | ||
const proxy = await detectProxy.getSystemProxy(); | ||
expect(proxy).toEqual([]); | ||
expect(logger.stderr).toEqual([ | ||
'[percy:client:detect-proxy] Not able to auto detect system proxy for aix platform' | ||
]); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters