Browse Source

Cone stream so we can do stuff with it at different times

cache-clone-stream
Luke Childs 8 years ago
parent
commit
0edfb324d9
  1. 115
      index.js
  2. 4568
      package-lock.json
  3. 24
      test/cache.js

115
index.js

@ -37,62 +37,49 @@ function requestAsEventEmitter(opts) {
const req = fn.request(opts, res => { const req = fn.request(opts, res => {
const statusCode = res.statusCode; const statusCode = res.statusCode;
if (isRedirect(statusCode) && opts.followRedirect && 'location' in res.headers && (opts.method === 'GET' || opts.method === 'HEAD')) {
res.resume();
if (++redirectCount > 10) {
ee.emit('error', new got.MaxRedirectsError(statusCode, opts), null, res);
return;
}
const bufferString = Buffer.from(res.headers.location, 'binary').toString();
redirectUrl = urlLib.resolve(urlLib.format(opts), bufferString);
const redirectOpts = Object.assign({}, opts, urlLib.parse(redirectUrl));
ee.emit('redirect', res, redirectOpts);
get(redirectOpts);
return;
}
setImmediate(() => {
let response = typeof unzipResponse === 'function' && req.method !== 'HEAD' ? unzipResponse(res) : res;
if (revalidateCache) { if (revalidateCache) {
const cachedResponse = revalidateCache.response; const cachedResponse = revalidateCache.response;
const {policy, modified} = CachePolicy.fromObject(revalidateCache.policy).revalidatedPolicy(opts, response); const {policy, modified} = CachePolicy.fromObject(revalidateCache.policy).revalidatedPolicy(opts, res);
if (!modified) { if (!modified) {
const {statusCode, url} = response; const {statusCode, url} = res;
const headers = policy.responseHeaders(); const headers = policy.responseHeaders();
const bodyBuffer = Buffer.from(cachedResponse.body.data, cachedResponse.body.encoding); const bodyBuffer = Buffer.from(cachedResponse.body.data, cachedResponse.body.encoding);
response = new Response(statusCode, headers, bodyBuffer, url); res = new Response(statusCode, headers, bodyBuffer, url);
response.cachePolicy = policy; res.cachePolicy = policy;
} }
} }
if (typeof response.cachePolicy === 'undefined') { if (typeof res.cachePolicy === 'undefined') {
response.cachePolicy = new CachePolicy(opts, response); res.cachePolicy = new CachePolicy(opts, res);
} }
if (opts.cache && response.cachePolicy.storable()) { if (opts.cache && res.cachePolicy.storable()) {
const encoding = opts.encoding === null ? 'buffer' : opts.encoding;
getStream(response, {encoding}) const stream = res;
const pass = require('stream').PassThrough;
res = new pass;
const clonedRes = new pass;
stream.pipe(res);
stream.pipe(clonedRes);
copyProps(stream, res);
copyProps(stream, clonedRes);
const encoding = 'buffer';
getStream(clonedRes, {encoding})
.then(body => { .then(body => {
response.body = body; res.body = body;
const key = cacheKey(opts); const key = cacheKey(opts);
const value = { const value = {
policy: response.cachePolicy.toObject(), policy: res.cachePolicy.toObject(),
response: { response: {
url: response.url, url: res.url,
statusCode: response.statusCode, statusCode: res.statusCode,
body: { body: {
encoding: opts.encoding, encoding: opts.encoding,
data: response.body data: res.body
} }
} }
}; };
const ttl = response.cachePolicy.timeToLive(); const ttl = res.cachePolicy.timeToLive();
opts.cache.set(key, value, ttl); opts.cache.set(key, value, ttl);
}); });
} else if (revalidateCache) { } else if (revalidateCache) {
@ -100,6 +87,29 @@ function requestAsEventEmitter(opts) {
opts.cache.delete(key); opts.cache.delete(key);
} }
if (isRedirect(statusCode) && opts.followRedirect && 'location' in res.headers && (opts.method === 'GET' || opts.method === 'HEAD')) {
res.resume();
if (++redirectCount > 10) {
ee.emit('error', new got.MaxRedirectsError(statusCode, opts), null, res);
return;
}
const bufferString = Buffer.from(res.headers.location, 'binary').toString();
redirectUrl = urlLib.resolve(urlLib.format(opts), bufferString);
const redirectOpts = Object.assign({}, opts, urlLib.parse(redirectUrl));
ee.emit('redirect', res, redirectOpts);
get(redirectOpts);
return;
}
setImmediate(() => {
let response = typeof unzipResponse === 'function' && req.method !== 'HEAD' ? unzipResponse(res) : res;
response.url = redirectUrl || requestUrl; response.url = redirectUrl || requestUrl;
response.requestUrl = requestUrl; response.requestUrl = requestUrl;
response.fromCache = false; response.fromCache = false;
@ -444,3 +454,32 @@ got.MaxRedirectsError = createErrorClass('MaxRedirectsError', function (statusCo
}); });
module.exports = got; module.exports = got;
const knownProps = [
'destroy',
'setTimeout',
'socket',
'headers',
'trailers',
'rawHeaders',
'statusCode',
'httpVersion',
'httpVersionMinor',
'httpVersionMajor',
'rawTrailers',
'statusMessage'
];
const copyProps = (fromStream, toStream) => {
const toProps = Object.keys(toStream);
const fromProps = new Set(Object.keys(fromStream).concat(knownProps));
for (const prop of fromProps) {
// Don't overwrite existing properties
if (toProps.indexOf(prop) !== -1) {
continue;
}
toStream[prop] = typeof prop === 'function' ? fromStream[prop].bind(fromStream) : fromStream[prop];
}
};

4568
package-lock.json

File diff suppressed because it is too large

24
test/cache.js

@ -56,6 +56,18 @@ test.before('setup', async () => {
res.end('cache-then-no-store-on-revalidate'); res.end('cache-then-no-store-on-revalidate');
}); });
s.on('/redirect-dest', (req, res) => {
res.end('reached');
});
s.on('/redirect', (req, res) => {
res.setHeader('Cache-Control', 'public, max-age=60');
res.writeHead(301, {
location: `${s.url}/redirect-dest`
});
res.end();
});
await s.listen(s.port); await s.listen(s.port);
}); });
@ -192,6 +204,18 @@ test('Response objects have fromCache property set correctly', async t => {
t.true(cachedResponse.fromCache); t.true(cachedResponse.fromCache);
}); });
test('Cached redirects work correctly', async t => {
const endpoint = '/redirect';
const cache = new Map();
const response = await got(s.url + endpoint, {cache});
// const cachedResponse = await got(s.url + endpoint, {cache});
console.log(cache);
t.is(response.body, 'reached');
});
test.after('cleanup', async () => { test.after('cleanup', async () => {
await s.close(); await s.close();
}); });

Loading…
Cancel
Save