From 34e66bb00f43c0ac0f005d34c084825f9d2854e2 Mon Sep 17 00:00:00 2001 From: Marko Vuksanovic Date: Fri, 9 May 2014 21:20:22 +1000 Subject: [PATCH] feat(Http): Http service can make cross-site requests (get, post, put, etc.) which use credentials (such as cookies or authorization headers). Closes #945 --- lib/core_dom/http.dart | 58 +++++++++++--------- test/core_dom/http_spec.dart | 100 +++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 25 deletions(-) diff --git a/lib/core_dom/http.dart b/lib/core_dom/http.dart index fdae907e8..334194e46 100644 --- a/lib/core_dom/http.dart +++ b/lib/core_dom/http.dart @@ -422,6 +422,7 @@ class Http { data, Map params, Map headers, + withCredentials: false, xsrfHeaderName, xsrfCookieName, interceptors, @@ -481,7 +482,8 @@ class Http { var result = _backend.request(url, method: method, requestHeaders: config.headers, - sendData: config.data).then((dom.HttpRequest value) { + sendData: config.data, + withCredentials: withCredentials).then((dom.HttpRequest value) { // TODO: Uncomment after apps migrate off of this class. // assert(value.status >= 200 && value.status < 300); @@ -535,15 +537,16 @@ class Http { String data, Map params, Map headers, + withCredentials: false, xsrfHeaderName, xsrfCookieName, interceptors, cache, timeout - }) => call(method: 'GET', url: url, data: data, params: params, - headers: headers, xsrfHeaderName: xsrfHeaderName, - xsrfCookieName: xsrfCookieName, interceptors: interceptors, - cache: cache, timeout: timeout); + }) => call(method: 'GET', url: url, data: data, params: params, headers: headers, + withCredentials: withCredentials, xsrfHeaderName: xsrfHeaderName, + xsrfCookieName: xsrfCookieName, interceptors: interceptors, cache: cache, + timeout: timeout); /** * Shortcut method for DELETE requests. See [call] for a complete description @@ -553,15 +556,16 @@ class Http { String data, Map params, Map headers, + withCredentials: false, xsrfHeaderName, xsrfCookieName, interceptors, cache, timeout - }) => call(method: 'DELETE', url: url, data: data, params: params, - headers: headers, xsrfHeaderName: xsrfHeaderName, - xsrfCookieName: xsrfCookieName, interceptors: interceptors, - cache: cache, timeout: timeout); + }) => call(method: 'DELETE', url: url, data: data, params: params, headers: headers, + withCredentials: withCredentials, xsrfHeaderName: xsrfHeaderName, + xsrfCookieName: xsrfCookieName, interceptors: interceptors, cache: cache, + timeout: timeout); /** * Shortcut method for HEAD requests. See [call] for a complete description @@ -571,15 +575,16 @@ class Http { String data, Map params, Map headers, + withCredentials: false, xsrfHeaderName, xsrfCookieName, interceptors, cache, timeout - }) => call(method: 'HEAD', url: url, data: data, params: params, - headers: headers, xsrfHeaderName: xsrfHeaderName, - xsrfCookieName: xsrfCookieName, interceptors: interceptors, - cache: cache, timeout: timeout); + }) => call(method: 'HEAD', url: url, data: data, params: params, headers: headers, + withCredentials: withCredentials, xsrfHeaderName: xsrfHeaderName, + xsrfCookieName: xsrfCookieName, interceptors: interceptors, cache: cache, + timeout: timeout); /** * Shortcut method for PUT requests. See [call] for a complete description @@ -588,15 +593,16 @@ class Http { async.Future put(String url, String data, { Map params, Map headers, + withCredentials: false, xsrfHeaderName, xsrfCookieName, interceptors, cache, timeout - }) => call(method: 'PUT', url: url, data: data, params: params, - headers: headers, xsrfHeaderName: xsrfHeaderName, - xsrfCookieName: xsrfCookieName, interceptors: interceptors, - cache: cache, timeout: timeout); + }) => call(method: 'PUT', url: url, data: data, params: params, headers: headers, + withCredentials: withCredentials, xsrfHeaderName: xsrfHeaderName, + xsrfCookieName: xsrfCookieName, interceptors: interceptors, cache: cache, + timeout: timeout); /** * Shortcut method for POST requests. See [call] for a complete description @@ -605,15 +611,16 @@ class Http { async.Future post(String url, String data, { Map params, Map headers, + withCredentials: false, xsrfHeaderName, xsrfCookieName, interceptors, cache, timeout - }) => call(method: 'POST', url: url, data: data, params: params, - headers: headers, xsrfHeaderName: xsrfHeaderName, - xsrfCookieName: xsrfCookieName, interceptors: interceptors, - cache: cache, timeout: timeout); + }) => call(method: 'POST', url: url, data: data, params: params, headers: headers, + withCredentials: withCredentials, xsrfHeaderName: xsrfHeaderName, + xsrfCookieName: xsrfCookieName, interceptors: interceptors, cache: cache, + timeout: timeout); /** * Shortcut method for JSONP requests. See [call] for a complete description @@ -623,15 +630,16 @@ class Http { String data, Map params, Map headers, + withCredentials: false, xsrfHeaderName, xsrfCookieName, interceptors, cache, timeout - }) => call(method: 'JSONP', url: url, data: data, params: params, - headers: headers, xsrfHeaderName: xsrfHeaderName, - xsrfCookieName: xsrfCookieName, interceptors: interceptors, - cache: cache, timeout: timeout); + }) => call(method: 'JSONP', url: url, data: data, params: params, headers: headers, + withCredentials: withCredentials, xsrfHeaderName: xsrfHeaderName, + xsrfCookieName: xsrfCookieName, interceptors: interceptors, cache: cache, + timeout: timeout); /** * Parse raw headers into key-value object diff --git a/test/core_dom/http_spec.dart b/test/core_dom/http_spec.dart index b15f7a856..4f1642d50 100644 --- a/test/core_dom/http_spec.dart +++ b/test/core_dom/http_spec.dart @@ -98,6 +98,69 @@ void main() { flush(); })); + describe('backend', () { + beforeEachModule((Module module) { + FakeBackend fakeBackend = new FakeBackend(); + module.bind(HttpBackend, toFactory: (i) => fakeBackend); + module.bind(FakeBackend, toFactory: (i) => i.get(HttpBackend)); + }); + + it('should pass on withCredentials to backend and use GET as default method', + async((FakeBackend backend) { + http(url: '/url', method: 'GET', withCredentials: true); + microLeap(); + expect(backend.url).toEqual('/url'); + expect(backend.method).toEqual('GET'); + expect(backend.withCredentials).toBeTruthy(); + })); + + it('get should pass on withCredentials to backend', async((FakeBackend backend) { + http.get('/url', withCredentials: true); + microLeap(); + expect(backend.method).toEqual('GET'); + expect(backend.withCredentials).toBeTruthy(); + })); + + it('delete should pass on withCredentials to backend', async((FakeBackend backend) { + http.delete('/url', withCredentials: true); + microLeap(); + expect(backend.method).toEqual('DELETE'); + expect(backend.withCredentials).toBeTruthy(); + })); + + it('head should pass on withCredentials to backend', async((FakeBackend backend) { + http.head('/url', withCredentials: true); + microLeap(); + expect(backend.method).toEqual('HEAD'); + expect(backend.withCredentials).toBeTruthy(); + })); + + it('put should pass on data and withCredentials to backend', async((FakeBackend backend) { + var mockData = "mockData"; + http.put('/url', mockData, withCredentials: true); + microLeap(); + expect(backend.sendData).toEqual(mockData); + expect(backend.method).toEqual('PUT'); + expect(backend.withCredentials).toBeTruthy(); + })); + + it('post should pass on data and withCredentials to backend', async((FakeBackend backend) { + var mockData = "mockData"; + http.post('/url', mockData, withCredentials: true); + microLeap(); + expect(backend.sendData).toEqual(mockData); + expect(backend.method).toEqual('POST'); + expect(backend.withCredentials).toBeTruthy(); + })); + + it('jsonp should pass withCredentials to backend', async((FakeBackend backend) { + http.jsonp('/url', withCredentials: true); + microLeap(); + expect(backend.method).toEqual('JSONP'); + expect(backend.withCredentials).toBeTruthy(); + })); + }); + describe('params', () { it('should do basic request with params and encode', async(() { @@ -1356,3 +1419,40 @@ class FakeFile implements File { Blob slice([int start, int end, String contentType]) => null; int get lastModified => new DateTime.now().millisecondsSinceEpoch; } + +class FakeBackend extends Mock implements HttpBackend { + + String url; + String method; + bool withCredentials; + String responseType; + String mimeType; + Map requestHeaders; + dynamic sendData; + + Future request(String url, { + String method, + bool withCredentials, + String responseType, + String mimeType, + Map requestHeaders, + sendData, + void onProgress(ProgressEvent e)}) { + this.url = url; + this.method = method; + this.withCredentials = withCredentials; + this.responseType = responseType; + this.mimeType = mimeType; + this.requestHeaders = requestHeaders; + this.sendData = sendData; + HttpRequest request = new HttpRequest(); + return new Future.value(new HttpRequest()); + } +} + +class FakeHttpRequest extends Mock implements HttpRequest { + FakeHttpRequest() { + when(callsTo('get status')).thenReturn(200); + when(callsTo('get responseText')).thenReturn('Fake Request'); + } +}