UNPKG

aurelia-http-client

Version:

A simple, restful, message-based wrapper around XMLHttpRequest.

213 lines (192 loc) 5.99 kB
/*eslint no-unused-vars:0*/ import {PLATFORM} from 'aurelia-pal'; import {RequestMessage} from './request-message'; import {HttpResponseMessage} from './http-response-message'; import {ErrorHttpResponseMessage} from './error-http-response-message'; function applyXhrTransformers(xhrTransformers, client, processor, message, xhr) { let i; let ii; for (i = 0, ii = xhrTransformers.length; i < ii; ++i) { xhrTransformers[i](client, processor, message, xhr); } } /** * Creates an XHR implementation. */ interface XHRConstructor { //new():XHR; } /** * Represents an XHR. */ interface XHR { /** * The status code of the response. */ status: number; /** * The status text. */ statusText: string; /** * The raw response. */ response: any; /** * The raw response text. */ responseText: string; /** * The load callback. */ onload: Function; /** * The timeout callback. */ ontimeout: Function; /** * The error callback. */ onerror: Function; /** * The abort callback. */ onabort: Function; /** * Aborts the request. */ abort(): void; /** * Opens the XHR channel. */ open(method: string, url: string, isAsync: boolean, user?: string, password?: string): void; /** * Sends the request. */ send(content? : any): void; } /** * Represents an XHR transformer. */ interface XHRTransformer { (client: HttpClient, processor: RequestMessageProcessor, message: RequestMessage, xhr: XHR): void; } /** * Processes request messages. */ export class RequestMessageProcessor { /** * Creates an instance of RequestMessageProcessor. */ constructor(xhrType: XHRConstructor, xhrTransformers: XHRTransformer[]) { this.XHRType = xhrType; this.xhrTransformers = xhrTransformers; this.isAborted = false; } /** * Aborts the request. */ abort(): void { // The logic here is if the xhr object is not set then there is nothing to abort so the intent was carried out // Also test if the XHR is UNSENT - if not, it will be aborted in the process() phase if (this.xhr && this.xhr.readyState !== PLATFORM.XMLHttpRequest.UNSENT) { this.xhr.abort(); } this.isAborted = true; } /** * Processes the request. * @param client The HttpClient making the request. * @param requestMessage The message to process. * @return A promise for an HttpResponseMessage. */ process(client: HttpClient, requestMessage: RequestMessage): Promise<HttpResponseMessage> { let promise = new Promise((resolve, reject) => { let rejectResponse: (resp: HttpResponseMessage) => void; if (client.rejectPromiseWithErrorObject) { rejectResponse = (resp: HttpResponseMessage) => { const errorResp = new ErrorHttpResponseMessage(resp); reject(errorResp); }; } else { rejectResponse = (resp: HttpResponseMessage) => { reject(resp); }; } let xhr = this.xhr = new this.XHRType(); xhr.onload = (e) => { let response = new HttpResponseMessage(requestMessage, xhr, requestMessage.responseType, requestMessage.reviver); if (response.isSuccess) { resolve(response); } else { rejectResponse(response); } }; xhr.ontimeout = (e) => { rejectResponse(new HttpResponseMessage(requestMessage, { response: e, status: xhr.status, statusText: xhr.statusText }, 'timeout')); }; xhr.onerror = (e) => { rejectResponse(new HttpResponseMessage(requestMessage, { response: e, status: xhr.status, statusText: xhr.statusText }, 'error')); }; xhr.onabort = (e) => { rejectResponse(new HttpResponseMessage(requestMessage, { response: e, status: xhr.status, statusText: xhr.statusText }, 'abort')); }; }); return Promise.resolve(requestMessage) .then(message => { let processRequest = () => { if (this.isAborted) { // Some interceptors can delay sending of XHR, so when abort is called // before XHR is actually sent we abort() instead send() this.xhr.abort(); } else { this.xhr.open(message.method, message.buildFullUrl(), true, message.user, message.password); applyXhrTransformers(this.xhrTransformers, client, this, message, this.xhr); if (typeof message.content === 'undefined') { // IE serializes undefined as "undefined" // some servers reject such requests because of unexpected payload, e.g. in case of DELETE requests this.xhr.send(); } else { this.xhr.send(message.content); } } return promise; }; // [ onFullfilled, onReject ] pairs let chain = [[processRequest, undefined]]; // Apply interceptors chain from the message.interceptors let interceptors = message.interceptors || []; interceptors.forEach(function(interceptor) { if (interceptor.request || interceptor.requestError) { chain.unshift([ interceptor.request ? interceptor.request.bind(interceptor) : undefined, interceptor.requestError ? interceptor.requestError.bind(interceptor) : undefined ]); } if (interceptor.response || interceptor.responseError) { chain.push([ interceptor.response ? interceptor.response.bind(interceptor) : undefined, interceptor.responseError ? interceptor.responseError.bind(interceptor) : undefined ]); } }); let interceptorsPromise = Promise.resolve(message); while (chain.length) { interceptorsPromise = interceptorsPromise.then(...chain.shift()); } return interceptorsPromise; }); } }