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

feat(http): http requests to implement xhr timeout #36681

Closed
wants to merge 2 commits into from

Conversation

daftfox
Copy link

@daftfox daftfox commented Apr 17, 2020

Http client allows an optional timeout option to be passed.
An error will be emitted when no response is received within the allotted interval.
This reflects XMLHttpRequest behaviour that is standard in all modern browsers.
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/timeout

This is behaviour that has been available for 'regular' js xhr requests, but has
not yet been implemented in Angular. Alternatives such as interceptors, RxJS
or custom client implementation don't offer the same behaviour in a neat package.

fixes #34421

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.io application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

The HttpClient does not allow developers to pass in a timeout option.

Issue Number: #34421

What is the new behavior?

A timeout option can be passed when creating an Http request. When no response has been received before the time runs out, the observer's error method is called.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Docs and tests have been added or updated where applicable. In addition, the payload-size limit for the aio main-es2015.js has been increased by 150 bytes to allow for the slight increase in size that happened due to the newly added feature.

@daftfox
Copy link
Author

daftfox commented Apr 20, 2020

Would anyone be able to review this PR?

Copy link
Member

@gkalpak gkalpak left a comment

Choose a reason for hiding this comment

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

Thx for updating the docs too 😍
A minor comments - otherwise lgtm ✨

packages/common/http/test/xhr_mock.ts Outdated Show resolved Hide resolved
packages/common/http/test/xhr_mock.ts Outdated Show resolved Hide resolved
packages/common/http/test/xhr_mock.ts Show resolved Hide resolved
packages/common/http/src/xhr.ts Outdated Show resolved Hide resolved
aio/content/guide/http.md Outdated Show resolved Hide resolved
aio/content/examples/http/src/app/config/config.service.ts Outdated Show resolved Hide resolved
@daftfox daftfox requested a review from gkalpak April 21, 2020 22:36
@matsko matsko added area: common/http Issues related to HTTP and HTTP Client feature Issue that requests a new feature labels Apr 23, 2020
@ngbot ngbot bot modified the milestone: needsTriage Apr 23, 2020
Copy link
Member

@gkalpak gkalpak left a comment

Choose a reason for hiding this comment

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

One suggestion. Otherwise lgtm 👍
BTW, please squash the commit into one (or create fixup commits).

