这是indexloc提供的服务,不要输入任何密码
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 63 additions & 99 deletions src/js/polyfills/fetch/body.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ function isDataView(obj) {
}

function consumed(body) {
if (body._noBody) {
if (body._bodySize===0) {
return;
}

Expand All @@ -14,48 +14,6 @@ function consumed(body) {
body.bodyUsed = true;
}

function fileReaderReady(reader) {
return new Promise(function(resolve, reject) {
reader.onload = function() {
resolve(reader.result);
};

reader.onerror = function() {
reject(reader.error);
};
});
}

function readBlobAsArrayBuffer(blob) {
var reader = new FileReader();
var promise = fileReaderReady(reader);

reader.readAsArrayBuffer(blob);

return promise;
}

function readBlobAsText(blob) {
var reader = new FileReader();
var promise = fileReaderReady(reader);
var match = /charset=([A-Za-z0-9_-]+)/.exec(blob.type);
var encoding = match ? match[1] : 'utf-8';

reader.readAsText(blob, encoding);

return promise;
}

function readArrayBufferAsText(buf) {
var view = new Uint8Array(buf);
var chars = new Array(view.length);

for (var i = 0; i < view.length; i++) {
chars[i] = String.fromCharCode(view[i]);
}

return chars.join('');
}

function bufferClone(buf) {
if (buf.slice) {
Expand All @@ -80,24 +38,50 @@ export const BodyMixin = {
this._bodyInit = body;

if (!body) {
this._noBody = true;
this._bodyText = '';
this._bodySize = 0;
this._bodyReadable = ReadableStream.from([ new Uint8Array(0) ]);
} else if (typeof body === 'string') {
this._bodyText = body;
const bodyBuffer=new TextEncoder().encode(body);

this._bodySize=bodyBuffer.byteLength;
this._bodyReadable = ReadableStream.from([ bodyBuffer ]);
} else if (isPrototypeOf(Blob.prototype, body)) {
this._bodyBlob = body;
this._bodySize=body.size;
this._bodyReadable = body.stream;
} else if (isPrototypeOf(FormData.prototype, body)) {
this._bodyFormData = body;
// formdata polyfill
const bodyBuffer = body['_blob']();

this._bodySize=bodyBuffer.byteLength;
this._bodyReadable = ReadableStream.from([ bodyBuffer ]);
} else if (isPrototypeOf(URLSearchParams.prototype, body)) {
this._bodyText = body.toString();
const textEncoder=new TextEncoder();
const bodyBuffer=textEncoder.encode(body);

this._bodySize=bodyBuffer.byteLength;
this._bodyReadable = ReadableStream.from([ textEncoder.encode(body.toString()) ]);
} else if (isDataView(body)) {
this._bodyArrayBuffer = bufferClone(body.buffer);
const bodyBuffer=new Uint8Array(bufferClone(body.buffer));

this._bodySize=bodyBuffer.byteLength;
this._bodyReadable = ReadableStream.from([ bodyBuffer ]);
} else if (isPrototypeOf(ArrayBuffer.prototype, body) || ArrayBuffer.isView(body)) {
this._bodyArrayBuffer = bufferClone(body);
const bodyBuffer=new Uint8Array(bufferClone(body.buffer));

this._bodySize=bodyBuffer.byteLength;
this._bodyReadable = ReadableStream.from([ bodyBuffer ]);
} else if (isPrototypeOf(ReadableStream.prototype, body)) {
this._bodySize=-1;
this._bodyReadable = body;
} else {
this._bodyText = body = Object.prototype.toString.call(body);
const bodyBuffer=new TextEncoder().encode(body.toString());

this._bodySize=bodyBuffer.byteLength;
this._bodyReadable = ReadableStream.from([ bodyBuffer ]);
}

this.body=this._bodyReadable;

if (!this.headers.get('content-type')) {
if (typeof body === 'string') {
this.headers.set('content-type', 'text/plain;charset=UTF-8');
Expand All @@ -109,70 +93,50 @@ export const BodyMixin = {
}
},

blob() {
async blob() {
const rejected = consumed(this);

if (rejected) {
return rejected;
await rejected;
}

if (this._bodyBlob) {
return Promise.resolve(this._bodyBlob);
} else if (this._bodyArrayBuffer) {
return Promise.resolve(new Blob([ this._bodyArrayBuffer ]));
} else if (this._bodyFormData) {
throw new Error('could not read FormData body as blob');
} else {
return Promise.resolve(new Blob([ this._bodyText ]));
if (!this._bodyReadable) {
throw new Error('Unknown body type');
}
},

arrayBuffer() {
if (this._bodyArrayBuffer) {
var isConsumed = consumed(this);

if (isConsumed) {
return isConsumed;
} else if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
return Promise.resolve(
this._bodyArrayBuffer.buffer.slice(
this._bodyArrayBuffer.byteOffset,
this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength
)
);
} else {
return Promise.resolve(this._bodyArrayBuffer);
const parts=[];
const reader=this._bodyReadable.getReader();

for (;;) {
const next=await reader.read();

if (next.done) {
break;
}
} else {
return this.blob().then(readBlobAsArrayBuffer);

parts.push(next.value);
}
},

text() {
const rejected = consumed(this);
return new Blob(parts);
},

if (rejected) {
return rejected;
}
async arrayBuffer() {
// TODO: expose Blob.parts to reduce memeory copy?
return await (await this.blob()).arrayBuffer();
},

if (this._bodyBlob) {
return readBlobAsText(this._bodyBlob);
} else if (this._bodyArrayBuffer) {
return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
} else if (this._bodyFormData) {
throw new Error('could not read FormData body as text');
} else {
return Promise.resolve(this._bodyText);
}
async text() {
return new TextDecoder().decode(await this.arrayBuffer());
},

formData() {
return this.text().then(decode);
async formData() {
return decode(await this.text());
},

json() {
return this.text().then(JSON.parse);
async json() {
return JSON.parse(await this.text());
},

};

function decode(body) {
Expand Down
79 changes: 58 additions & 21 deletions src/js/polyfills/fetch/fetch.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { kXhrGetAndClearResponseBuffer,kXhrOnTjsStreamSendData, kXhrStreamSend } from '../xhr.js';

import { Headers, normalizeName, normalizeValue } from './headers.js';
import { Request } from './request.js';

Expand All @@ -16,18 +18,25 @@ export function fetch(input, init) {
xhr.abort();
}

xhr.onload = function() {
const options = {
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders() || ''),
status: xhr.status,
url: xhr.responseURL
};
const body = xhr.response;
// tjs only
let controller=null;
const responseReader=new ReadableStream({
start(controller_) {
controller=controller_;
}
});

setTimeout(function() {
resolve(new Response(body, options));
}, 0);
xhr.onprogress=function() {
if (controller!==null) {
controller.enqueue(new Uint8Array(xhr[kXhrGetAndClearResponseBuffer]()));
}
};

xhr.onload=function() {
if (controller!==null) {
controller.enqueue(new Uint8Array(xhr[kXhrGetAndClearResponseBuffer]()));
controller.close();
}
};

xhr.onerror = function() {
Expand Down Expand Up @@ -77,19 +86,47 @@ export function fetch(input, init) {
});
}

if (request.signal) {
request.signal.addEventListener('abort', abortXhr);
xhr.onreadystatechange = function() {
// DONE (success or failure)
if (request.signal && xhr.readyState === xhr.DONE) {
request.signal.removeEventListener('abort', abortXhr);
}

xhr.onreadystatechange = function() {
// DONE (success or failure)
if (xhr.readyState === xhr.DONE) {
request.signal.removeEventListener('abort', abortXhr);
}
if (xhr.readyState === xhr.HEADERS_RECEIVED) {
const options = {
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders() || ''),
status: xhr.status,
url: xhr.responseURL
};

setTimeout(function() {
resolve(new Response(responseReader, options));
}, 0);
}
};

if (request._bodySize>0) {
request.arrayBuffer().then(body=>{
xhr.send(new Uint8Array(body));
});
} else if (request._bodySize===-1) {
const reader=request.body.getReader();

xhr[kXhrOnTjsStreamSendData]=function() {
reader.read().then(next=>{
if (next.done) {
xhr[kXhrStreamSend](null);
} else {
xhr[kXhrStreamSend](next.value);
}
});
};
}

// TODO: why not use the .body property?
xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
xhr[kXhrStreamSend]();
} else {
xhr.send(null);
}
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/js/polyfills/fetch/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export class Request {
this.mode = input.mode;
this.signal = input.signal;

if (!body && input._bodyInit !== null) {
body = input._bodyInit;
if (!body && input.body !== null) {
body = input.body;
input.bodyUsed = true;
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/js/polyfills/fetch/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class Response {
this.status = options.status === undefined ? 200 : options.status;

if (this.status < 200 || this.status > 599) {
throw new RangeError('The status provided (0) is outside the range [200, 599].');
throw new RangeError(`The status provided ${this.status} is outside the range [200, 599].`);
}

this.ok = this.status >= 200 && this.status < 300;
Expand Down
20 changes: 20 additions & 0 deletions src/js/polyfills/xhr.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ const core = globalThis[Symbol.for('tjs.internal.core')];
const XHR = core.XMLHttpRequest;
const kXHR = Symbol('kXHR');

export const kXhrGetAndClearResponseBuffer = Symbol('kXhrGetAndClearResponseBuffer');
export const kXhrOnTjsStreamSendData= Symbol('kXhrOnTjsStreamSendData');
export const kXhrStreamSend=Symbol('kXhrStreamSend');

class XMLHttpRequest extends EventTarget {
static UNSENT = XHR.UNSENT;
static OPENED = XHR.OPENED;
Expand All @@ -16,6 +20,8 @@ class XMLHttpRequest extends EventTarget {
LOADING = XHR.LOADING;
DONE = XHR.DONE;

[kXhrOnTjsStreamSendData]=null;

constructor() {
super();

Expand Down Expand Up @@ -53,6 +59,12 @@ class XMLHttpRequest extends EventTarget {
this.dispatchEvent(new Event('timeout'));
};

xhr.ontjsstreamsenddata = () => {
if (this[kXhrOnTjsStreamSendData]!==null) {
this[kXhrOnTjsStreamSendData]();
}
};

this[kXHR] = xhr;
}

Expand Down Expand Up @@ -175,6 +187,14 @@ class XMLHttpRequest extends EventTarget {
setRequestHeader(name, value) {
return this[kXHR].setRequestHeader(name, value);
}

[kXhrGetAndClearResponseBuffer]() {
return this[kXHR].__tjsGetAndClearResponseBuffer();
}

[kXhrStreamSend](buffer) {
return this[kXHR].__tjsStreamSend(buffer);
}
}

const xhrProto = XMLHttpRequest.prototype;
Expand Down
Loading
Loading