diff --git a/spec/helpers/ajax-helper.ts b/spec/helpers/ajax-helper.ts index 3d32f5d01ae..244a6e345d8 100644 --- a/spec/helpers/ajax-helper.ts +++ b/spec/helpers/ajax-helper.ts @@ -133,6 +133,7 @@ export class MockXMLHttpRequest { method: any; data: any; requestHeaders: any = {}; + withCredentials: boolean = false; constructor() { this.previousRequest = MockXMLHttpRequest.recentRequest; diff --git a/spec/observables/dom/ajax-spec.ts b/spec/observables/dom/ajax-spec.ts index 5782b983ed5..0f85988be5f 100644 --- a/spec/observables/dom/ajax-spec.ts +++ b/spec/observables/dom/ajax-spec.ts @@ -9,12 +9,16 @@ declare const global: any; describe('Observable.ajax', () => { let gXHR: XMLHttpRequest; let rXHR: XMLHttpRequest; + let xDomain: any; beforeEach(() => { gXHR = global.XMLHttpRequest; rXHR = root.XMLHttpRequest; + xDomain = root.XDomainRequest; + global.XMLHttpRequest = MockXMLHttpRequest; root.XMLHttpRequest = MockXMLHttpRequest; + root.XDomainRequest = MockXMLHttpRequest; }); afterEach(() => { @@ -22,6 +26,88 @@ describe('Observable.ajax', () => { global.XMLHttpRequest = gXHR; root.XMLHttpRequest = rXHR; + root.XDomainRequest = xDomain; + root.ActiveXObject = null; + }); + + it('should create default XMLHttpRequest for non CORS', () => { + const obj: Rx.AjaxRequest = { + url: '/', + method: '' + }; + + Rx.Observable.ajax(obj).subscribe(); + expect(MockXMLHttpRequest.mostRecent.withCredentials).to.be.false; + }); + + it('should try to create AXObject for XHR in old version of IE', () => { + const axObject = new MockXMLHttpRequest(); + root.ActiveXObject = () => axObject; + root.XMLHttpRequest = null; + + const obj: Rx.AjaxRequest = { + url: '/', + method: '' + }; + + Rx.Observable.ajax(obj).subscribe(); + expect(MockXMLHttpRequest.mostRecent).to.be.equal(axObject); + }); + + it('should throw if not able to create XMLHttpRequest', () => { + root.XMLHttpRequest = null; + root.ActiveXObject = null; + + const obj: Rx.AjaxRequest = { + url: '/', + method: '' + }; + + expect(() => { + Rx.Observable.ajax(obj).subscribe(); + }).to.throw(); + }); + + it('should create XMLHttpRequest for CORS', () => { + const obj: Rx.AjaxRequest = { + url: '/', + method: '', + crossDomain: true, + withCredentials: true + }; + + Rx.Observable.ajax(obj).subscribe(); + expect(MockXMLHttpRequest.mostRecent.withCredentials).to.be.true; + }); + + it('should try to create XDomainRequest for CORS if XMLHttpRequest is not available', () => { + root.XMLHttpRequest = null; + + const obj: Rx.AjaxRequest = { + url: '/', + method: '', + crossDomain: true, + withCredentials: true + }; + + Rx.Observable.ajax(obj).subscribe(); + expect(MockXMLHttpRequest.mostRecent).to.exist; + }); + + it('should throw if not able to create CORS request', () => { + root.XMLHttpRequest = null; + root.XDomainRequest = null; + + const obj: Rx.AjaxRequest = { + url: '/', + method: '', + crossDomain: true, + withCredentials: true + }; + + expect(() => { + Rx.Observable.ajax(obj).subscribe(); + }).to.throw(); }); it('should set headers', () => { diff --git a/spec/support/default.opts b/spec/support/default.opts index 287be14820d..04406230fa0 100644 --- a/spec/support/default.opts +++ b/spec/support/default.opts @@ -8,7 +8,7 @@ --bail --full-trace --check-leaks ---globals WebSocket,FormData +--globals WebSocket,FormData,XDomainRequest,ActiveXObject --recursive --timeout 5000 diff --git a/src/observable/dom/AjaxObservable.ts b/src/observable/dom/AjaxObservable.ts index 7cd7922da82..87da9f89f13 100644 --- a/src/observable/dom/AjaxObservable.ts +++ b/src/observable/dom/AjaxObservable.ts @@ -16,25 +16,48 @@ export interface AjaxRequest { password?: string; hasContent?: boolean; crossDomain?: boolean; + withCredentials?: boolean; createXHR?: () => XMLHttpRequest; progressSubscriber?: Subscriber; resultSelector?: (response: AjaxResponse) => T; responseType?: string; } -function createXHRDefault(): XMLHttpRequest { - let xhr = new root.XMLHttpRequest(); - if (this.crossDomain) { +function getCORSRequest(): XMLHttpRequest { + if (root.XMLHttpRequest) { + const xhr = new root.XMLHttpRequest(); if ('withCredentials' in xhr) { - xhr.withCredentials = true; - return xhr; - } else if (!!root.XDomainRequest) { - return new root.XDomainRequest(); - } else { - throw new Error('CORS is not supported by your browser'); + xhr.withCredentials = !!this.withCredentials; } - } else { return xhr; + } else if (!!root.XDomainRequest) { + return new root.XDomainRequest(); + } else { + throw new Error('CORS is not supported by your browser'); + } +} + +function getXMLHttpRequest(): XMLHttpRequest { + if (root.XMLHttpRequest) { + return new root.XMLHttpRequest(); + } else { + let progId: string; + try { + const progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; + for (let i = 0; i < 3; i++) { + try { + progId = progIds[i]; + if (new root.ActiveXObject(progId)) { + break; + } + } catch (e) { + //suppress exceptions + } + } + return new root.ActiveXObject(progId); + } catch (e) { + throw new Error('XMLHttpRequest is not supported by your browser'); + } } } @@ -104,8 +127,6 @@ export class AjaxObservable extends Observable { * @name ajax * @owner Observable */ - static _create_stub(): void { return null; } - static create: AjaxCreationMethod = (() => { const create: any = (urlOrRequest: string | AjaxRequest) => { return new AjaxObservable(urlOrRequest); @@ -127,8 +148,11 @@ export class AjaxObservable extends Observable { const request: AjaxRequest = { async: true, - createXHR: createXHRDefault, + createXHR: function() { + return this.crossDomain ? getCORSRequest.call(this) : getXMLHttpRequest(); + }, crossDomain: false, + withCredentials: false, headers: {}, method: 'GET', responseType: 'json',