Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 7b44124

Browse files
yjbanovjbdeboer
authored andcommitted
feat(http): run interceptors synchronously until first non-sync interceptor
1 parent 7b078bd commit 7b44124

File tree

2 files changed

+70
-10
lines changed

2 files changed

+70
-10
lines changed

lib/core_dom/http.dart

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ class Http {
454454
if (v is Function) headers[k] = v();
455455
});
456456

457-
var serverRequest = (HttpResponseConfig config) {
457+
serverRequest(HttpResponseConfig config) {
458458
assert(config.data == null || config.data is String || config.data is dom.File);
459459

460460
// Strip content-type if data is undefined
@@ -508,11 +508,11 @@ class Http {
508508

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

511-
var future = new async.Future.value(new HttpResponseConfig(
511+
var initialInput = new HttpResponseConfig(
512512
url: url,
513513
params: params,
514514
headers: headers,
515-
data: data));
515+
data: data);
516516

517517
_interceptors.constructChain(chain);
518518

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

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

534541
/**

test/core_dom/http_spec.dart

Lines changed: 55 additions & 2 deletions
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.
@@ -936,6 +935,60 @@ void main() {
936935
flush();
937936
}));
938937
});
938+
939+
describe('request interceptors', () {
940+
bool interceptorCalled;
941+
942+
beforeEach(() {
943+
interceptorCalled = false;
944+
});
945+
946+
describe('synchronous', () {
947+
beforeEachModule((Module module) {
948+
module.bind(HttpInterceptors, toValue: new HttpInterceptors()
949+
// The first interceptor is sync, causing the second interceptor to be called synchronously
950+
..add(new HttpInterceptor(request: (cfg) => cfg))
951+
..add(new HttpInterceptor(request: (cfg) {
952+
interceptorCalled = true;
953+
return cfg;
954+
})));
955+
});
956+
957+
it('should call backend synchronously if request interceptor chain is '
958+
'synchronous', async(() {
959+
backend.expect('POST', '/url', '').respond('');
960+
http(url: '/url', method: 'POST', data: '');
961+
expect(interceptorCalled).toBe(true);
962+
expect(backend.responses.isEmpty).toBe(false); // request made immediately
963+
flush();
964+
}));
965+
});
966+
967+
describe('asynchronous', () {
968+
beforeEachModule((Module module) {
969+
module.bind(HttpInterceptors, toValue: new HttpInterceptors()
970+
// The first interceptor is async, causing the second interceptor to be
971+
// called in a microtask
972+
..add(new HttpInterceptor(request: (cfg) => new Future.value(cfg)))
973+
..add(new HttpInterceptor(request: (cfg) {
974+
interceptorCalled = true;
975+
return cfg;
976+
})));
977+
});
978+
979+
it('should call backend asynchronously if request interceptor chain is '
980+
'asynchronous', async(() {
981+
backend.expect('POST', '/url', '').respond('');
982+
http(url: '/url', method: 'POST', data: '');
983+
expect(interceptorCalled).toBe(false);
984+
expect(backend.expectations.isEmpty).toBe(false);
985+
backend.verifyNoOutstandingRequest();
986+
987+
flush();
988+
expect(interceptorCalled).toBe(true);
989+
}));
990+
});
991+
});
939992
});
940993

941994
describe('url rewriting', () {

0 commit comments

Comments
 (0)