Nessuna descrizione

XMLHttpRequest.js 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // Copyright 2016 wkh237@github. All rights reserved.
  2. // Use of this source code is governed by a MIT-style license that can be
  3. // found in the LICENSE file.
  4. import RNFetchBlob from '../index.js'
  5. import XMLHttpRequestEventTarget from './XMLHttpRequestEventTarget.js'
  6. import Log from '../utils/log.js'
  7. import Blob from './Blob.js'
  8. const log = new Log('XMLHttpRequest')
  9. log.level(3)
  10. const UNSENT = 0
  11. const OPENED = 1
  12. const HEADERS_RECEIVED = 2
  13. const LOADING = 3
  14. const DONE = 4
  15. export default class XMLHttpRequest extends XMLHttpRequestEventTarget{
  16. _onreadystatechange : () => void;
  17. _readyState : number = UNSENT;
  18. _response : any = '';
  19. _responseText : any = '';
  20. _responseHeaders : any = '';
  21. _responseType : '' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' = '';
  22. // TODO : not suppoted for now
  23. _responseURL : null = '';
  24. _responseXML : null = '';
  25. _status : number;
  26. _statusText : string;
  27. _timeout : number = 0;
  28. _upload : XMLHttpRequestEventTarget;
  29. _sendFlag : boolean = false;
  30. // RNFetchBlob compatible data structure
  31. _config : RNFetchBlobConfig;
  32. _url : any;
  33. _method : string;
  34. _headers: any;
  35. _body: any;
  36. // RNFetchBlob promise object, which has `progress`, `uploadProgress`, and
  37. // `cancel` methods.
  38. _task: any;
  39. static get UNSENT() {
  40. return UNSENT
  41. }
  42. static get OPENED() {
  43. return OPENED
  44. }
  45. static get HEADERS_RECEIVED() {
  46. return HEADERS_RECEIVED
  47. }
  48. static get LOADING() {
  49. return LOADING
  50. }
  51. static get DONE() {
  52. return DONE
  53. }
  54. constructor(...args) {
  55. super()
  56. log.verbose('XMLHttpRequest constructor called', args)
  57. this._config = {}
  58. this._args = {}
  59. this._headers = {}
  60. }
  61. // XMLHttpRequest.open, always async, user and password not supported.
  62. open(method:string, url:string, async:true, user:any, password:any) {
  63. log.verbose('XMLHttpRequest open ', method, url, async, user, password)
  64. this._method = method
  65. this._url = url
  66. this.readyState = XMLHttpRequest.OPENED
  67. }
  68. /**
  69. * Invoke this function to send HTTP request, and set body.
  70. * @param {any} body Body in RNfetchblob flavor
  71. */
  72. send(body) {
  73. this._sendFlag = true
  74. log.verbose('XMLHttpRequest send ', body)
  75. let {_method, _url, _headers } = this
  76. log.verbose('sending request with args', _method, _url, _headers, body)
  77. this._upload = new XMLHttpRequestEventTarget()
  78. log.verbose(typeof body, body instanceof FormData)
  79. if(body instanceof Blob) {
  80. body = RNFetchBlob.wrap(body.getRNFetchBlobRef())
  81. }
  82. this.dispatchEvent('loadstart')
  83. if(this.onloadstart)
  84. this.onloadstart()
  85. this._task = RNFetchBlob.config({ auto: true })
  86. .fetch(_method, _url, _headers, body)
  87. this._task
  88. .stateChange(this._headerReceived.bind(this))
  89. .uploadProgress(this._progressEvent.bind(this))
  90. .progress(this._progressEvent.bind(this))
  91. .catch(this._onError.bind(this))
  92. .then(this._onDone.bind(this))
  93. }
  94. overrideMimeType(mime:string) {
  95. log.verbose('XMLHttpRequest overrideMimeType', mime)
  96. this._headers['content-type'] = mime
  97. }
  98. setRequestHeader(name, value) {
  99. log.verbose('XMLHttpRequest set header', name, value)
  100. if(this._readyState !== OPENED && this._sendFlag) {
  101. throw `InvalidStateError : Calling setRequestHeader in wrong state ${this._readyState}`
  102. }
  103. if(/[^\u0000-\u00ff]/.test(name) || typeof name !== 'string') {
  104. throw `TypeError : Invalid header field name ${name}`
  105. }
  106. this._headers[name] = value
  107. }
  108. abort() {
  109. log.verbose('XMLHttpRequest abort ')
  110. if(!this._task)
  111. return
  112. this._task.cancel((err) => {
  113. let e = {
  114. timeStamp : Date.now(),
  115. }
  116. if(this.onabort)
  117. this.onabort()
  118. if(err) {
  119. e.detail = err
  120. e.type = 'error'
  121. this.dispatchEvent('error', e)
  122. }
  123. else {
  124. e.type = 'abort'
  125. this.dispatchEvent('abort', e)
  126. }
  127. })
  128. }
  129. getResponseHeader(field:string):string | null {
  130. log.verbose('XMLHttpRequest get header', field)
  131. if(!this._responseHeaders)
  132. return null
  133. return this.responseHeaders[field] || null
  134. }
  135. getAllResponseHeaders():string | null {
  136. log.verbose('XMLHttpRequest get all headers',this._task.taskId, this._responseHeaders)
  137. if(!this._responseHeaders)
  138. return null
  139. let result = ''
  140. let respHeaders = this.responseHeaders
  141. for(let i in respHeaders) {
  142. result += `${i}:${respHeaders[i]}\r\n`
  143. }
  144. return result
  145. }
  146. _headerReceived(e) {
  147. log.verbose('header received ', this._task.taskId, e)
  148. this.responseURL = this._url
  149. if(e.state === "2") {
  150. this._readyState = XMLHttpRequest.HEADERS_RECEIVED
  151. this._responseHeaders = e.headers
  152. this._responseText = e.status
  153. this._responseType = e.respType || ''
  154. this._status = Math.floor(e.status)
  155. }
  156. }
  157. _progressEvent(send:number, total:number) {
  158. log.verbose(this.readyState)
  159. if(this.readyState === XMLHttpRequest.HEADERS_RECEIVED)
  160. this.readyState = XMLHttpRequest.LOADING
  161. let lengthComputable = false
  162. let e = { lengthComputable }
  163. if(total && total >= 0)
  164. e.lengthComputable = true
  165. else {
  166. Object.assign(e, { loaded : send, total })
  167. }
  168. if(this.onprogress)
  169. this.onprogress(e)
  170. this.dispatchEvent('progress', e)
  171. }
  172. _onError(err) {
  173. log.verbose('XMLHttpRequest error', err)
  174. this.statusText = err
  175. this.status = String(err).match(/\d+/)
  176. this.status = this.status ? Math.floor(this.status) : 404
  177. this.readyState = XMLHttpRequest.DONE
  178. if(String(err).match('timeout') !== null) {
  179. this.dispatchEvent('timeout')
  180. if(this.ontimeout)
  181. this.ontimeout()
  182. }
  183. else if(this.onerror) {
  184. this.dispatchEvent('error')
  185. this.onerror({
  186. type : 'error',
  187. detail : err
  188. })
  189. }
  190. }
  191. _onDone(resp) {
  192. log.verbose('XMLHttpRequest done', this._task.taskId, this)
  193. this.statusText = '200 OK'
  194. this._status = 200
  195. switch(resp.type) {
  196. case 'base64' :
  197. if(this.responseType === 'json') {
  198. this._responseText = resp.text()
  199. this._response = resp.json()
  200. }
  201. else {
  202. this._responseText = resp.text()
  203. this._response = this.responseText
  204. }
  205. break;
  206. case 'path' :
  207. this.response = resp.blob()
  208. break;
  209. }
  210. this.dispatchEvent('loadend')
  211. if(this.onloadend)
  212. this.onloadend()
  213. this.dispatchEvent('load')
  214. if(this._onload)
  215. this._onload()
  216. this.readyState = XMLHttpRequest.DONE
  217. }
  218. set onreadystatechange(fn:() => void) {
  219. log.verbose('XMLHttpRequest set onreadystatechange', fn.toString())
  220. this._onreadystatechange = fn
  221. }
  222. set readyState(val:number) {
  223. log.verbose('XMLHttpRequest ready state changed to ', val)
  224. this._readyState = val
  225. if(this._onreadystatechange) {
  226. log.verbose('trigger onreadystatechange event', this._readyState)
  227. log.verbose(this._onreadystatechange)
  228. this.dispatchEvent('readystatechange', )
  229. if(this._onreadystatechange)
  230. this._onreadystatechange()
  231. }
  232. }
  233. get readyState() {
  234. log.verbose('get readyState', this._readyState)
  235. return this._readyState
  236. }
  237. get status() {
  238. log.verbose('get status', this._status)
  239. return this._status
  240. }
  241. set statusText(val) {
  242. this._statusText = val
  243. }
  244. get statusText() {
  245. log.verbose('get statusText', this._statusText)
  246. return this._statusText
  247. }
  248. set response(val) {
  249. log.verbose('set response', val)
  250. this._response = val
  251. }
  252. get response() {
  253. log.verbose('get response', this._response)
  254. return this._response
  255. }
  256. get responseText() {
  257. log.verbose('get responseText', this._responseText)
  258. return this._responseText
  259. }
  260. get responseURL() {
  261. log.verbose('get responseURL', this._responseURL)
  262. return this._responseURL
  263. }
  264. get responseHeaders() {
  265. log.verbose('get responseHeaders', this._task.taskId, this._responseHeaders)
  266. return this._responseHeaders
  267. }
  268. set timeout(val) {
  269. log.verbose('set timeout', this._timeout)
  270. this._timeout = val
  271. }
  272. get timeout() {
  273. log.verbose('get timeout', this._timeout)
  274. return this._timeout
  275. }
  276. get upload() {
  277. log.verbose('get upload', this._upload)
  278. return this._upload
  279. }
  280. get responseType() {
  281. log.verbose('get response type', this._responseType)
  282. return this._responseType
  283. }
  284. }