Socket
Socket
Sign inDemoInstall

@npmcli/agent

Package Overview
Dependencies
Maintainers
5
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@npmcli/agent - npm Package Compare versions

Comparing version 2.0.0 to 2.1.0

lib/agents.js

68

lib/dns.js

@@ -6,39 +6,36 @@ 'use strict'

const defaultOptions = exports.defaultOptions = {
family: undefined,
hints: dns.ADDRCONFIG,
all: false,
verbatim: undefined,
}
// this is a factory so that each request can have its own opts (i.e. ttl)
// while still sharing the cache across all requests
const cache = new LRUCache({ max: 50 })
const lookupCache = exports.lookupCache = new LRUCache({ max: 50 })
const getOptions = ({
family = 0,
hints = dns.ADDRCONFIG,
all = false,
verbatim = undefined,
ttl = 5 * 60 * 1000,
lookup = dns.lookup,
}) => ({
// hints and lookup are returned since both are top level properties to (net|tls).connect
hints,
lookup: (hostname, ...args) => {
const callback = args.pop() // callback is always last arg
const lookupOptions = args[0] ?? {}
// this is a factory so that each request can have its own opts (i.e. ttl)
// while still sharing the cache across all requests
exports.getLookup = (dnsOptions) => {
return (hostname, options, callback) => {
if (typeof options === 'function') {
callback = options
options = null
} else if (typeof options === 'number') {
options = { family: options }
const options = {
family,
hints,
all,
verbatim,
...(typeof lookupOptions === 'number' ? { family: lookupOptions } : lookupOptions),
}
options = { ...defaultOptions, ...options }
const key = JSON.stringify({ hostname, ...options })
const key = JSON.stringify({
hostname,
family: options.family,
hints: options.hints,
all: options.all,
verbatim: options.verbatim,
})
if (lookupCache.has(key)) {
const [address, family] = lookupCache.get(key)
process.nextTick(callback, null, address, family)
return
if (cache.has(key)) {
const cached = cache.get(key)
return process.nextTick(callback, null, ...cached)
}
dnsOptions.lookup(hostname, options, (err, address, family) => {
lookup(hostname, options, (err, ...result) => {
if (err) {

@@ -48,6 +45,11 @@ return callback(err)

lookupCache.set(key, [address, family], { ttl: dnsOptions.ttl })
return callback(null, address, family)
cache.set(key, result, { ttl })
return callback(null, ...result)
})
}
},
})
module.exports = {
cache,
getOptions,
}
'use strict'
const { appendPort } = require('./util')
class InvalidProxyProtocolError extends Error {

@@ -11,13 +13,5 @@ constructor (url) {

class InvalidProxyResponseError extends Error {
constructor (url, status) {
super(`Invalid status code \`${status}\` connecting to proxy \`${url.host}\``)
this.code = 'EINVALIDRESPONSE'
this.proxy = url
this.status = status
}
}
class ConnectionTimeoutError extends Error {
constructor (host) {
constructor ({ host, port }) {
host = appendPort(host, port)
super(`Timeout connecting to host \`${host}\``)

@@ -30,3 +24,4 @@ this.code = 'ECONNECTIONTIMEOUT'

class IdleTimeoutError extends Error {
constructor (host) {
constructor ({ host, port }) {
host = appendPort(host, port)
super(`Idle timeout reached for host \`${host}\``)

@@ -39,6 +34,6 @@ this.code = 'EIDLETIMEOUT'

class ResponseTimeoutError extends Error {
constructor (proxy, request) {
constructor (request, proxy) {
let msg = 'Response timeout '
if (proxy.url) {
msg += `from proxy \`${proxy.url.host}\` `
if (proxy) {
msg += `from proxy \`${proxy.host}\` `
}

@@ -48,3 +43,3 @@ msg += `connecting to host \`${request.host}\``

this.code = 'ERESPONSETIMEOUT'
this.proxy = proxy.url
this.proxy = proxy
this.request = request

@@ -55,6 +50,6 @@ }

class TransferTimeoutError extends Error {
constructor (proxy, request) {
constructor (request, proxy) {
let msg = 'Transfer timeout '
if (proxy.url) {
msg += `from proxy \`${proxy.url.host}\` `
if (proxy) {
msg += `from proxy \`${proxy.host}\` `
}

@@ -64,3 +59,3 @@ msg += `for \`${request.host}\``

this.code = 'ETRANSFERTIMEOUT'
this.proxy = proxy.url
this.proxy = proxy
this.request = request

@@ -72,3 +67,2 @@ }

InvalidProxyProtocolError,
InvalidProxyResponseError,
ConnectionTimeoutError,

@@ -75,0 +69,0 @@ IdleTimeoutError,

'use strict'
const { normalizeOptions } = require('./util.js')
const HttpAgent = require('./http.js')
const HttpsAgent = require('./https.js')
const { LRUCache } = require('lru-cache')
const { urlify, cacheAgent } = require('./util')
const { normalizeOptions, cacheOptions } = require('./options')
const { getProxy, proxyCache } = require('./proxy.js')
const dns = require('./dns.js')
const { HttpAgent, HttpsAgent } = require('./agents.js')
const AgentCache = new Map()
const agentCache = new LRUCache({ max: 20 })
const proxyEnv = {}
for (const [key, value] of Object.entries(process.env)) {
const lowerKey = key.toLowerCase()
if (['https_proxy', 'http_proxy', 'proxy', 'no_proxy'].includes(lowerKey)) {
proxyEnv[lowerKey] = value
}
}
const getAgent = (url, options) => {
url = new URL(url)
options = normalizeOptions(options)
const getAgent = (url, { agent: _agent, proxy: _proxy, noProxy, ..._options } = {}) => {
// false has meaning so this can't be a simple truthiness check
if (options.agent != null) {
return options.agent
if (_agent != null) {
return _agent
}
const isHttps = url.protocol === 'https:'
url = urlify(url)
let proxy = options.proxy
if (!proxy) {
proxy = isHttps
? proxyEnv.https_proxy
: (proxyEnv.https_proxy || proxyEnv.http_proxy || proxyEnv.proxy)
}
const secure = url.protocol === 'https:'
const proxy = getProxy(url, { proxy: _proxy, noProxy })
const options = { ...normalizeOptions(_options), proxy }
if (proxy) {
proxy = new URL(proxy)
let noProxy = options.noProxy || proxyEnv.no_proxy
if (typeof noProxy === 'string') {
noProxy = noProxy.split(',').map((p) => p.trim())
}
if (noProxy) {
const hostSegments = url.hostname.split('.').reverse()
const matches = noProxy.some((no) => {
const noSegments = no.split('.').filter(Boolean).reverse()
if (!noSegments.length) {
return false
}
for (let i = 0; i < noSegments.length; ++i) {
if (hostSegments[i] !== noSegments[i]) {
return false
}
}
return true
})
if (matches) {
proxy = ''
}
}
}
const timeouts = [
options.timeouts.connection || 0,
options.timeouts.idle || 0,
options.timeouts.response || 0,
options.timeouts.transfer || 0,
].join('.')
const maxSockets = options.maxSockets || 15
let proxyDescriptor = 'proxy:'
if (!proxy) {
proxyDescriptor += 'null'
} else {
proxyDescriptor += `${proxy.protocol}//`
let auth = ''
if (proxy.username) {
auth += proxy.username
}
if (proxy.password) {
auth += `:${proxy.password}`
}
if (auth) {
proxyDescriptor += `${auth}@`
}
proxyDescriptor += proxy.host
}
const key = [
`https:${isHttps}`,
proxyDescriptor,
`local-address:${options.localAddress || 'null'}`,
`strict-ssl:${isHttps ? options.rejectUnauthorized : 'false'}`,
`ca:${isHttps && options.ca || 'null'}`,
`cert:${isHttps && options.cert || 'null'}`,
`key:${isHttps && options.key || 'null'}`,
`timeouts:${timeouts}`,
`maxSockets:${maxSockets}`,
].join(':')
if (AgentCache.has(key)) {
return AgentCache.get(key)
}
const agentOptions = {
ca: options.ca,
cert: options.cert,
key: options.key,
rejectUnauthorized: options.rejectUnauthorized,
maxSockets,
timeouts: options.timeouts,
localAddress: options.localAddress,
proxy,
}
const agent = isHttps
? new HttpsAgent(agentOptions)
: new HttpAgent(agentOptions)
AgentCache.set(key, agent)
return agent
return cacheAgent({
key: cacheOptions({ ...options, secure }),
cache: agentCache,
secure,
proxies: [HttpAgent, HttpsAgent],
}, options)
}

@@ -135,2 +36,12 @@

HttpsAgent,
cache: {
proxy: proxyCache,
agent: agentCache,
dns: dns.cache,
clear: () => {
proxyCache.clear()
agentCache.clear()
dns.cache.clear()
},
},
}
'use strict'
const dns = require('dns')
const timers = require('timers/promises')
const normalizeOptions = (_options) => {
const options = { ..._options }
const createKey = (obj) => {
let key = ''
const sorted = Object.entries(obj).sort((a, b) => a[0] - b[0])
for (let [k, v] of sorted) {
if (v == null) {
v = 'null'
} else if (v instanceof URL) {
v = v.toString()
} else if (typeof v === 'object') {
v = createKey(v)
}
key += `${k}:${v}:`
}
return key
}
if (typeof options.keepAlive === 'undefined') {
options.keepAlive = true
const createTimeout = (delay, signal) => {
if (!delay) {
return signal ? new Promise(() => {}) : null
}
if (!options.timeouts) {
options.timeouts = {}
if (!signal) {
let timeout
return {
start: (cb) => (timeout = setTimeout(cb, delay)),
clear: () => clearTimeout(timeout),
}
}
if (options.timeout) {
options.timeouts.idle = options.timeout
delete options.timeout
return timers.setTimeout(delay, null, signal)
.then(() => {
throw new Error()
}).catch((err) => {
if (err.name === 'AbortError') {
return
}
throw err
})
}
const abortRace = async (promises, ac = new AbortController()) => {
let res
try {
res = await Promise.race(promises.map((p) => p(ac)))
ac.abort()
} catch (err) {
ac.abort()
throw err
}
return res
}
options.family = !isNaN(+options.family) ? +options.family : 0
options.dns = {
ttl: 5 * 60 * 1000,
lookup: dns.lookup,
...options.dns,
const urlify = (url) => typeof url === 'string' ? new URL(url) : url
const appendPort = (host, port) => {
// istanbul ignore next
if (port) {
host += `:${port}`
}
return host
}
return options
const cacheAgent = ({ key, cache, secure, proxies }, ...args) => {
if (cache.has(key)) {
return cache.get(key)
}
const Ctor = (secure ? proxies[1] : proxies[0]) ?? proxies[0]
const agent = new Ctor(...args)
cache.set(key, agent)
return agent
}
module.exports = {
normalizeOptions,
createKey,
createTimeout,
abortRace,
urlify,
cacheAgent,
appendPort,
}
{
"name": "@npmcli/agent",
"version": "2.0.0",
"version": "2.1.0",
"description": "the http/https agent used by the npm cli",

@@ -40,2 +40,8 @@ "main": "lib/index.js",

},
"dependencies": {
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.1",
"lru-cache": "^10.0.1",
"socks-proxy-agent": "^8.0.1"
},
"devDependencies": {

@@ -58,7 +64,3 @@ "@npmcli/eslint-config": "^4.0.0",

]
},
"dependencies": {
"lru-cache": "^10.0.1",
"socks": "^2.7.1"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc