Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Browser support #2

Merged
merged 8 commits into from
Feb 20, 2019
Merged

Browser support #2

merged 8 commits into from
Feb 20, 2019

Conversation

bergundy
Copy link

There's some duplication in the client template ATM, I'll handle it later.

@bergundy bergundy requested a review from vogre February 15, 2019 17:08
Copy link

@vogre vogre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

This requires a major version bump?

Missing features:

  • unknown performance (so I'm a bit wary of node-fetch)
  • alternative fetch implementation in constructor
  • need to extend the AbortError on timeouts to provide info on which method timed out

body: JSON.stringify(body),
method: 'POST',
});
const isJSON = (response.headers.get('content-type') || '').startsWith('application/json');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I actually prefer this explicit error handling instead of the "simple" request-promise-native.

{{/throws}}
throw new InternalServerError(body.message);
} else if (!isJSON) {
throw new Error(`${response.status} - ${response.statusText}`);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have partial body if available (e.g. slice(0,256))? Also headers might tell us the server/load-balancer the error response came from.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add this in a separate commit #4

| 'window'
> {
headers?: Record<string, string>;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can options include a fetch implementation, with node-fetch being the default, so it's possible to pass a wrapped fetch for tracing similar to https://github.com/openzipkin/zipkin-js/blob/master/packages/zipkin-instrumentation-fetch/src/wrapFetch.js ?

// tslint:disable
import fetch from 'node-fetch';
import { RequestInit } from 'node-fetch';
import AbortController from 'abort-controller';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not implemented on browser?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet, #3


if (mergedOptions.timeoutMs) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), mergedOptions.timeoutMs);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this timeout is much more crude than https://nodejs.org/api/http.html#http_request_settimeout_timeout_callback - it's a global timeout and not timeout between consecutive bytes received.

This might not matter for small messages, but highlights the lack of control of the low level request when using fetch API.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is usually the expected behavior.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both behaviors are interesting (and the interval between bytes is used by Node.js and NGINX), but I think it might be enough for now.

@@ -0,0 +1,124 @@
// tslint:disable
import fetch from 'node-fetch';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see any benchmark comparisons with request (and request-promise)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed F2F transition to fetch should not slow us down.

import { TestClient } from './client';

export default async function test(client: TestClient) {
await expect(client.bar('yay', { timeoutMs: 100 })).to.eventually.be.rejectedWith(Error, 'The user aborted a request.');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a confusing error, can we catch the AbortError and throw a TimeoutError with details "call to bar timed out"?

reject();
});
webpack.stdout.on('data', (buff: Buffer) => {
const data = buff.toString();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch.. there is no nicer API for this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a comment that this is ugly

ValidationError,
};

export interface Options extends Pick<RequestInit, 'agent' | 'redirect' | 'follow' | 'compress'> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we shouldn't expose node-fetch options at all, and only provide our own, so that we can switch implementation easily?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will consider but I think for now it's best not to limit our users.

Copy link
Author

@bergundy bergundy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

reject();
});
webpack.stdout.on('data', (buff: Buffer) => {
const data = buff.toString();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a comment that this is ugly

{{/throws}}
throw new InternalServerError(body.message);
} else if (!isJSON) {
throw new Error(`${response.status} - ${response.statusText}`);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add this in a separate commit #4

@@ -0,0 +1,124 @@
// tslint:disable
import fetch from 'node-fetch';
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed F2F transition to fetch should not slow us down.

ValidationError,
};

export interface Options extends Pick<RequestInit, 'agent' | 'redirect' | 'follow' | 'compress'> {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will consider but I think for now it's best not to limit our users.


if (mergedOptions.timeoutMs) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), mergedOptions.timeoutMs);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is usually the expected behavior.

// tslint:disable
import fetch from 'node-fetch';
import { RequestInit } from 'node-fetch';
import AbortController from 'abort-controller';
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet, #3

@bergundy
Copy link
Author

@vogre PTAL

Copy link

@vogre vogre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

WDYT about passing the fetch implementation as part of options / setting it via some setFetch method for tracing?


if (mergedOptions.timeoutMs) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), mergedOptions.timeoutMs);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both behaviors are interesting (and the interval between bytes is used by Node.js and NGINX), but I think it might be enough for now.

@bergundy
Copy link
Author

Thanks!

Didn't really understand the comment about passing the fetch implementation in options, was what I did not good enough?

@bergundy bergundy merged commit 2c7a49e into master Feb 20, 2019
@bergundy bergundy deleted the browser-support branch February 20, 2019 07:59
@vogre
Copy link

vogre commented Feb 20, 2019

@bergundy: what I want is some way to provide instrumentation (e.g. counters, timings, logs, etc. for the requests). One option is instead of await fetch do await fetchImpl (when by default fetchImpl = fetch, but let users override it.

}

try {
const response = await (mergedOptions.fetchImplementation || fetch)(`${this.serverUrl}/{{name}}`, {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vogre was this what you were looking for?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separation of client-browser and client-node got me there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants