@zeit/fetch-cached-dns
Advanced tools
Comparing version 1.0.1 to 1.0.2
@@ -0,1 +1,8 @@ | ||
1.0.2 / 2017-11-16 | ||
================== | ||
* fix redirects to a different hostname | ||
* use "@zeit/eslint-config-node" for eslint | ||
1.0.0 / 2017-10-19 | ||
@@ -2,0 +9,0 @@ ================== |
60
index.js
@@ -1,34 +0,58 @@ | ||
const { isIP } = require('net'); | ||
const { format, parse } = require('url'); | ||
const resolve = require('@zeit/dns-cached-resolve'); | ||
const { isIP } = require('net') | ||
const { format, parse } = require('url') | ||
const resolve = require('@zeit/dns-cached-resolve') | ||
module.exports = setup; | ||
module.exports = setup | ||
function setup (fetch) { | ||
const isRedirect = v => ((v / 100) | 0) === 3 | ||
function setup(fetch) { | ||
if (!fetch) { | ||
fetch = require('node-fetch'); | ||
fetch = require('node-fetch') | ||
} | ||
const { Headers } = fetch | ||
async function fetchCachedDns(url, opts) { | ||
const parsed = parse(url); | ||
const ip = isIP(parsed.hostname); | ||
const parsed = parse(url) | ||
const ip = isIP(parsed.hostname) | ||
if (ip === 0) { | ||
if (!opts) opts = {}; | ||
if (!opts.headers) opts.headers = {}; | ||
if (!opts.headers.Host) { | ||
opts.headers.Host = parsed.host; | ||
if (!opts) opts = {} | ||
if (!opts.headers) opts.headers = new Headers() | ||
if (!opts.headers.has('Host')) { | ||
opts.headers.set('Host', parsed.host) | ||
} | ||
parsed.hostname = await resolve(parsed.hostname); | ||
url = format(parsed); | ||
opts.redirect = 'manual' | ||
parsed.hostname = await resolve(parsed.hostname) | ||
url = format(parsed) | ||
} | ||
return fetch(url, opts); | ||
const res = await fetch(url, opts) | ||
if (isRedirect(res.status)) { | ||
const redirectOpts = Object.assign({}, opts) | ||
if (!redirectOpts.headers) redirectOpts.headers = new Headers() | ||
// per fetch spec, for POST request with 301/302 response, or any request with 303 response, use GET when following redirect | ||
if ( | ||
res.status === 303 || | ||
((res.status === 301 || res.status === 302) && opts.method === 'POST') | ||
) { | ||
redirectOpts.method = 'GET' | ||
redirectOpts.body = null | ||
redirectOpts.headers.delete('content-length') | ||
} | ||
const location = res.headers.get('Location') | ||
redirectOpts.headers.delete('Host') | ||
return fetchCachedDns(location, redirectOpts) | ||
} else { | ||
return res | ||
} | ||
} | ||
for (const key of Object.keys(fetch)) { | ||
fetchCachedDns[key] = fetch[key]; | ||
fetchCachedDns[key] = fetch[key] | ||
} | ||
fetchCachedDns.default = fetchCachedDns; | ||
fetchCachedDns.default = fetchCachedDns | ||
return fetchCachedDns; | ||
return fetchCachedDns | ||
} |
{ | ||
"name": "@zeit/fetch-cached-dns", | ||
"version": "1.0.1", | ||
"version": "1.0.2", | ||
"description": "A decorator on top of `fetch` that caches the DNS query of the `hostname` of the passed URL", | ||
@@ -25,21 +25,3 @@ "scripts": { | ||
"eslintConfig": { | ||
"parserOptions": { | ||
"ecmaVersion": 8, | ||
"sourceType": "script" | ||
}, | ||
"extends": [ | ||
"eslint:recommended" | ||
], | ||
"env": { | ||
"es6": true, | ||
"node": true | ||
}, | ||
"rules": { | ||
"func-names": [ | ||
"error", | ||
"as-needed" | ||
], | ||
"no-shadow": "error", | ||
"no-extra-semi": 0 | ||
} | ||
"extends": "@zeit/eslint-config-node" | ||
}, | ||
@@ -57,6 +39,8 @@ "bugs": { | ||
"devDependencies": { | ||
"@zeit/eslint-config-node": "0.1.0", | ||
"async-listen": "1.0.0", | ||
"eslint": "4.10.0", | ||
"jest": "21.2.1", | ||
"lint-staged": "4.3.0", | ||
"node-fetch": "^1.7.3", | ||
"node-fetch": "1.7.3", | ||
"pre-commit": "1.2.2", | ||
@@ -63,0 +47,0 @@ "prettier": "1.7.4" |
55
test.js
/* eslint-env jest*/ | ||
const listen = require('async-listen') | ||
const { createServer } = require('http') | ||
const cachedDNSFetch = require('./index')() | ||
const cachedDNSFetch = require('./index')(require('node-fetch')) | ||
test('works with localhost', async () => { | ||
/** | ||
* Using `localtest.me` to use DNS to resolve to localhost | ||
* http://readme.localtest.me/ | ||
*/ | ||
test('works with localtest.me', async () => { | ||
const server = createServer((req, res) => { | ||
@@ -10,16 +16,35 @@ res.end('ha') | ||
return new Promise((resolve, reject) => { | ||
server.listen(async () => { | ||
const { port } = server.address() | ||
try { | ||
const res = await cachedDNSFetch(`http://127.0.0.1:${port}`) | ||
expect(await res.text()).toBe('ha') | ||
server.close() | ||
resolve() | ||
} catch (err) { | ||
reject(err) | ||
} | ||
}) | ||
server.on('error', reject) | ||
await listen(server) | ||
const { port } = server.address() | ||
const res = await cachedDNSFetch(`http://localtest.me:${port}`) | ||
expect(await res.text()).toBe('ha') | ||
server.close() | ||
}) | ||
test('works with redirects', async () => { | ||
let portA | ||
let portB | ||
const serverA = createServer((req, res) => { | ||
res.setHeader('Location', `http://localtest.me:${portB}`) | ||
res.statusCode = 302 | ||
res.end() | ||
}) | ||
const serverB = createServer((req, res) => { | ||
// ensure the Host header is properly re-written upon redirect | ||
res.end(req.headers.host) | ||
}) | ||
await listen(serverA) | ||
await listen(serverB) | ||
portA = serverA.address().port | ||
portB = serverB.address().port | ||
const res = await cachedDNSFetch(`http://localtest.me:${portA}`) | ||
expect(await res.status).toBe(200) | ||
expect(await res.text()).toBe(`localtest.me:${portB}`) | ||
serverA.close() | ||
serverB.close() | ||
}) |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
113356
89
8