Skip to content

Commit

Permalink
Refine base64 decode to support decode UTF8 data, use TypedArray to h…
Browse files Browse the repository at this point in the history
…andle binary data
  • Loading branch information
Riku Ayanokoji committed Jun 6, 2016
1 parent a40a0a1 commit a4486d6
Showing 1 changed file with 56 additions and 8 deletions.
64 changes: 56 additions & 8 deletions Libraries/Network/XMLHttpRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const EventTarget = require('event-target-shim');
const invariant = require('fbjs/lib/invariant');
const utf8 = require('utf8');
const warning = require('fbjs/lib/warning');
const base64 = require('base64-js');

type ResponseType = '' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text';
type Response = ?Object | string;
Expand Down Expand Up @@ -103,6 +104,7 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
_response: string | ?Object;
_responseType: ResponseType;
_responseText: string = '';
_responseBytes: Uint8Array;
_sent: boolean;
_url: ?string = null;
_timedOut: boolean = false;
Expand All @@ -125,7 +127,8 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
this._hasError = false;
this._headers = {};
this._responseText = '';
this._responseType = '';
this._responseBytes = undefined;
// this._responseType = '';
this._sent = false;
this._lowerCaseResponseHeaders = {};

Expand Down Expand Up @@ -199,13 +202,12 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
break;

case 'arraybuffer':
this._cachedResponse = toArrayBuffer(
this._responseText, this.getResponseHeader('content-type') || '');
this._cachedResponse = this._responseBytes.buffer;
break;

case 'blob':
this._cachedResponse = new global.Blob(
[this._responseText],
[this._responseBytes],
{type: this.getResponseHeader('content-type') || ''}
);
break;
Expand Down Expand Up @@ -261,12 +263,24 @@ class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
}

__didReceiveData(requestId: number, responseText: string): void {
responseText = atob(responseText);
if (requestId === this._requestId) {
if (!this._responseText) {
this._responseText = responseText;
if (this.responseType == '' || this.responseType == 'text') {
responseText = btou(atob(responseText));
if (!this._responseText) {
this._responseText = responseText;
} else {
this._responseText += responseText;
}
} else {
this._responseText += responseText;
let bytes = base64.toByteArray(responseText);
if (!this._responseBytes) {
this._responseBytes = bytes;
} else {
let buffer = new Uint8Array(this._responseBytes.byteLength + bytes.byteLength);
buffer.set(this._responseBytes, 0);
buffer.set(bytes, this._responseBytes.byteLength);
this._responseBytes = buffer;
}
}
this._cachedResponse = undefined; // force lazy recomputation
this.setReadyState(this.LOADING);
Expand Down Expand Up @@ -473,4 +487,38 @@ function toArrayBuffer(text: string, contentType: string): ArrayBuffer {
}
}

// from https://github.com/dankogai/js-base64/blob/master/base64.js
const re_btou = new RegExp([
'[\xC0-\xDF][\x80-\xBF]',
'[\xE0-\xEF][\x80-\xBF]{2}',
'[\xF0-\xF7][\x80-\xBF]{3}'].join('|'), 'g');

var cb_btou = function(cccc) {
switch(cccc.length) {
case 4:
var cp = ((0x07 & cccc.charCodeAt(0)) << 18)
| ((0x3f & cccc.charCodeAt(1)) << 12)
| ((0x3f & cccc.charCodeAt(2)) << 6)
| (0x3f & cccc.charCodeAt(3)),
offset = cp - 0x10000;
return (String.fromCharCode((offset >>> 10) + 0xD800)
+ String.fromCharCode((offset & 0x3FF) + 0xDC00));
case 3:
return String.fromCharCode(
((0x0f & cccc.charCodeAt(0)) << 12)
| ((0x3f & cccc.charCodeAt(1)) << 6)
| (0x3f & cccc.charCodeAt(2))
);
default:
return String.fromCharCode(
((0x1f & cccc.charCodeAt(0)) << 6)
| (0x3f & cccc.charCodeAt(1))
);
}
}

function btou(s) {
return s.replace(re_btou, cb_btou);
}

module.exports = XMLHttpRequest;

0 comments on commit a4486d6

Please sign in to comment.