diff options
Diffstat (limited to 'fetch.ts')
-rw-r--r-- | fetch.ts | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/fetch.ts b/fetch.ts new file mode 100644 index 000000000..dac019048 --- /dev/null +++ b/fetch.ts @@ -0,0 +1,151 @@ +import { assert, log, createResolvable, Resolvable } from "./util"; +import * as util from "./util"; +import * as dispatch from "./dispatch"; +import { main as pb } from "./msg.pb"; +import { TextDecoder } from "text-encoding"; + +export function initFetch() { + dispatch.sub("fetch", (payload: Uint8Array) => { + const msg = pb.Msg.decode(payload); + assert(msg.command === pb.Msg.Command.FETCH_RES); + const id = msg.fetchResId; + const f = fetchRequests.get(id); + assert(f != null, `Couldn't find FetchRequest id ${id}`); + + f.onMsg(msg); + }); +} + +const fetchRequests = new Map<number, FetchRequest>(); + +class FetchResponse implements Response { + readonly url: string; + body: null; + bodyUsed = false; // TODO + status: number; + statusText = "FIXME"; // TODO + readonly type = "basic"; // TODO + redirected = false; // TODO + headers: null; // TODO + //private bodyChunks: Uint8Array[] = []; + private first = true; + + constructor(readonly req: FetchRequest) { + this.url = req.url; + } + + bodyWaiter: Resolvable<ArrayBuffer>; + arrayBuffer(): Promise<ArrayBuffer> { + this.bodyWaiter = createResolvable(); + return this.bodyWaiter; + } + + blob(): Promise<Blob> { + throw Error("not implemented"); + } + + formData(): Promise<FormData> { + throw Error("not implemented"); + } + + async json(): Promise<object> { + const text = await this.text(); + return JSON.parse(text); + } + + async text(): Promise<string> { + const ab = await this.arrayBuffer(); + const enc = new TextDecoder("utf-8"); + // Maybe new Uint8Array(ab) + return enc.decode(ab); + } + + get ok(): boolean { + return 200 <= this.status && this.status < 300; + } + + clone(): Response { + throw Error("not implemented"); + } + + onHeader: (res: Response) => void; + onError: (error: Error) => void; + + onMsg(msg: pb.Msg) { + if (msg.error !== null && msg.error !== "") { + //throw new Error(msg.error) + this.onError(new Error(msg.error)); + return; + } + + if (this.first) { + this.first = false; + this.status = msg.fetchResStatus; + this.onHeader(this); + } else { + // Body message. Assuming it all comes in one message now. + const ab = util.typedArrayToArrayBuffer(msg.fetchResBody); + this.bodyWaiter.resolve(ab); + } + } +} + +let nextFetchId = 0; +//TODO implements Request +class FetchRequest { + private readonly id: number; + response: FetchResponse; + constructor(readonly url: string) { + this.id = nextFetchId++; + fetchRequests.set(this.id, this); + this.response = new FetchResponse(this); + } + + onMsg(msg: pb.Msg) { + this.response.onMsg(msg); + } + + destroy() { + fetchRequests.delete(this.id); + } + + start() { + log("dispatch FETCH_REQ", this.id, this.url); + const res = dispatch.sendMsg("fetch", { + command: pb.Msg.Command.FETCH_REQ, + fetchReqId: this.id, + fetchReqUrl: this.url + }); + assert(res == null); + } +} + +export function fetch( + input?: Request | string, + init?: RequestInit +): Promise<Response> { + const fetchReq = new FetchRequest(input as string); + const response = fetchReq.response; + return new Promise((resolve, reject) => { + // tslint:disable-next-line:no-any + response.onHeader = (response: any) => { + log("onHeader"); + resolve(response); + }; + response.onError = (error: Error) => { + log("onError", error); + reject(error); + }; + fetchReq.start(); + }); +} + +/* +fetch('http://example.com/movies.json') + .then(function(response) { + return response.json(); + }) + .then(function(myJson) { + console.log(myJson); + }); + */ |