summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/fetch.ts106
-rw-r--r--js/fetch_test.ts26
2 files changed, 106 insertions, 26 deletions
diff --git a/js/fetch.ts b/js/fetch.ts
index d5f01b870..3659710c5 100644
--- a/js/fetch.ts
+++ b/js/fetch.ts
@@ -247,7 +247,7 @@ export class Response implements domTypes.Response {
readonly url: string = "";
statusText = "FIXME"; // TODO
readonly type = "basic"; // TODO
- redirected = false; // TODO
+ readonly redirected: boolean;
headers: domTypes.Headers;
readonly trailer: Promise<domTypes.Headers>;
bodyUsed = false;
@@ -257,6 +257,7 @@ export class Response implements domTypes.Response {
readonly status: number,
headersList: Array<[string, string]>,
rid: number,
+ redirected_: boolean,
body_: null | Body = null
) {
this.trailer = createResolvable();
@@ -268,6 +269,8 @@ export class Response implements domTypes.Response {
} else {
this.body = body_;
}
+
+ this.redirected = redirected_;
}
async arrayBuffer(): Promise<ArrayBuffer> {
@@ -308,7 +311,13 @@ export class Response implements domTypes.Response {
headersList.push(header);
}
- return new Response(this.status, headersList, -1, this.body);
+ return new Response(
+ this.status,
+ headersList,
+ -1,
+ this.redirected,
+ this.body
+ );
}
}
@@ -352,6 +361,29 @@ function deserializeHeaderFields(m: msg.HttpHeader): Array<[string, string]> {
return out;
}
+async function getFetchRes(
+ url: string,
+ method: string | null,
+ headers: domTypes.Headers | null,
+ body: ArrayBufferView | undefined
+): Promise<msg.FetchRes> {
+ // Send Fetch message
+ const builder = flatbuffers.createBuilder();
+ const headerOff = msgHttpRequest(builder, url, method, headers);
+ const resBase = await sendAsync(
+ builder,
+ msg.Any.Fetch,
+ msg.Fetch.createFetch(builder, headerOff),
+ body
+ );
+
+ // Decode FetchRes
+ assert(msg.Any.FetchRes === resBase.innerType());
+ const inner = new msg.FetchRes();
+ assert(resBase.inner(inner) != null);
+ return inner;
+}
+
/** Fetch a resource from the network. */
export async function fetch(
input: domTypes.Request | string,
@@ -361,6 +393,8 @@ export async function fetch(
let method: string | null = null;
let headers: domTypes.Headers | null = null;
let body: ArrayBufferView | undefined;
+ let redirected = false;
+ let remRedirectCount = 20; // TODO: use a better way to handle
if (typeof input === "string") {
url = input;
@@ -414,28 +448,48 @@ export async function fetch(
}
}
- // Send Fetch message
- const builder = flatbuffers.createBuilder();
- const headerOff = msgHttpRequest(builder, url, method, headers);
- const resBase = await sendAsync(
- builder,
- msg.Any.Fetch,
- msg.Fetch.createFetch(builder, headerOff),
- body
- );
-
- // Decode FetchRes
- assert(msg.Any.FetchRes === resBase.innerType());
- const inner = new msg.FetchRes();
- assert(resBase.inner(inner) != null);
-
- const header = inner.header()!;
- const bodyRid = inner.bodyRid();
- assert(!header.isRequest());
- const status = header.status();
-
- const headersList = deserializeHeaderFields(header);
-
- const response = new Response(status, headersList, bodyRid);
- return response;
+ while (remRedirectCount) {
+ const inner = await getFetchRes(url, method, headers, body);
+
+ const header = inner.header()!;
+ const bodyRid = inner.bodyRid();
+ assert(!header.isRequest());
+ const status = header.status();
+
+ const headersList = deserializeHeaderFields(header);
+
+ const response = new Response(status, headersList, bodyRid, redirected);
+ if ([301, 302, 303, 307, 308].includes(response.status)) {
+ // We're in a redirect status
+ switch ((init && init.redirect) || "follow") {
+ case "error":
+ throw notImplemented();
+ case "manual":
+ throw notImplemented();
+ case "follow":
+ default:
+ let redirectUrl = response.headers.get("Location");
+ if (redirectUrl == null) {
+ return response; // Unspecified
+ }
+ if (
+ !redirectUrl.startsWith("http://") &&
+ !redirectUrl.startsWith("https://")
+ ) {
+ redirectUrl =
+ url.split("//")[0] +
+ "//" +
+ url.split("//")[1].split("/")[0] +
+ redirectUrl; // TODO: handle relative redirection more gracefully
+ }
+ url = redirectUrl;
+ redirected = true;
+ remRedirectCount--;
+ }
+ } else {
+ return response;
+ }
+ }
+ // Return a network error due to too many redirections
+ throw notImplemented();
}
diff --git a/js/fetch_test.ts b/js/fetch_test.ts
index 032727dbc..2020716bf 100644
--- a/js/fetch_test.ts
+++ b/js/fetch_test.ts
@@ -99,6 +99,32 @@ testPerm(
}
);
+testPerm({ net: true }, async function fetchWithRedirection(): Promise<void> {
+ const response = await fetch("http://localhost:4546/"); // will redirect to http://localhost:4545/
+ assertEquals(response.status, 200);
+ const body = await response.text();
+ assert(body.includes("<title>Directory listing for /</title>"));
+});
+
+testPerm({ net: true }, async function fetchWithRelativeRedirection(): Promise<
+ void
+> {
+ const response = await fetch("http://localhost:4545/tests"); // will redirect to /tests/
+ assertEquals(response.status, 200);
+ const body = await response.text();
+ assert(body.includes("<title>Directory listing for /tests/</title>"));
+});
+
+// The feature below is not implemented, but the test should work after implementation
+/*
+testPerm({ net: true }, async function fetchWithInfRedirection(): Promise<
+ void
+> {
+ const response = await fetch("http://localhost:4549/tests"); // will redirect to the same place
+ assertEquals(response.status, 0); // network error
+});
+*/
+
testPerm({ net: true }, async function fetchInitStringBody(): Promise<void> {
const data = "Hello World";
const response = await fetch("http://localhost:4545/echo_server", {