1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 07:14:47 -05:00

feat: fetch() now handles redirects (#2561)

This commit is contained in:
Long(Tony) Lian 2019-06-24 06:34:09 -07:00 committed by Ryan Dahl
parent d82089ca35
commit 1d0d54247c
3 changed files with 110 additions and 24 deletions

View file

@ -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
);
while (remRedirectCount) {
const inner = await getFetchRes(url, method, headers, 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 header = inner.header()!;
const bodyRid = inner.bodyRid();
assert(!header.isRequest());
const status = header.status();
const headersList = deserializeHeaderFields(header);
const headersList = deserializeHeaderFields(header);
const response = new Response(status, headersList, bodyRid);
return response;
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();
}

View file

@ -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", {

View file

@ -16,6 +16,7 @@ PORT = 4545
REDIRECT_PORT = 4546
ANOTHER_REDIRECT_PORT = 4547
DOUBLE_REDIRECTS_PORT = 4548
INF_REDIRECTS_PORT = 4549
QUIET = '-v' not in sys.argv and '--verbose' not in sys.argv
@ -153,6 +154,11 @@ def double_redirects_server():
return base_redirect_server(DOUBLE_REDIRECTS_PORT, REDIRECT_PORT)
# redirect server that points to itself
def inf_redirects_server():
return base_redirect_server(INF_REDIRECTS_PORT, INF_REDIRECTS_PORT)
def start(s):
thread = Thread(target=s.serve_forever, kwargs={"poll_interval": 0.05})
thread.daemon = True
@ -175,7 +181,7 @@ def spawn():
def main():
servers = (server(), redirect_server(), another_redirect_server(),
double_redirects_server())
double_redirects_server(), inf_redirects_server())
try:
while all(s.thread.is_alive() for s in servers):
sleep(10)