mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(http): add hooks for propagating traces across XHR callbacks.
Enables propagating a trace across XHR callbacks by providing a hook for wrapping the callback with a function bound to the send trace context.
This commit is contained in:
committed by
Kristiyan Kostadinov
parent
763db3a70b
commit
07b8e953f4
@@ -8,9 +8,12 @@
|
||||
|
||||
import {XhrFactory} from '../../index';
|
||||
import {
|
||||
inject,
|
||||
Injectable,
|
||||
ɵRuntimeError as RuntimeError,
|
||||
ɵformatRuntimeError as formatRuntimeError,
|
||||
ɵTracingService as TracingService,
|
||||
ɵTracingSnapshot as TracingSnapshot,
|
||||
} from '@angular/core';
|
||||
import {from, Observable, Observer, of} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
@@ -103,8 +106,16 @@ function validateXhrCompatibility(req: HttpRequest<any>) {
|
||||
*/
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class HttpXhrBackend implements HttpBackend {
|
||||
private readonly tracingService: TracingService<TracingSnapshot> | null = inject(TracingService, {
|
||||
optional: true,
|
||||
});
|
||||
|
||||
constructor(private xhrFactory: XhrFactory) {}
|
||||
|
||||
private maybePropagateTrace<T extends Function>(fn: T): T {
|
||||
return this.tracingService?.propagate ? this.tracingService.propagate(fn) : fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a request and returns a stream of response events.
|
||||
* @param req The request object.
|
||||
@@ -219,7 +230,7 @@ export class HttpXhrBackend implements HttpBackend {
|
||||
// emit. This allows them to be unregistered as event listeners later.
|
||||
|
||||
// First up is the load event, which represents a response being fully available.
|
||||
const onLoad = () => {
|
||||
const onLoad = this.maybePropagateTrace(() => {
|
||||
// Read response state from the memoized partial data.
|
||||
let {headers, status, statusText, url} = partialFromXhr();
|
||||
|
||||
@@ -296,12 +307,12 @@ export class HttpXhrBackend implements HttpBackend {
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// The onError callback is called when something goes wrong at the network level.
|
||||
// Connection timeout, DNS error, offline, etc. These are actual errors, and are
|
||||
// transmitted on the error channel.
|
||||
const onError = (error: ProgressEvent) => {
|
||||
const onError = this.maybePropagateTrace((error: ProgressEvent) => {
|
||||
const {url} = partialFromXhr();
|
||||
const res = new HttpErrorResponse({
|
||||
error,
|
||||
@@ -310,12 +321,12 @@ export class HttpXhrBackend implements HttpBackend {
|
||||
url: url || undefined,
|
||||
});
|
||||
observer.error(res);
|
||||
};
|
||||
});
|
||||
|
||||
let onTimeout = onError;
|
||||
|
||||
if (req.timeout) {
|
||||
onTimeout = (_: ProgressEvent) => {
|
||||
onTimeout = this.maybePropagateTrace((_: ProgressEvent) => {
|
||||
const {url} = partialFromXhr();
|
||||
const res = new HttpErrorResponse({
|
||||
error: new DOMException('Request timed out', 'TimeoutError'),
|
||||
@@ -324,7 +335,7 @@ export class HttpXhrBackend implements HttpBackend {
|
||||
url: url || undefined,
|
||||
});
|
||||
observer.error(res);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// The sentHeaders flag tracks whether the HttpResponseHeaders event
|
||||
@@ -335,7 +346,7 @@ export class HttpXhrBackend implements HttpBackend {
|
||||
|
||||
// The download progress event handler, which is only registered if
|
||||
// progress events are enabled.
|
||||
const onDownProgress = (event: ProgressEvent) => {
|
||||
const onDownProgress = this.maybePropagateTrace((event: ProgressEvent) => {
|
||||
// Send the HttpResponseHeaders event if it hasn't been sent already.
|
||||
if (!sentHeaders) {
|
||||
observer.next(partialFromXhr());
|
||||
@@ -363,11 +374,11 @@ export class HttpXhrBackend implements HttpBackend {
|
||||
|
||||
// Finally, fire the event.
|
||||
observer.next(progressEvent);
|
||||
};
|
||||
});
|
||||
|
||||
// The upload progress event handler, which is only registered if
|
||||
// progress events are enabled.
|
||||
const onUpProgress = (event: ProgressEvent) => {
|
||||
const onUpProgress = this.maybePropagateTrace((event: ProgressEvent) => {
|
||||
// Upload progress events are simpler. Begin building the progress
|
||||
// event.
|
||||
let progress: HttpUploadProgressEvent = {
|
||||
@@ -383,7 +394,7 @@ export class HttpXhrBackend implements HttpBackend {
|
||||
|
||||
// Send the event.
|
||||
observer.next(progress);
|
||||
};
|
||||
});
|
||||
|
||||
// By default, register for load and error events.
|
||||
xhr.addEventListener('load', onLoad);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {XhrFactory} from '../../index';
|
||||
import {HttpRequest} from '../src/request';
|
||||
import {
|
||||
HttpDownloadProgressEvent,
|
||||
@@ -19,6 +20,7 @@ import {
|
||||
HttpUploadProgressEvent,
|
||||
} from '../src/response';
|
||||
import {HttpXhrBackend} from '../src/xhr';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {Observable} from 'rxjs';
|
||||
import {toArray} from 'rxjs/operators';
|
||||
|
||||
@@ -53,8 +55,11 @@ describe('XhrBackend', () => {
|
||||
let factory: MockXhrFactory = null!;
|
||||
let backend: HttpXhrBackend = null!;
|
||||
beforeEach(() => {
|
||||
factory = new MockXhrFactory();
|
||||
backend = new HttpXhrBackend(factory);
|
||||
TestBed.configureTestingModule({
|
||||
providers: [{provide: XhrFactory, useClass: MockXhrFactory}],
|
||||
});
|
||||
factory = TestBed.inject(XhrFactory) as MockXhrFactory;
|
||||
backend = TestBed.inject(HttpXhrBackend);
|
||||
});
|
||||
it('emits status immediately', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST));
|
||||
|
||||
@@ -49,6 +49,13 @@ export interface TracingService<T extends TracingSnapshot> {
|
||||
*/
|
||||
snapshot(linkedSnapshot: T | null): T;
|
||||
|
||||
/**
|
||||
* Propagate the current tracing context to the provided function.
|
||||
* @param fn A function.
|
||||
* @return A function that will propagate the current tracing context.
|
||||
*/
|
||||
propagate?<T extends Function>(fn: T): T;
|
||||
|
||||
/**
|
||||
* Wrap an event listener bound by the framework for tracing.
|
||||
* @param element Element on which the event is bound.
|
||||
|
||||
Reference in New Issue
Block a user