@@ -74,6 +84,10 @@ export class ConfigService {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else if (error.status === 0 && error.statusText === 'Request timeout') {
Copy link
Member

Choose a reason for hiding this comment

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

It is no longer certain that error.statusText === 'Request timeout'. How about error.error instanceof ProgressEvent && error.status === 0?

@gkalpak gkalpak added the action: review The PR is still awaiting reviews from at least one requested reviewer label Jun 16, 2020
@kara kara removed their request for review June 16, 2020 17:27
Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

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

Should there also be documentation on how to fake a request timeout for testing?


* The server backend might reject the request, returning an HTTP response with a status code such as 404 or 500. These are error _responses_.

* Something could go wrong on the client-side such as a network error that prevents the request from completing successfully or an exception thrown in an RxJS operator. These errors produce JavaScript `ErrorEvent` objects.

`HttpClient` captures both kinds of errors in its `HttpErrorResponse`. You can inspect that response to identify the error's cause.
* The server took too long to respond, but only when a timeout value has been passed in as an optional parameter with the request object or the `HttpClient`'s request methods.
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps more concise:

Suggested change
* The server took too long to respond, but only when a timeout value has been passed in as an optional parameter with the request object or the `HttpClient`'s request methods.
* The server took too long to respond. This can occur only when the request specifies an explicit `timeout`.

@@ -134,20 +140,23 @@ export class HttpRequest<T> {
params?: HttpParams,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
timeout?: number,
Copy link
Member

Choose a reason for hiding this comment

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

@gkalpak perhaps out of scope for this PR, but would it make sense to extract this options object into its own type? The repetition seems unfortunate.

Copy link
Member

Choose a reason for hiding this comment

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

👍

@@ -196,6 +207,10 @@ export class HttpRequest<T> {
this.headers = options.headers;
}

if (!!options.timeout) {
Copy link
Member

Choose a reason for hiding this comment

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

Using !! is unnecessary in conditionals since it will always evaluate to the same thing as normal truthiness.

@@ -93,6 +93,11 @@ export class HttpXhrBackend implements HttpBackend {
xhr.withCredentials = true;
}

// Set the timeout if set in request object
if (req.timeout) {
Copy link
Member

Choose a reason for hiding this comment

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

What happens if xhr.timeout is set to null, undefined, or zero?

@@ -234,6 +239,19 @@ export class HttpXhrBackend implements HttpBackend {
}
};

// The onTimeout callback is called when the request has timed out. This timeout is optional
// and can be set by the user.
const onTimeout = (error: ProgressEvent) => {
Copy link
Member

Choose a reason for hiding this comment

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

@gkalpak I'm wondering if creating this function for every request will allocate an additional amount of memory that we care about. Would it make a meaningful difference to only define onTimeout when req.timeout is set?

Copy link
Member

Choose a reason for hiding this comment

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

I doubt it will matter in the grand scheme of things, but it definitely wouldn't hurt to only define onTimeout if needed.
Same goes for onDownProgress and onUpProgress.

@pullapprove pullapprove bot requested a review from jelbourn June 17, 2020 03:14
@petebacondarwin
Copy link
Contributor

@daftfox - sorry this PR has been around for a while. Could you respond to @jelbourn's questions and rebase (resolving the conflicts)?

@petebacondarwin petebacondarwin added the action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews label Jan 28, 2021
@ngbot ngbot bot modified the milestones: needsTriage, Backlog Jan 28, 2021
@petebacondarwin petebacondarwin removed the action: review The PR is still awaiting reviews from at least one requested reviewer label Jan 28, 2021
@daftfox
Copy link
Author

daftfox commented Jan 29, 2021

@daftfox - sorry this PR has been around for a while. Could you respond to @jelbourn's questions and rebase (resolving the conflicts)?

Howdy, it's been a while indeed. Will look into resolving the conflicts and answering aforementioned questions at my earliest convenience. Probably 1 Feb. Thanks for the message.

@petebacondarwin
Copy link
Contributor

Hi @daftfox - did you manage to find some time to update this PR?

@petebacondarwin petebacondarwin marked this pull request as draft May 4, 2021 15:41
@petebacondarwin petebacondarwin added the target: minor This PR is targeted for the next minor release label May 4, 2021
daftfox added 2 commits July 7, 2021 15:21
Http client allows an optional timeout option to be passed.
An error will be emitted when no response is received within the allotted interval.
This reflects XMLHttpRequest behaviour that is standard in all modern browsers.
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/timeout

This is behaviour that has been available for 'regular' js xhr requests, but has
not yet been implemented in Angular. Alternatives such as interceptors, RxJS
or custom client implementation don't offer the same behaviour in a neat package.

fixes angular#34421
Update docs and apply changes requested by @gkalpak
@daftfox
Copy link
Author

daftfox commented Jul 7, 2021

Hey @petebacondarwin. Sorry, it has been a while.
I have rebased my changes on top of master.

@petebacondarwin
Copy link
Contributor

Looks like there is a legitimate compilation error:

packages/common/http/test/xhr_mock.ts:58:5 - error TS2300: Duplicate identifier 'timeout'.

58     timeout?: (event: ErrorEvent) => void,
       ~~~~~~~
packages/common/http/test/xhr_mock.ts:62:5 - error TS2300: Duplicate identifier 'timeout'.

62     timeout?: (event: ProgressEvent) => void,
       ~~~~~~~
packages/common/http/test/xhr_mock.ts:62:5 - error TS2717: Subsequent property declarations must have the same type.  Property 'timeout' must be of type '((event: ErrorEvent) => void) | undefined', but here has type '((event: ProgressEvent<EventTarget>) => void) | undefined'.

62     timeout?: (event: ProgressEvent) => void,
       ~~~~~~~

  packages/common/http/test/xhr_mock.ts:58:5
    58     timeout?: (event: ErrorEvent) => void,
           ~~~~~~~
    'timeout' was also declared here.
packages/common/http/test/xhr_mock.ts:126:3 - error TS2393: Duplicate function implementation.

126   mockTimeoutEvent(): void {
      ~~~~~~~~~~~~~~~~
packages/common/http/test/xhr_mock.ts:130:30 - error TS2345: Argument of type 'ProgressEvent<EventTarget>' is not assignable to parameter of type 'ErrorEvent'.
  Type 'ProgressEvent<EventTarget>' is missing the following properties from type 'ErrorEvent': colno, error, filename, lineno, message

130       this.listeners.timeout(mockProgressEvent as ProgressEvent);
                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
packages/common/http/test/xhr_mock.ts:146:3 - error TS2393: Duplicate function implementation.

146   mockTimeoutEvent(error: any): void {

@petebacondarwin
Copy link
Contributor

@daftfox - are you able to clean up the failures and finish dealing with the comments on this PR?

@alxhub
Copy link
Member

alxhub commented Feb 4, 2022

(Closing due to no response)

@alxhub alxhub closed this Feb 4, 2022
@terencehonles
Copy link
Contributor

I think I might be willing to pick this up if that's OK, not sure if there are any issues with that (it looks like the CLA has been signed). I can see what bandwidth I have to pick this up and open a new PR, but I would like to know if that would be OK. I've been watching this PR for a bit but was also waiting for a response and then was busy coming upon the end of the year.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Mar 7, 2022
@jessicajaniuk
Copy link
Contributor

@terencehonles Feel free to pick this up and create a new PR with the fixes addressed. We'd love to land it. If you could mention the related issue #34421 in the PR, that'd be great.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews area: common/http Issues related to HTTP and HTTP Client cla: yes feature Issue that requests a new feature target: minor This PR is targeted for the next minor release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

The possibility of setting Timeouts with HttpClient
10 participants