Skip to content

Commit

Permalink
Add .isFromCache property to the stream API and rename the Promise …
Browse files Browse the repository at this point in the history
…API property from `.fromCache` to `.isFromCache` (#768)

Fixes #747
  • Loading branch information
szmarczak authored and sindresorhus committed Apr 10, 2019
1 parent 92b1005 commit b5e443b
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 16 deletions.
12 changes: 7 additions & 5 deletions readme.md
Expand Up @@ -110,7 +110,7 @@ Properties from `options` will override properties in the parsed `url`.

If no protocol is specified, it will throw a `TypeError`.

**Note**: this can also be an option.
**Note:** this can also be an option.

##### options

Expand Down Expand Up @@ -229,7 +229,7 @@ If set to `true` and `Content-Type` header is not set, it will be set to `applic

Type: `string` `Object<string, string|number>` [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)

**Note**: The `query` option was renamed to `searchParams` in Got v10. The `query` option name is still functional, but is being deprecated and will be completely removed in Got v11.
**Note:** The `query` option was renamed to `searchParams` in Got v10. The `query` option name is still functional, but is being deprecated and will be completely removed in Got v11.

Query string that will be added to the request URL. This will override the query string in `url`.

Expand Down Expand Up @@ -576,7 +576,7 @@ The object contains the following properties:

**Note:** The time is a `number` representing the milliseconds elapsed since the UNIX epoch.

##### fromCache
##### isFromCache

Type: `boolean`

Expand All @@ -598,6 +598,8 @@ The number of times the request was retried.

**Note:** Progress events, redirect events and request/response events can also be used with promises.

**Note:** To access `response.isFromCache` you need to use `got.stream(url, options).isFromCache`. The value will be undefined until the `response` event.

#### got.stream(url, [options])

Sets `options.stream` to `true`.
Expand Down Expand Up @@ -827,11 +829,11 @@ const map = new Map();

(async () => {
let response = await got('https://sindresorhus.com', {cache: map});
console.log(response.fromCache);
console.log(response.isFromCache);
//=> false

response = await got('https://sindresorhus.com', {cache: map});
console.log(response.fromCache);
console.log(response.isFromCache);
//=> true
})();
```
Expand Down
22 changes: 18 additions & 4 deletions source/as-stream.ts
@@ -1,13 +1,17 @@
import {PassThrough as PassThroughStream} from 'stream';
import {PassThrough as PassThroughStream, Duplex as DuplexStream} from 'stream';
import duplexer3 from 'duplexer3';
import requestAsEventEmitter from './request-as-event-emitter';
import {HTTPError, ReadError} from './errors';
import {MergedOptions, Response} from './utils/types';

export class ProxyStream extends DuplexStream {
isFromCache?: boolean;
}

export default function asStream(options: MergedOptions) {
const input = new PassThroughStream();
const output = new PassThroughStream();
const proxy = duplexer3(input, output);
const proxy = duplexer3(input, output) as ProxyStream;
const piped = new Set();
let isFinished = false;

Expand All @@ -29,7 +33,8 @@ export default function asStream(options: MergedOptions) {
};

emitter.on('response', (response: Response) => {
const {statusCode} = response;
const {statusCode, isFromCache} = response;
proxy.isFromCache = isFromCache;

response.on('error', error => {
proxy.emit('error', new ReadError(error, options));
Expand All @@ -40,7 +45,14 @@ export default function asStream(options: MergedOptions) {
return;
}

isFinished = true;
{
const read = proxy._read.bind(proxy);
proxy._read = (...args) => {
isFinished = true;

return read(...args);
};
}

response.pipe(output);

Expand Down Expand Up @@ -93,5 +105,7 @@ export default function asStream(options: MergedOptions) {
return unpipe(stream);
};

proxy.isFromCache = undefined;

return proxy;
}
2 changes: 2 additions & 0 deletions source/request-as-event-emitter.ts
Expand Up @@ -119,6 +119,8 @@ export default (options, input?: TransformStream) => {
response.request = {
gotOptions: options
};
response.isFromCache = response.fromCache || false;
delete response.fromCache;

const rawCookies = response.headers['set-cookie'];
if (options.cookieJar && rawCookies) {
Expand Down
1 change: 1 addition & 0 deletions source/utils/types.ts
Expand Up @@ -51,6 +51,7 @@ export type IterateFunction = (options: Options) => void;
export interface Response extends IncomingMessage {
body: string | Buffer;
statusCode: number;
isFromCache?: boolean;
}

export interface Timings {
Expand Down
41 changes: 41 additions & 0 deletions test/cache.ts
@@ -1,4 +1,7 @@
import test from 'ava';
import pEvent from 'p-event';
import getStream from 'get-stream';
import {Response} from '../source/utils/types';
import withServer from './helpers/with-server';

const cacheEndpoint = (_request, response) => {
Expand Down Expand Up @@ -135,3 +138,41 @@ test('DNS cache works', withServer, async (t, _server, got) => {

t.is(map.size, 1);
});

test('`isFromCache` stream property is undefined before the `response` event', withServer, async (t, server, got) => {
server.get('/', cacheEndpoint);

const cache = new Map();
const stream = got.stream({cache});
t.is(stream.isFromCache, undefined);

await getStream(stream);
});

test('`isFromCache` stream property is false after the `response` event', withServer, async (t, server, got) => {
server.get('/', cacheEndpoint);

const cache = new Map();
const stream = got.stream({cache});

const response = await pEvent(stream, 'response') as Response;
t.is(response.isFromCache, false);
t.is(stream.isFromCache, false);

await getStream(stream);
});

test('`isFromCache` stream property is true if the response was cached', withServer, async (t, server, got) => {
server.get('/', cacheEndpoint);

const cache = new Map();

await getStream(got.stream({cache}));
const stream = got.stream({cache});

const response = await pEvent(stream, 'response') as Response;
t.is(response.isFromCache, true);
t.is(stream.isFromCache, true);

await getStream(stream);
});
15 changes: 8 additions & 7 deletions test/stream.ts
@@ -1,8 +1,8 @@
import {PassThrough} from 'stream';
import test from 'ava';
import toReadableStream from 'to-readable-stream';
import getStream from 'get-stream';
import pEvent from 'p-event';
import delay from 'delay';
import is from '@sindresorhus/is';
import withServer from './helpers/with-server';

Expand Down Expand Up @@ -170,14 +170,15 @@ test('skips proxying headers after server has sent them already', withServer, as

test('throws when trying to proxy through a closed stream', withServer, async (t, server, got) => {
server.get('/', defaultHandler);
server.get('/proxy', async (_request, response) => {
const stream = got.stream('');
await delay(1000);
t.throws(() => stream.pipe(response), 'Failed to pipe. The response has been emitted already.');
response.end();

const stream = got.stream('');
const promise = getStream(stream);

stream.once('data', () => {
t.throws(() => stream.pipe(new PassThrough()), 'Failed to pipe. The response has been emitted already.');
});

await got('proxy');
await promise;
});

test('proxies `content-encoding` header when `options.decompress` is false', withServer, async (t, server, got) => {
Expand Down

0 comments on commit b5e443b

Please sign in to comment.