Skip to content

Instantly share code, notes, and snippets.

@i-like-robots
Created March 17, 2019 11:47
Show Gist options
  • Select an option

  • Save i-like-robots/8913dad2204f95f0d4044b338092a584 to your computer and use it in GitHub Desktop.

Select an option

Save i-like-robots/8913dad2204f95f0d4044b338092a584 to your computer and use it in GitHub Desktop.

Revisions

  1. i-like-robots created this gist Mar 17, 2019.
    12 changes: 12 additions & 0 deletions fetch.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    const nodeFetch = require('node-fetch');
    const httpsAgent = require('./httpsAgent');
    const handleResponse = require('./handleResponse');

    module.exports = async (url, options = {}) => {
    const response = await nodeFetch(url, {
    ...options,
    agent: httpsAgent
    });

    return handleResponse(response);
    };
    13 changes: 13 additions & 0 deletions handleResponse.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    module.exports = (response) => {
    if (response.ok) {
    const contentType = response.headers.get('content-type');

    if (contentType && contentType.includes('application/json')) {
    return response.json();
    } else {
    return response.text();
    }
    } else {
    throw Error(`Request to ${response.url} responded with a ${response.status}`);
    }
    };
    2 changes: 2 additions & 0 deletions httpsAgent.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2 @@
    const https = require('https');
    module.exports = new https.Agent({ keepAlive: true });
    28 changes: 28 additions & 0 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    const aws4 = require('aws4');
    const { URL } = require('url');
    const fetch = require('./fetch');
    const resolveCname = require('./resolveCname');

    module.exports = async (url, options = {}, credentials = {}) => {
    const parsedURL = new URL(url);

    // Ensure we sign the actual service URL and not a load balancer URL
    const hostname = await resolveCname(parsedURL.host);

    const signedOptions = {
    method: options.method,
    host: hostname,
    path: parsedURL.pathname + parsedURL.search,
    body: options.body,
    headers: options.headers
    };

    aws4.sign(signedOptions, {
    accessKeyId: credentials.awsAccessKey,
    secretAccessKey: credentials.awsSecretAccessKey
    });

    options.headers = signedOptions.headers;

    return fetch(`https://${signedOptions.host}${signedOptions.path}`, options);
    };
    19 changes: 19 additions & 0 deletions package.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    {
    "name": "signed-aws-fetch",
    "version": "1.0.0",
    "description": "",
    "engines": {
    "node": ">= 8.0.0"
    },
    "main": "index.js",
    "dependencies": {
    "aws4": "^1.8.0",
    "lru-cache": "^5.1.0",
    "node-fetch": "^2.3.0"
    },
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "Matt Hinchliffe",
    "license": "ISC"
    }
    25 changes: 25 additions & 0 deletions resolveCname.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    const dns = require('dns');
    const util = require('util');
    const LRU = require('lru-cache');

    const cache = new LRU({ maxAge: 1000 * 60, stale: true });
    const resolveCname = util.promisify(dns.resolveCname);

    module.exports = async (domain) => {
    if (/\.amazonaws\.com$/.test(domain)) {
    return domain;
    }

    if (cache.has(domain)) {
    return cache.get(domain);
    }

    const [ resolvedName ] = await resolveCname(domain);

    if (resolvedName) {
    cache.set(domain, resolvedName);
    return resolvedName;
    } else {
    throw Error(`Could not resolve CNAME for ${domain}`);
    }
    };