forked from denoland/deno
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fetch.ts
141 lines (121 loc) · 3.34 KB
/
fetch.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2018 Ryan Dahl <[email protected]>
// All rights reserved. MIT License.
import { assert, log, createResolvable, Resolvable } from "./util";
import * as util from "./util";
import { pubInternal, sub } from "./dispatch";
import { main as pb } from "./msg.pb";
export function initFetch() {
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 decoder = new TextDecoder("utf-8");
return decoder.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 = pubInternal("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();
});
}