I am having a use case where I need to limit the number of outgoing http requests. Yes, I do have rate limiter on the server-side but a limit on the number of active http requests is also need on the front end too.For that reason I am attempting to implement a sliding window protocol where at any single time I will only have n active requests.
This approach using Rxjs works fine in general, see here: https://jsbin.com/pacicubeci/1/edit?js,console,output
but I am not clear how to use the same logic with http interceptors. My attempt below fails at compile time with the following error:
Type 'Subscription' is missing the following properties from type 'Observable<HttpEvent>': _isScalar, source, operator, lift, and 114 more.(2740)
With that, how can I return an observable and maintain a queue at the http interceptor at the same time?Is my approach flawed? Can I use http interceptors to http rate limit at all?
@Injectable()
export class I1 implements HttpInterceptor {
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const modified = req.clone({ setHeaders: { "Custom-Header-1": "1" } });
return next
.handle(req)
.do((ev: HttpEvent<any>) => {
if (ev instanceof HttpResponse) {
console.log(ev);
}
})
.pipe(
bufferTime(1000, null, 1),
filter(buffer => buffer.length > 0),
concatMap(buffer => of(buffer).pipe(delay(1000)))
)
.subscribe(console.log);
}
}
https://stackblitz.com/edit/angular-interceptors-npqkjp?file=app/interceptors.ts
If you'd like to find out more about how interceptors and the HttpClientModule work under the hood, you could check out this article: Exploring the HttpClientModule in Angular.
To get a better understanding of why, I will paste a snippet copied from the article linked above:
StackBlitz demo.
The gist is that interceptors create some sort of chain which ends with an observable that is responsible for making the actual request. This is the last node from the chain:
I think a way to solve this is to create an interceptor that will contain the queue logic and make its
interceptmethod return anObservable, so that it can be subscribed to:The
filteroperators were used because by usingshare, the responses will be sent to all subscribers. Imagine you're synchronously callinghttp.get5 times, so 5 new subscribers forshare's Subject, and the last one will receive its response, but the response of other requests as well. So use can usefilterin order to give the request the right response, in this case by comparing the URL of the request(req.url) with the URL we got from theHttpResponse.url:Link for the above snippet.
Now, why did we use
share()?Let's see a simpler example first:
At this point, the Subject
sshould have 3 subscribers. This is because when you return queue, you returns.pipe(...)and when you subscribe to that, it's the same as doing:so, that's why the subject will have 3 subscribers at the end.
Now let's examine the same snippet, but with
share():After you subscribe to request 1,
sharewill create a Subject instance and all the subsequent subscribers will belong to it, instead of belonging to the main Subjects. So,swill have only one subscriber. This will make sure that we implement the queue correctly, because although the Subjectshas only one subscriber, it will still accepts.next()values, whose results will be passed along to the other subject(that one which comes fromshare()), which will eventually send the responses to all of its subscribers.