Skip to content

Commit 38d3cfd

Browse files
yjbanovjbdeboer
authored andcommitted
feat(http): run interceptors synchronously until first non-sync interceptor
1 parent 016d463 commit 38d3cfd

File tree

2 files changed

+70
-10
lines changed

2 files changed

+70
-10
lines changed

lib/core_dom/http.dart

+15-8
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ class Http {
457457
if (v is Function) headers[k] = v();
458458
});
459459

460-
var serverRequest = (HttpResponseConfig config) {
460+
serverRequest(HttpResponseConfig config) {
461461
// Strip content-type if data is undefined
462462
if (config.data == null) {
463463
new List.from(headers.keys)
@@ -509,11 +509,11 @@ class Http {
509509

510510
var chain = [[serverRequest, null]];
511511

512-
var future = new async.Future.value(new HttpResponseConfig(
512+
var initialInput = new HttpResponseConfig(
513513
url: url,
514514
params: params,
515515
headers: headers,
516-
data: data));
516+
data: data);
517517

518518
_interceptors.constructChain(chain);
519519

@@ -525,11 +525,18 @@ class Http {
525525
interceptors.constructChain(chain);
526526
}
527527

528-
chain.forEach((chainFns) {
529-
future = future.then(chainFns[0], onError: chainFns[1]);
530-
});
531-
532-
return future;
528+
// Try to run interceptors synchronously until one of them returns a Future. This
529+
// makes sure that in common cases the HTTP backend sends the HTTP request immediately
530+
// saving dozens of millis of RPC latency.
531+
var chainResult = chain.fold(initialInput, (prev, chainFns) => prev is async.Future
532+
? prev.then(chainFns[0], onError: chainFns[1])
533+
: chainFns[0](prev));
534+
535+
// Depending on the implementation of HttpBackend (e.g. with a local cache) the entire
536+
// chain could finish synchronously with a non-Future result.
537+
return chainResult is async.Future
538+
? chainResult
539+
: new async.Future.value(chainResult);
533540
}
534541

535542
/**

test/core_dom/http_spec.dart

+55-2
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,8 @@ void main() {
8888
// we don't care about the data field.
8989
backend.expect('POST', '/url', 'null').respond('');
9090

91-
http(url: '/url', method: 'POST');
9291
expect(() {
93-
flush();
92+
http(url: '/url', method: 'POST');
9493
}).toThrow('with different data');
9594

9695
// satisfy the expectation for our afterEach's assert.
@@ -952,6 +951,60 @@ void main() {
952951
flush();
953952
}));
954953
});
954+
955+
describe('request interceptors', () {
956+
bool interceptorCalled;
957+
958+
beforeEach(() {
959+
interceptorCalled = false;
960+
});
961+
962+
describe('synchronous', () {
963+
beforeEachModule((Module module) {
964+
module.bind(HttpInterceptors, toValue: new HttpInterceptors()
965+
// The first interceptor is sync, causing the second interceptor to be called synchronously
966+
..add(new HttpInterceptor(request: (cfg) => cfg))
967+
..add(new HttpInterceptor(request: (cfg) {
968+
interceptorCalled = true;
969+
return cfg;
970+
})));
971+
});
972+
973+
it('should call backend synchronously if request interceptor chain is '
974+
'synchronous', async(() {
975+
backend.expect('POST', '/url', '').respond('');
976+
http(url: '/url', method: 'POST', data: '');
977+
expect(interceptorCalled).toBe(true);
978+
expect(backend.responses.isEmpty).toBe(false); // request made immediately
979+
flush();
980+
}));
981+
});
982+
983+
describe('asynchronous', () {
984+
beforeEachModule((Module module) {
985+
module.bind(HttpInterceptors, toValue: new HttpInterceptors()
986+
// The first interceptor is async, causing the second interceptor to be
987+
// called in a microtask
988+
..add(new HttpInterceptor(request: (cfg) => new Future.value(cfg)))
989+
..add(new HttpInterceptor(request: (cfg) {
990+
interceptorCalled = true;
991+
return cfg;
992+
})));
993+
});
994+
995+
it('should call backend asynchronously if request interceptor chain is '
996+
'asynchronous', async(() {
997+
backend.expect('POST', '/url', '').respond('');
998+
http(url: '/url', method: 'POST', data: '');
999+
expect(interceptorCalled).toBe(false);
1000+
expect(backend.expectations.isEmpty).toBe(false);
1001+
backend.verifyNoOutstandingRequest();
1002+
1003+
flush();
1004+
expect(interceptorCalled).toBe(true);
1005+
}));
1006+
});
1007+
});
9551008
});
9561009

9571010
describe('url rewriting', () {

0 commit comments

Comments
 (0)