mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
First pass at fetch()
With help from Thomas Ghysels <info@thomasg.be>
This commit is contained in:
parent
7b0618c20b
commit
868e8d8866
12 changed files with 659 additions and 40 deletions
2
BUILD.gn
2
BUILD.gn
|
@ -197,6 +197,8 @@ run_node("bundle") {
|
||||||
sources = [
|
sources = [
|
||||||
"js/assets.ts",
|
"js/assets.ts",
|
||||||
"js/console.ts",
|
"js/console.ts",
|
||||||
|
"js/fetch.ts",
|
||||||
|
"js/fetch_types.d.ts",
|
||||||
"js/globals.ts",
|
"js/globals.ts",
|
||||||
"js/lib.globals.d.ts",
|
"js/lib.globals.d.ts",
|
||||||
"js/main.ts",
|
"js/main.ts",
|
||||||
|
|
|
@ -11,6 +11,7 @@ import consoleDts from "gen/js/console.d.ts!string";
|
||||||
import denoDts from "gen/js/deno.d.ts!string";
|
import denoDts from "gen/js/deno.d.ts!string";
|
||||||
import globalsDts from "gen/js/globals.d.ts!string";
|
import globalsDts from "gen/js/globals.d.ts!string";
|
||||||
import osDts from "gen/js/os.d.ts!string";
|
import osDts from "gen/js/os.d.ts!string";
|
||||||
|
import fetchDts from "gen/js/fetch.d.ts!string";
|
||||||
import timersDts from "gen/js/timers.d.ts!string";
|
import timersDts from "gen/js/timers.d.ts!string";
|
||||||
import utilDts from "gen/js/util.d.ts!string";
|
import utilDts from "gen/js/util.d.ts!string";
|
||||||
|
|
||||||
|
@ -48,6 +49,7 @@ import libGlobalsDts from "/js/lib.globals.d.ts!string";
|
||||||
// Static definitions
|
// Static definitions
|
||||||
import typescriptDts from "/third_party/node_modules/typescript/lib/typescript.d.ts!string";
|
import typescriptDts from "/third_party/node_modules/typescript/lib/typescript.d.ts!string";
|
||||||
import typesDts from "/js/types.d.ts!string";
|
import typesDts from "/js/types.d.ts!string";
|
||||||
|
import fetchTypesDts from "/js/fetch_types.d.ts!string";
|
||||||
// tslint:enable:max-line-length
|
// tslint:enable:max-line-length
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
|
@ -57,6 +59,8 @@ export const assetSourceCode: { [key: string]: string } = {
|
||||||
"deno.d.ts": denoDts,
|
"deno.d.ts": denoDts,
|
||||||
"globals.d.ts": globalsDts,
|
"globals.d.ts": globalsDts,
|
||||||
"os.d.ts": osDts,
|
"os.d.ts": osDts,
|
||||||
|
"fetch.d.ts": fetchDts,
|
||||||
|
"fetch_types.d.ts": fetchTypesDts,
|
||||||
"timers.d.ts": timersDts,
|
"timers.d.ts": timersDts,
|
||||||
"util.d.ts": utilDts,
|
"util.d.ts": utilDts,
|
||||||
|
|
||||||
|
@ -94,4 +98,7 @@ export const assetSourceCode: { [key: string]: string } = {
|
||||||
// Static definitions
|
// Static definitions
|
||||||
"typescript.d.ts": typescriptDts,
|
"typescript.d.ts": typescriptDts,
|
||||||
"types.d.ts": typesDts,
|
"types.d.ts": typesDts,
|
||||||
|
|
||||||
|
// TODO Remove.
|
||||||
|
"msg_generated.d.ts": "",
|
||||||
};
|
};
|
||||||
|
|
|
@ -112,7 +112,8 @@ export class Console {
|
||||||
|
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
warn(...args: any[]): void {
|
warn(...args: any[]): void {
|
||||||
this.printFunc(`ERROR: ${stringifyArgs(args)}`);
|
// TODO Log to stderr.
|
||||||
|
this.printFunc(stringifyArgs(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
error = this.warn;
|
error = this.warn;
|
||||||
|
|
118
js/fetch.ts
118
js/fetch.ts
|
@ -7,42 +7,77 @@ import {
|
||||||
typedArrayToArrayBuffer,
|
typedArrayToArrayBuffer,
|
||||||
notImplemented,
|
notImplemented,
|
||||||
} from "./util";
|
} from "./util";
|
||||||
import { pubInternal, sub } from "./dispatch";
|
import { flatbuffers } from "flatbuffers";
|
||||||
import { deno as pb } from "./msg.pb";
|
import { libdeno } from "./globals";
|
||||||
|
import { deno as fbs } from "gen/msg_generated";
|
||||||
|
import {
|
||||||
|
Headers,
|
||||||
|
Request,
|
||||||
|
Response,
|
||||||
|
Blob,
|
||||||
|
RequestInit,
|
||||||
|
FormData
|
||||||
|
} from "./fetch_types";
|
||||||
|
import { TextDecoder } from "./text_encoding";
|
||||||
|
|
||||||
export function initFetch() {
|
/** @internal */
|
||||||
sub("fetch", (payload: Uint8Array) => {
|
export function onFetchRes(base: fbs.Base, msg: fbs.FetchRes) {
|
||||||
const msg = pb.Msg.decode(payload);
|
const id = msg.id();
|
||||||
assert(msg.command === pb.Msg.Command.FETCH_RES);
|
const req = fetchRequests.get(id);
|
||||||
const id = msg.fetchResId;
|
assert(req != null, `Couldn't find FetchRequest id ${id}`);
|
||||||
const f = fetchRequests.get(id);
|
req!.onMsg(base, msg);
|
||||||
assert(f != null, `Couldn't find FetchRequest id ${id}`);
|
|
||||||
|
|
||||||
f.onMsg(msg);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchRequests = new Map<number, FetchRequest>();
|
const fetchRequests = new Map<number, FetchRequest>();
|
||||||
|
|
||||||
|
class DenoHeaders implements Headers {
|
||||||
|
append(name: string, value: string): void {
|
||||||
|
assert(false, "Implement me");
|
||||||
|
}
|
||||||
|
delete(name: string): void {
|
||||||
|
assert(false, "Implement me");
|
||||||
|
}
|
||||||
|
get(name: string): string | null {
|
||||||
|
assert(false, "Implement me");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
has(name: string): boolean {
|
||||||
|
assert(false, "Implement me");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
set(name: string, value: string): void {
|
||||||
|
assert(false, "Implement me");
|
||||||
|
}
|
||||||
|
forEach(
|
||||||
|
callbackfn: (value: string, key: string, parent: Headers) => void,
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
thisArg?: any
|
||||||
|
): void {
|
||||||
|
assert(false, "Implement me");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class FetchResponse implements Response {
|
class FetchResponse implements Response {
|
||||||
readonly url: string;
|
readonly url: string;
|
||||||
body: null;
|
body: null;
|
||||||
bodyUsed = false; // TODO
|
bodyUsed = false; // TODO
|
||||||
status: number;
|
status = 0;
|
||||||
statusText = "FIXME"; // TODO
|
statusText = "FIXME"; // TODO
|
||||||
readonly type = "basic"; // TODO
|
readonly type = "basic"; // TODO
|
||||||
redirected = false; // TODO
|
redirected = false; // TODO
|
||||||
headers: null; // TODO
|
headers = new DenoHeaders();
|
||||||
|
readonly trailer: Promise<Headers>;
|
||||||
//private bodyChunks: Uint8Array[] = [];
|
//private bodyChunks: Uint8Array[] = [];
|
||||||
private first = true;
|
private first = true;
|
||||||
|
private bodyWaiter: Resolvable<ArrayBuffer>;
|
||||||
|
|
||||||
constructor(readonly req: FetchRequest) {
|
constructor(readonly req: FetchRequest) {
|
||||||
this.url = req.url;
|
this.url = req.url;
|
||||||
|
this.bodyWaiter = createResolvable();
|
||||||
|
this.trailer = createResolvable();
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyWaiter: Resolvable<ArrayBuffer>;
|
|
||||||
arrayBuffer(): Promise<ArrayBuffer> {
|
arrayBuffer(): Promise<ArrayBuffer> {
|
||||||
this.bodyWaiter = createResolvable();
|
|
||||||
return this.bodyWaiter;
|
return this.bodyWaiter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,23 +108,27 @@ class FetchResponse implements Response {
|
||||||
notImplemented();
|
notImplemented();
|
||||||
}
|
}
|
||||||
|
|
||||||
onHeader: (res: Response) => void;
|
onHeader?: (res: FetchResponse) => void;
|
||||||
onError: (error: Error) => void;
|
onError?: (error: Error) => void;
|
||||||
|
|
||||||
onMsg(msg: pb.Msg) {
|
onMsg(base: fbs.Base, msg: fbs.FetchRes) {
|
||||||
if (msg.error !== null && msg.error !== "") {
|
const error = base.error();
|
||||||
//throw new Error(msg.error)
|
if (error != null) {
|
||||||
this.onError(new Error(msg.error));
|
assert(this.onError != null);
|
||||||
|
this.onError!(new Error(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.first) {
|
if (this.first) {
|
||||||
this.first = false;
|
this.first = false;
|
||||||
this.status = msg.fetchResStatus;
|
this.status = msg.status();
|
||||||
this.onHeader(this);
|
assert(this.onHeader != null);
|
||||||
|
this.onHeader!(this);
|
||||||
} else {
|
} else {
|
||||||
// Body message. Assuming it all comes in one message now.
|
// Body message. Assuming it all comes in one message now.
|
||||||
const ab = typedArrayToArrayBuffer(msg.fetchResBody);
|
const bodyArray = msg.bodyArray();
|
||||||
|
assert(bodyArray != null);
|
||||||
|
const ab = typedArrayToArrayBuffer(bodyArray!);
|
||||||
this.bodyWaiter.resolve(ab);
|
this.bodyWaiter.resolve(ab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,8 +145,8 @@ class FetchRequest {
|
||||||
this.response = new FetchResponse(this);
|
this.response = new FetchResponse(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMsg(msg: pb.Msg) {
|
onMsg(base: fbs.Base, msg: fbs.FetchRes) {
|
||||||
this.response.onMsg(msg);
|
this.response.onMsg(base, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
@ -116,12 +155,22 @@ class FetchRequest {
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
log("dispatch FETCH_REQ", this.id, this.url);
|
log("dispatch FETCH_REQ", this.id, this.url);
|
||||||
const res = pubInternal("fetch", {
|
|
||||||
command: pb.Msg.Command.FETCH_REQ,
|
// Send FetchReq message
|
||||||
fetchReqId: this.id,
|
const builder = new flatbuffers.Builder();
|
||||||
fetchReqUrl: this.url
|
const url = builder.createString(this.url);
|
||||||
});
|
fbs.FetchReq.startFetchReq(builder);
|
||||||
assert(res == null);
|
fbs.FetchReq.addId(builder, this.id);
|
||||||
|
fbs.FetchReq.addUrl(builder, url);
|
||||||
|
const msg = fbs.FetchReq.endFetchReq(builder);
|
||||||
|
fbs.Base.startBase(builder);
|
||||||
|
fbs.Base.addMsg(builder, msg);
|
||||||
|
fbs.Base.addMsgType(builder, fbs.Any.FetchReq);
|
||||||
|
builder.finish(fbs.Base.endBase(builder));
|
||||||
|
const resBuf = libdeno.send(builder.asUint8Array());
|
||||||
|
assert(resBuf == null);
|
||||||
|
|
||||||
|
//console.log("FetchReq sent", builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,8 +181,7 @@ export function fetch(
|
||||||
const fetchReq = new FetchRequest(input as string);
|
const fetchReq = new FetchRequest(input as string);
|
||||||
const response = fetchReq.response;
|
const response = fetchReq.response;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// tslint:disable-next-line:no-any
|
response.onHeader = (response: FetchResponse) => {
|
||||||
response.onHeader = (response: any) => {
|
|
||||||
log("onHeader");
|
log("onHeader");
|
||||||
resolve(response);
|
resolve(response);
|
||||||
};
|
};
|
||||||
|
|
441
js/fetch_types.d.ts
vendored
Normal file
441
js/fetch_types.d.ts
vendored
Normal file
|
@ -0,0 +1,441 @@
|
||||||
|
/*! ****************************************************************************
|
||||||
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
||||||
|
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||||
|
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||||
|
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||||
|
|
||||||
|
See the Apache Version 2.0 License for specific language governing permissions
|
||||||
|
and limitations under the License.
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
type HeadersInit = Headers | string[][] | Record<string, string>;
|
||||||
|
type BodyInit =
|
||||||
|
| Blob
|
||||||
|
| BufferSource
|
||||||
|
| FormData
|
||||||
|
| URLSearchParams
|
||||||
|
| ReadableStream
|
||||||
|
| string;
|
||||||
|
type RequestInfo = Request | string;
|
||||||
|
type ReferrerPolicy =
|
||||||
|
| ""
|
||||||
|
| "no-referrer"
|
||||||
|
| "no-referrer-when-downgrade"
|
||||||
|
| "origin-only"
|
||||||
|
| "origin-when-cross-origin"
|
||||||
|
| "unsafe-url";
|
||||||
|
type BlobPart = BufferSource | Blob | string;
|
||||||
|
declare type EventListenerOrEventListenerObject =
|
||||||
|
| EventListener
|
||||||
|
| EventListenerObject;
|
||||||
|
|
||||||
|
interface Element {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HTMLFormElement {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormDataEntryValue {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BlobPropertyBag {
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AbortSignalEventMap {
|
||||||
|
abort: ProgressEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventTarget {
|
||||||
|
addEventListener(
|
||||||
|
type: string,
|
||||||
|
listener: EventListenerOrEventListenerObject | null,
|
||||||
|
options?: boolean | AddEventListenerOptions
|
||||||
|
): void;
|
||||||
|
dispatchEvent(evt: Event): boolean;
|
||||||
|
removeEventListener(
|
||||||
|
type: string,
|
||||||
|
listener?: EventListenerOrEventListenerObject | null,
|
||||||
|
options?: EventListenerOptions | boolean
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProgressEventInit extends EventInit {
|
||||||
|
lengthComputable?: boolean;
|
||||||
|
loaded?: number;
|
||||||
|
total?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface URLSearchParams {
|
||||||
|
/**
|
||||||
|
* Appends a specified key/value pair as a new search parameter.
|
||||||
|
*/
|
||||||
|
append(name: string, value: string): void;
|
||||||
|
/**
|
||||||
|
* Deletes the given search parameter, and its associated value, from the list of all search parameters.
|
||||||
|
*/
|
||||||
|
delete(name: string): void;
|
||||||
|
/**
|
||||||
|
* Returns the first value associated to the given search parameter.
|
||||||
|
*/
|
||||||
|
get(name: string): string | null;
|
||||||
|
/**
|
||||||
|
* Returns all the values association with a given search parameter.
|
||||||
|
*/
|
||||||
|
getAll(name: string): string[];
|
||||||
|
/**
|
||||||
|
* Returns a Boolean indicating if such a search parameter exists.
|
||||||
|
*/
|
||||||
|
has(name: string): boolean;
|
||||||
|
/**
|
||||||
|
* Sets the value associated to a given search parameter to the given value. If there were several values, delete the others.
|
||||||
|
*/
|
||||||
|
set(name: string, value: string): void;
|
||||||
|
sort(): void;
|
||||||
|
forEach(
|
||||||
|
callbackfn: (value: string, key: string, parent: URLSearchParams) => void,
|
||||||
|
thisArg?: any
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventListener {
|
||||||
|
(evt: Event): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventInit {
|
||||||
|
bubbles?: boolean;
|
||||||
|
cancelable?: boolean;
|
||||||
|
composed?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Event {
|
||||||
|
readonly bubbles: boolean;
|
||||||
|
cancelBubble: boolean;
|
||||||
|
readonly cancelable: boolean;
|
||||||
|
readonly composed: boolean;
|
||||||
|
readonly currentTarget: EventTarget | null;
|
||||||
|
readonly defaultPrevented: boolean;
|
||||||
|
readonly eventPhase: number;
|
||||||
|
readonly isTrusted: boolean;
|
||||||
|
returnValue: boolean;
|
||||||
|
readonly srcElement: Element | null;
|
||||||
|
readonly target: EventTarget | null;
|
||||||
|
readonly timeStamp: number;
|
||||||
|
readonly type: string;
|
||||||
|
deepPath(): EventTarget[];
|
||||||
|
initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void;
|
||||||
|
preventDefault(): void;
|
||||||
|
stopImmediatePropagation(): void;
|
||||||
|
stopPropagation(): void;
|
||||||
|
readonly AT_TARGET: number;
|
||||||
|
readonly BUBBLING_PHASE: number;
|
||||||
|
readonly CAPTURING_PHASE: number;
|
||||||
|
readonly NONE: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProgressEvent extends Event {
|
||||||
|
readonly lengthComputable: boolean;
|
||||||
|
readonly loaded: number;
|
||||||
|
readonly total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var ProgressEvent: {
|
||||||
|
prototype: ProgressEvent;
|
||||||
|
new (type: string, eventInitDict?: ProgressEventInit): ProgressEvent;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface EventListenerOptions {
|
||||||
|
capture?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddEventListenerOptions extends EventListenerOptions {
|
||||||
|
once?: boolean;
|
||||||
|
passive?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AbortSignal extends EventTarget {
|
||||||
|
readonly aborted: boolean;
|
||||||
|
onabort: ((this: AbortSignal, ev: ProgressEvent) => any) | null;
|
||||||
|
addEventListener<K extends keyof AbortSignalEventMap>(
|
||||||
|
type: K,
|
||||||
|
listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any,
|
||||||
|
options?: boolean | AddEventListenerOptions
|
||||||
|
): void;
|
||||||
|
addEventListener(
|
||||||
|
type: string,
|
||||||
|
listener: EventListenerOrEventListenerObject,
|
||||||
|
options?: boolean | AddEventListenerOptions
|
||||||
|
): void;
|
||||||
|
removeEventListener<K extends keyof AbortSignalEventMap>(
|
||||||
|
type: K,
|
||||||
|
listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any,
|
||||||
|
options?: boolean | EventListenerOptions
|
||||||
|
): void;
|
||||||
|
removeEventListener(
|
||||||
|
type: string,
|
||||||
|
listener: EventListenerOrEventListenerObject,
|
||||||
|
options?: boolean | EventListenerOptions
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var AbortSignal: {
|
||||||
|
prototype: AbortSignal;
|
||||||
|
new (): AbortSignal;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ReadableStream {
|
||||||
|
readonly locked: boolean;
|
||||||
|
cancel(): Promise<void>;
|
||||||
|
getReader(): ReadableStreamReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var ReadableStream: {
|
||||||
|
prototype: ReadableStream;
|
||||||
|
new (): ReadableStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface EventListenerObject {
|
||||||
|
handleEvent(evt: Event): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReadableStreamReader {
|
||||||
|
cancel(): Promise<void>;
|
||||||
|
read(): Promise<any>;
|
||||||
|
releaseLock(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var ReadableStreamReader: {
|
||||||
|
prototype: ReadableStreamReader;
|
||||||
|
new (): ReadableStreamReader;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface FormData {
|
||||||
|
append(name: string, value: string | Blob, fileName?: string): void;
|
||||||
|
delete(name: string): void;
|
||||||
|
get(name: string): FormDataEntryValue | null;
|
||||||
|
getAll(name: string): FormDataEntryValue[];
|
||||||
|
has(name: string): boolean;
|
||||||
|
set(name: string, value: string | Blob, fileName?: string): void;
|
||||||
|
forEach(
|
||||||
|
callbackfn: (
|
||||||
|
value: FormDataEntryValue,
|
||||||
|
key: string,
|
||||||
|
parent: FormData
|
||||||
|
) => void,
|
||||||
|
thisArg?: any
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var FormData: {
|
||||||
|
prototype: FormData;
|
||||||
|
new (form?: HTMLFormElement): FormData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Blob {
|
||||||
|
readonly size: number;
|
||||||
|
readonly type: string;
|
||||||
|
slice(start?: number, end?: number, contentType?: string): Blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var Blob: {
|
||||||
|
prototype: Blob;
|
||||||
|
new (blobParts?: BlobPart[], options?: BlobPropertyBag): Blob;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Body {
|
||||||
|
readonly body: ReadableStream | null;
|
||||||
|
readonly bodyUsed: boolean;
|
||||||
|
arrayBuffer(): Promise<ArrayBuffer>;
|
||||||
|
blob(): Promise<Blob>;
|
||||||
|
formData(): Promise<FormData>;
|
||||||
|
json(): Promise<any>;
|
||||||
|
text(): Promise<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Headers {
|
||||||
|
append(name: string, value: string): void;
|
||||||
|
delete(name: string): void;
|
||||||
|
get(name: string): string | null;
|
||||||
|
has(name: string): boolean;
|
||||||
|
set(name: string, value: string): void;
|
||||||
|
forEach(
|
||||||
|
callbackfn: (value: string, key: string, parent: Headers) => void,
|
||||||
|
thisArg?: any
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var Headers: {
|
||||||
|
prototype: Headers;
|
||||||
|
new (init?: HeadersInit): Headers;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RequestCache =
|
||||||
|
| "default"
|
||||||
|
| "no-store"
|
||||||
|
| "reload"
|
||||||
|
| "no-cache"
|
||||||
|
| "force-cache"
|
||||||
|
| "only-if-cached";
|
||||||
|
type RequestCredentials = "omit" | "same-origin" | "include";
|
||||||
|
type RequestDestination =
|
||||||
|
| ""
|
||||||
|
| "audio"
|
||||||
|
| "audioworklet"
|
||||||
|
| "document"
|
||||||
|
| "embed"
|
||||||
|
| "font"
|
||||||
|
| "image"
|
||||||
|
| "manifest"
|
||||||
|
| "object"
|
||||||
|
| "paintworklet"
|
||||||
|
| "report"
|
||||||
|
| "script"
|
||||||
|
| "sharedworker"
|
||||||
|
| "style"
|
||||||
|
| "track"
|
||||||
|
| "video"
|
||||||
|
| "worker"
|
||||||
|
| "xslt";
|
||||||
|
type RequestMode = "navigate" | "same-origin" | "no-cors" | "cors";
|
||||||
|
type RequestRedirect = "follow" | "error" | "manual";
|
||||||
|
type ResponseType =
|
||||||
|
| "basic"
|
||||||
|
| "cors"
|
||||||
|
| "default"
|
||||||
|
| "error"
|
||||||
|
| "opaque"
|
||||||
|
| "opaqueredirect";
|
||||||
|
|
||||||
|
export interface RequestInit {
|
||||||
|
body?: BodyInit | null;
|
||||||
|
cache?: RequestCache;
|
||||||
|
credentials?: RequestCredentials;
|
||||||
|
headers?: HeadersInit;
|
||||||
|
integrity?: string;
|
||||||
|
keepalive?: boolean;
|
||||||
|
method?: string;
|
||||||
|
mode?: RequestMode;
|
||||||
|
redirect?: RequestRedirect;
|
||||||
|
referrer?: string;
|
||||||
|
referrerPolicy?: ReferrerPolicy;
|
||||||
|
signal?: AbortSignal | null;
|
||||||
|
window?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResponseInit {
|
||||||
|
headers?: HeadersInit;
|
||||||
|
status?: number;
|
||||||
|
statusText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Request extends Body {
|
||||||
|
/**
|
||||||
|
* Returns the cache mode associated with request, which is a string indicating
|
||||||
|
* how the the request will interact with the browser's cache when fetching.
|
||||||
|
*/
|
||||||
|
readonly cache: RequestCache;
|
||||||
|
/**
|
||||||
|
* Returns the credentials mode associated with request, which is a string
|
||||||
|
* indicating whether credentials will be sent with the request always, never, or only when sent to a
|
||||||
|
* same-origin URL.
|
||||||
|
*/
|
||||||
|
readonly credentials: RequestCredentials;
|
||||||
|
/**
|
||||||
|
* Returns the kind of resource requested by request, e.g., "document" or
|
||||||
|
* "script".
|
||||||
|
*/
|
||||||
|
readonly destination: RequestDestination;
|
||||||
|
/**
|
||||||
|
* Returns a Headers object consisting of the headers associated with request.
|
||||||
|
* Note that headers added in the network layer by the user agent will not be accounted for in this
|
||||||
|
* object, e.g., the "Host" header.
|
||||||
|
*/
|
||||||
|
readonly headers: Headers;
|
||||||
|
/**
|
||||||
|
* Returns request's subresource integrity metadata, which is a cryptographic hash of
|
||||||
|
* the resource being fetched. Its value consists of multiple hashes separated by whitespace. [SRI]
|
||||||
|
*/
|
||||||
|
readonly integrity: string;
|
||||||
|
/**
|
||||||
|
* Returns a boolean indicating whether or not request is for a history
|
||||||
|
* navigation (a.k.a. back-foward navigation).
|
||||||
|
*/
|
||||||
|
readonly isHistoryNavigation: boolean;
|
||||||
|
/**
|
||||||
|
* Returns a boolean indicating whether or not request is for a reload navigation.
|
||||||
|
*/
|
||||||
|
readonly isReloadNavigation: boolean;
|
||||||
|
/**
|
||||||
|
* Returns a boolean indicating whether or not request can outlive the global in which
|
||||||
|
* it was created.
|
||||||
|
*/
|
||||||
|
readonly keepalive: boolean;
|
||||||
|
/**
|
||||||
|
* Returns request's HTTP method, which is "GET" by default.
|
||||||
|
*/
|
||||||
|
readonly method: string;
|
||||||
|
/**
|
||||||
|
* Returns the mode associated with request, which is a string indicating
|
||||||
|
* whether the request will use CORS, or will be restricted to same-origin URLs.
|
||||||
|
*/
|
||||||
|
readonly mode: RequestMode;
|
||||||
|
/**
|
||||||
|
* Returns the redirect mode associated with request, which is a string
|
||||||
|
* indicating how redirects for the request will be handled during fetching. A request will follow redirects by default.
|
||||||
|
*/
|
||||||
|
readonly redirect: RequestRedirect;
|
||||||
|
/**
|
||||||
|
* Returns the referrer of request. Its value can be a same-origin URL if
|
||||||
|
* explicitly set in init, the empty string to indicate no referrer, and
|
||||||
|
* "about:client" when defaulting to the global's default. This is used during
|
||||||
|
* fetching to determine the value of the `Referer` header of the request being made.
|
||||||
|
*/
|
||||||
|
readonly referrer: string;
|
||||||
|
/**
|
||||||
|
* Returns the referrer policy associated with request. This is used during
|
||||||
|
* fetching to compute the value of the request's referrer.
|
||||||
|
*/
|
||||||
|
readonly referrerPolicy: ReferrerPolicy;
|
||||||
|
/**
|
||||||
|
* Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort
|
||||||
|
* event handler.
|
||||||
|
*/
|
||||||
|
readonly signal: AbortSignal;
|
||||||
|
/**
|
||||||
|
* Returns the URL of request as a string.
|
||||||
|
*/
|
||||||
|
readonly url: string;
|
||||||
|
clone(): Request;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var Request: {
|
||||||
|
prototype: Request;
|
||||||
|
new (input: RequestInfo, init?: RequestInit): Request;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Response extends Body {
|
||||||
|
readonly headers: Headers;
|
||||||
|
readonly ok: boolean;
|
||||||
|
readonly redirected: boolean;
|
||||||
|
readonly status: number;
|
||||||
|
readonly statusText: string;
|
||||||
|
readonly trailer: Promise<Headers>;
|
||||||
|
readonly type: ResponseType;
|
||||||
|
readonly url: string;
|
||||||
|
clone(): Response;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var Response: {
|
||||||
|
prototype: Response;
|
||||||
|
new (body?: BodyInit | null, init?: ResponseInit): Response;
|
||||||
|
error(): Response;
|
||||||
|
redirect(url: string, status?: number): Response;
|
||||||
|
};
|
|
@ -4,6 +4,7 @@ import { Console } from "./console";
|
||||||
import { RawSourceMap } from "./types";
|
import { RawSourceMap } from "./types";
|
||||||
import * as timers from "./timers";
|
import * as timers from "./timers";
|
||||||
import { TextEncoder, TextDecoder } from "./text_encoding";
|
import { TextEncoder, TextDecoder } from "./text_encoding";
|
||||||
|
import * as fetch_ from "./fetch";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
@ -18,6 +19,8 @@ declare global {
|
||||||
const console: Console;
|
const console: Console;
|
||||||
const window: Window;
|
const window: Window;
|
||||||
|
|
||||||
|
const fetch: typeof fetch_.fetch;
|
||||||
|
|
||||||
// tslint:disable:variable-name
|
// tslint:disable:variable-name
|
||||||
let TextEncoder: TextEncoder;
|
let TextEncoder: TextEncoder;
|
||||||
let TextDecoder: TextDecoder;
|
let TextDecoder: TextDecoder;
|
||||||
|
@ -58,5 +61,4 @@ window.console = new Console(libdeno.print);
|
||||||
window.TextEncoder = TextEncoder;
|
window.TextEncoder = TextEncoder;
|
||||||
window.TextDecoder = TextDecoder;
|
window.TextDecoder = TextDecoder;
|
||||||
|
|
||||||
// import { fetch } from "./fetch";
|
window.fetch = fetch_.fetch;
|
||||||
// window["fetch"] = fetch;
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import * as os from "./os";
|
||||||
import * as runtime from "./runtime";
|
import * as runtime from "./runtime";
|
||||||
import { libdeno } from "./globals";
|
import { libdeno } from "./globals";
|
||||||
import * as timers from "./timers";
|
import * as timers from "./timers";
|
||||||
|
import { onFetchRes } from "./fetch";
|
||||||
|
|
||||||
function startMsg(cmdId: number): Uint8Array {
|
function startMsg(cmdId: number): Uint8Array {
|
||||||
const builder = new flatbuffers.Builder();
|
const builder = new flatbuffers.Builder();
|
||||||
|
@ -24,6 +25,12 @@ function onMessage(ui8: Uint8Array) {
|
||||||
const bb = new flatbuffers.ByteBuffer(ui8);
|
const bb = new flatbuffers.ByteBuffer(ui8);
|
||||||
const base = fbs.Base.getRootAsBase(bb);
|
const base = fbs.Base.getRootAsBase(bb);
|
||||||
switch (base.msgType()) {
|
switch (base.msgType()) {
|
||||||
|
case fbs.Any.FetchRes: {
|
||||||
|
const msg = new fbs.FetchRes();
|
||||||
|
assert(base.msg(msg) != null);
|
||||||
|
onFetchRes(base, msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case fbs.Any.TimerReady: {
|
case fbs.Any.TimerReady: {
|
||||||
const msg = new fbs.TimerReady();
|
const msg = new fbs.TimerReady();
|
||||||
assert(base.msg(msg) != null);
|
assert(base.msg(msg) != null);
|
||||||
|
|
|
@ -92,13 +92,13 @@ test(async function tests_readFileSync() {
|
||||||
assertEqual(pkg.name, "deno");
|
assertEqual(pkg.name, "deno");
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
test(async function tests_fetch() {
|
test(async function tests_fetch() {
|
||||||
const response = await fetch("http://localhost:4545/package.json");
|
const response = await fetch("http://localhost:4545/package.json");
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
assertEqual(json.name, "deno");
|
assertEqual(json.name, "deno");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
test(async function tests_writeFileSync() {
|
test(async function tests_writeFileSync() {
|
||||||
const enc = new TextEncoder();
|
const enc = new TextEncoder();
|
||||||
const data = enc.encode("Hello");
|
const data = enc.encode("Hello");
|
||||||
|
|
|
@ -56,7 +56,7 @@ export interface ResolvableMethods<T> {
|
||||||
reject: (reason?: any) => void;
|
reject: (reason?: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Resolvable<T> = Promise<T> & ResolvableMethods<T>;
|
export type Resolvable<T> = Promise<T> & ResolvableMethods<T>;
|
||||||
|
|
||||||
export function createResolvable<T>(): Resolvable<T> {
|
export function createResolvable<T>(): Resolvable<T> {
|
||||||
let methods: ResolvableMethods<T>;
|
let methods: ResolvableMethods<T>;
|
||||||
|
|
|
@ -6,9 +6,13 @@ use from_c;
|
||||||
use fs;
|
use fs;
|
||||||
use futures;
|
use futures;
|
||||||
use futures::sync::oneshot;
|
use futures::sync::oneshot;
|
||||||
|
use hyper;
|
||||||
|
use hyper::rt::{Future, Stream};
|
||||||
|
use hyper::Client;
|
||||||
use msg_generated::deno as msg;
|
use msg_generated::deno as msg;
|
||||||
use std;
|
use std;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use tokio::prelude::future;
|
||||||
|
|
||||||
pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) {
|
pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) {
|
||||||
let bytes = unsafe { std::slice::from_raw_parts(buf.data_ptr, buf.data_len) };
|
let bytes = unsafe { std::slice::from_raw_parts(buf.data_ptr, buf.data_len) };
|
||||||
|
@ -33,6 +37,12 @@ pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) {
|
||||||
let output_code = msg.output_code().unwrap();
|
let output_code = msg.output_code().unwrap();
|
||||||
handle_code_cache(d, filename, source_code, output_code);
|
handle_code_cache(d, filename, source_code, output_code);
|
||||||
}
|
}
|
||||||
|
msg::Any::FetchReq => {
|
||||||
|
// TODO base.msg_as_FetchReq();
|
||||||
|
let msg = msg::FetchReq::init_from_table(base.msg().unwrap());
|
||||||
|
let url = msg.url().unwrap();
|
||||||
|
handle_fetch_req(d, msg.id(), url);
|
||||||
|
}
|
||||||
msg::Any::TimerStart => {
|
msg::Any::TimerStart => {
|
||||||
// TODO base.msg_as_TimerStart();
|
// TODO base.msg_as_TimerStart();
|
||||||
let msg = msg::TimerStart::init_from_table(base.msg().unwrap());
|
let msg = msg::TimerStart::init_from_table(base.msg().unwrap());
|
||||||
|
@ -210,6 +220,92 @@ fn handle_code_cache(
|
||||||
// null response indicates success.
|
// null response indicates success.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_fetch_req(d: *const DenoC, id: u32, url: &str) {
|
||||||
|
let deno = from_c(d);
|
||||||
|
let url = url.parse::<hyper::Uri>().unwrap();
|
||||||
|
let client = Client::new();
|
||||||
|
|
||||||
|
deno.rt.spawn(
|
||||||
|
client
|
||||||
|
.get(url)
|
||||||
|
.map(move |res| {
|
||||||
|
let status = res.status().as_u16() as i32;
|
||||||
|
|
||||||
|
// Send the first message without a body. This is just to indicate
|
||||||
|
// what status code.
|
||||||
|
let mut builder = flatbuffers::FlatBufferBuilder::new();
|
||||||
|
let msg = msg::FetchRes::create(
|
||||||
|
&mut builder,
|
||||||
|
&msg::FetchResArgs {
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
send_base(
|
||||||
|
d,
|
||||||
|
&mut builder,
|
||||||
|
&msg::BaseArgs {
|
||||||
|
msg: Some(flatbuffers::Offset::new(msg.value())),
|
||||||
|
msg_type: msg::Any::FetchRes,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
res
|
||||||
|
})
|
||||||
|
.and_then(move |res| {
|
||||||
|
// Send the body as a FetchRes message.
|
||||||
|
res.into_body().concat2().map(move |body_buffer| {
|
||||||
|
let mut builder = flatbuffers::FlatBufferBuilder::new();
|
||||||
|
let data_off = builder.create_byte_vector(body_buffer.as_ref());
|
||||||
|
let msg = msg::FetchRes::create(
|
||||||
|
&mut builder,
|
||||||
|
&msg::FetchResArgs {
|
||||||
|
id,
|
||||||
|
body: Some(data_off),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
send_base(
|
||||||
|
d,
|
||||||
|
&mut builder,
|
||||||
|
&msg::BaseArgs {
|
||||||
|
msg: Some(flatbuffers::Offset::new(msg.value())),
|
||||||
|
msg_type: msg::Any::FetchRes,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map_err(move |err| {
|
||||||
|
let errmsg = format!("{}", err);
|
||||||
|
|
||||||
|
// TODO This is obviously a lot of duplicated code from the success case.
|
||||||
|
// Leaving it here now jsut to get a first pass implementation, but this
|
||||||
|
// needs to be cleaned up.
|
||||||
|
let mut builder = flatbuffers::FlatBufferBuilder::new();
|
||||||
|
let err_off = builder.create_string(errmsg.as_str());
|
||||||
|
let msg = msg::FetchRes::create(
|
||||||
|
&mut builder,
|
||||||
|
&msg::FetchResArgs {
|
||||||
|
id,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
send_base(
|
||||||
|
d,
|
||||||
|
&mut builder,
|
||||||
|
&msg::BaseArgs {
|
||||||
|
msg: Some(flatbuffers::Offset::new(msg.value())),
|
||||||
|
msg_type: msg::Any::FetchRes,
|
||||||
|
error: Some(err_off),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn set_timeout<F>(
|
fn set_timeout<F>(
|
||||||
cb: F,
|
cb: F,
|
||||||
delay: u32,
|
delay: u32,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
extern crate flatbuffers;
|
extern crate flatbuffers;
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
|
extern crate hyper;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
extern crate msg_rs as msg_generated;
|
extern crate msg_rs as msg_generated;
|
||||||
extern crate sha1;
|
extern crate sha1;
|
||||||
|
|
14
tests/fetch_deps.ts
Normal file
14
tests/fetch_deps.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Run ./tools/http_server.py too in order for this test to run.
|
||||||
|
import { assert } from "../js/testing/util.ts";
|
||||||
|
|
||||||
|
// TODO Top level await https://github.com/denoland/deno/issues/471
|
||||||
|
async function main() {
|
||||||
|
const response = await fetch("http://localhost:4545/package.json");
|
||||||
|
const json = await response.json();
|
||||||
|
const deps = Object.keys(json.devDependencies);
|
||||||
|
console.log("Deno JS Deps");
|
||||||
|
console.log(deps.map(d => `* ${d}`).join("\n"));
|
||||||
|
assert(deps.includes("typescript"));
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
Loading…
Reference in a new issue