import RNFetchBlob from '../index.js' import Log from '../utils/log.js' import fs from '../fs' import unicode from '../utils/unicode' import Blob from './Blob' const log = new Log('FetchPolyfill') log.disable() // log.level(3) export default class Fetch { constructor(config:RNFetchBlobConfig) { Object.assign(this, new RNFetchBlobFetchPolyfill(config)) } } class RNFetchBlobFetchPolyfill { constructor(config:RNFetchBlobConfig) { this.build = () => (url, options = {}) => { let body = options.body let promise = Promise.resolve() let blobCache = null options.headers = options.headers || {} let ctype = options['Content-Type'] || options['content-type'] let ctypeH = options.headers['Content-Type'] || options.headers['content-type'] options.headers['Content-Type'] = ctype || ctypeH options.headers['content-type'] = ctype || ctypeH options.method = options.method || 'GET' if(body) { // When the request body is an instance of FormData, create a Blob cache // to upload the body. if(body instanceof FormData) { log.verbose('convert FormData to blob body') promise = Blob.build(body).then((b) => { blobCache = b options.headers['Content-Type'] = 'multipart/form-data;boundary=' + b.multipartBoundary return Promise.resolve(RNFetchBlob.wrap(b._ref)) }) } // When request body is a Blob, use file URI of the Blob as request body. else if (body.isRNFetchBlobPolyfill) promise = Promise.resolve(RNFetchBlob.wrap(body.blobPath)) else if (typeof body !== 'object' && options.headers['Content-Type'] !== 'application/json') promise = Promise.resolve(JSON.stringify(body)) else if (typeof body !== 'string') promise = Promise.resolve(body.toString()) // send it as-is, leave the native module decide how to send the body. else promise = Promise.resolve(body) } // task is a progress reportable and cancellable Promise, however, // task.then is not, so we have to extend task.then with progress and // cancel function let progressHandler, uploadHandler, cancelHandler let statefulPromise = promise .then((body) => { let task = RNFetchBlob.config(config) .fetch(options.method, url, options.headers, body) if(progressHandler) task.progress(progressHandler) if(uploadHandler) task.uploadProgress(uploadHandler) if(cancelHandler) task.cancel() return task.then((resp) => { log.verbose('response', resp) // release blob cache created when sending request if(blobCache !== null && blobCache instanceof Blob) blobCache.close() return Promise.resolve(new RNFetchBlobFetchResponse(resp)) }) }) // extend task.then progress with report and cancelling functions statefulPromise.progress = (fn) => { progressHandler = fn } statefulPromise.uploadProgress = (fn) => { uploadHandler = fn } statefulPromise.cancel = () => { cancelHandler = true if(task.cancel) task.cancel() } return statefulPromise } } } class RNFetchBlobFetchResponse { constructor(resp:FetchBlobResponse) { let info = resp.info() this.headers = info.headers this.ok = info.status >= 200 && info.status <= 299, this.status = info.status this.type = 'basic' this.bodyUsed = false this.resp = resp this.rnfbRespInfo = info this.rnfbResp = resp } rawResp() { return Promise.resolve(this.rnfbResp) } arrayBuffer(){ log.verbose('to arrayBuffer', this.rnfbRespInfo) this.bodyUsed = true return readArrayBuffer(this.rnfbResp, this.rnfbRespInfo) } text() { log.verbose('to text', this.rnfbResp, this.rnfbRespInfo) this.bodyUsed = true return readText(this.rnfbResp, this.rnfbRespInfo) } json() { log.verbose('to json', this.rnfbResp, this.rnfbRespInfo) this.bodyUsed = true return readJSON(this.rnfbResp, this.rnfbRespInfo) } blob() { log.verbose('to blob', this.rnfbResp, this.rnfbRespInfo) this.bodyUsed = true return readBlob(this.rnfbResp, this.rnfbRespInfo) } } /** * Get response data as array. * @param {FetchBlobResponse} resp Response data object from RNFB fetch call. * @param {RNFetchBlobResponseInfo} info Response informations. * @return {Promise} */ function readArrayBuffer(resp, info):Promise { switch (info.rnfbEncode) { case 'path': return resp.readFile('ascii') break default: let buffer = [] let str = resp.text() for (let i in str) { buffer[i] = str.charCodeAt(i); } return Promise.resolve(buffer) break } } /** * Get response data as string. * @param {FetchBlobResponse} resp Response data object from RNFB fetch call. * @param {RNFetchBlobResponseInfo} info Response informations. * @return {Promise} */ function readText(resp, info):Promise { switch (info.rnfbEncode) { case 'base64': return Promise.resolve(resp.text()) break case 'path': return resp.text() break default: return Promise.resolve(resp.text()) break } } /** * Get response data as RNFetchBlob Blob polyfill object. * @param {FetchBlobResponse} resp Response data object from RNFB fetch call. * @param {RNFetchBlobResponseInfo} info Response informations. * @return {Promise} */ function readBlob(resp, info):Promise { log.verbose('readBlob', resp, info) return resp.blob() } /** * Get response data as JSON object. * @param {FetchBlobResponse} resp Response data object from RNFB fetch call. * @param {RNFetchBlobResponseInfo} info Response informations. * @return {Promise} */ function readJSON(resp, info):Promise { log.verbose('readJSON', resp, info) switch (info.rnfbEncode) { case 'base64': return Promise.resolve(resp.json()) case 'path': return resp.json() default: return Promise.resolve(resp.json()) } }