2021-04-08 18:34:15 -04:00
|
|
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
|
|
import {
|
|
|
|
assert,
|
|
|
|
assertEquals,
|
|
|
|
assertThrowsAsync,
|
|
|
|
unitTest,
|
|
|
|
} from "./test_util.ts";
|
|
|
|
|
|
|
|
unitTest({ perms: { net: true } }, async function httpServerBasic() {
|
|
|
|
const promise = (async () => {
|
|
|
|
const listener = Deno.listen({ port: 4501 });
|
|
|
|
for await (const conn of listener) {
|
2021-04-09 11:54:27 -04:00
|
|
|
const httpConn = Deno.serveHttp(conn);
|
2021-04-08 18:34:15 -04:00
|
|
|
for await (const { request, respondWith } of httpConn) {
|
2021-04-19 11:07:44 -04:00
|
|
|
assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/");
|
2021-04-08 18:34:15 -04:00
|
|
|
assertEquals(await request.text(), "");
|
2021-04-12 10:24:45 -04:00
|
|
|
respondWith(new Response("Hello World", { headers: { "foo": "bar" } }));
|
2021-04-08 18:34:15 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
})();
|
|
|
|
|
|
|
|
const resp = await fetch("http://127.0.0.1:4501/", {
|
|
|
|
headers: { "connection": "close" },
|
|
|
|
});
|
|
|
|
const text = await resp.text();
|
|
|
|
assertEquals(text, "Hello World");
|
2021-04-12 10:24:45 -04:00
|
|
|
assertEquals(resp.headers.get("foo"), "bar");
|
2021-04-08 18:34:15 -04:00
|
|
|
await promise;
|
|
|
|
});
|
|
|
|
|
|
|
|
unitTest(
|
|
|
|
{ perms: { net: true } },
|
|
|
|
async function httpServerStreamResponse() {
|
|
|
|
const stream = new TransformStream();
|
|
|
|
const writer = stream.writable.getWriter();
|
|
|
|
writer.write(new TextEncoder().encode("hello "));
|
|
|
|
writer.write(new TextEncoder().encode("world"));
|
|
|
|
writer.close();
|
|
|
|
|
|
|
|
const promise = (async () => {
|
|
|
|
const listener = Deno.listen({ port: 4501 });
|
|
|
|
const conn = await listener.accept();
|
2021-04-09 11:54:27 -04:00
|
|
|
const httpConn = Deno.serveHttp(conn);
|
2021-04-08 18:34:15 -04:00
|
|
|
const evt = await httpConn.nextRequest();
|
|
|
|
assert(evt);
|
|
|
|
const { request, respondWith } = evt;
|
|
|
|
assert(!request.body);
|
|
|
|
await respondWith(new Response(stream.readable));
|
|
|
|
httpConn.close();
|
|
|
|
listener.close();
|
|
|
|
})();
|
|
|
|
|
|
|
|
const resp = await fetch("http://127.0.0.1:4501/");
|
|
|
|
const respBody = await resp.text();
|
|
|
|
assertEquals("hello world", respBody);
|
|
|
|
await promise;
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
unitTest(
|
|
|
|
{ perms: { net: true } },
|
|
|
|
async function httpServerStreamRequest() {
|
|
|
|
const stream = new TransformStream();
|
|
|
|
const writer = stream.writable.getWriter();
|
|
|
|
writer.write(new TextEncoder().encode("hello "));
|
|
|
|
writer.write(new TextEncoder().encode("world"));
|
|
|
|
writer.close();
|
|
|
|
|
|
|
|
const promise = (async () => {
|
|
|
|
const listener = Deno.listen({ port: 4501 });
|
|
|
|
const conn = await listener.accept();
|
2021-04-09 11:54:27 -04:00
|
|
|
const httpConn = Deno.serveHttp(conn);
|
2021-04-08 18:34:15 -04:00
|
|
|
const evt = await httpConn.nextRequest();
|
|
|
|
assert(evt);
|
|
|
|
const { request, respondWith } = evt;
|
|
|
|
const reqBody = await request.text();
|
|
|
|
assertEquals("hello world", reqBody);
|
|
|
|
await respondWith(new Response(""));
|
|
|
|
|
|
|
|
// TODO(ry) If we don't call httpConn.nextRequest() here we get "error sending
|
|
|
|
// request for url (https://localhost:4501/): connection closed before
|
|
|
|
// message completed".
|
|
|
|
assertEquals(await httpConn.nextRequest(), null);
|
|
|
|
|
|
|
|
listener.close();
|
|
|
|
})();
|
|
|
|
|
|
|
|
const resp = await fetch("http://127.0.0.1:4501/", {
|
|
|
|
body: stream.readable,
|
|
|
|
method: "POST",
|
|
|
|
headers: { "connection": "close" },
|
|
|
|
});
|
|
|
|
|
|
|
|
await resp.arrayBuffer();
|
|
|
|
await promise;
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
unitTest({ perms: { net: true } }, async function httpServerStreamDuplex() {
|
|
|
|
const promise = (async () => {
|
|
|
|
const listener = Deno.listen({ port: 4501 });
|
|
|
|
const conn = await listener.accept();
|
2021-04-09 11:54:27 -04:00
|
|
|
const httpConn = Deno.serveHttp(conn);
|
2021-04-08 18:34:15 -04:00
|
|
|
const evt = await httpConn.nextRequest();
|
|
|
|
assert(evt);
|
|
|
|
const { request, respondWith } = evt;
|
|
|
|
assert(request.body);
|
|
|
|
await respondWith(new Response(request.body));
|
|
|
|
httpConn.close();
|
|
|
|
listener.close();
|
|
|
|
})();
|
|
|
|
|
|
|
|
const ts = new TransformStream();
|
|
|
|
const writable = ts.writable.getWriter();
|
|
|
|
const resp = await fetch("http://127.0.0.1:4501/", {
|
|
|
|
method: "POST",
|
|
|
|
body: ts.readable,
|
|
|
|
});
|
|
|
|
assert(resp.body);
|
|
|
|
const reader = resp.body.getReader();
|
|
|
|
await writable.write(new Uint8Array([1]));
|
|
|
|
const chunk1 = await reader.read();
|
|
|
|
assert(!chunk1.done);
|
|
|
|
assertEquals(chunk1.value, new Uint8Array([1]));
|
|
|
|
await writable.write(new Uint8Array([2]));
|
|
|
|
const chunk2 = await reader.read();
|
|
|
|
assert(!chunk2.done);
|
|
|
|
assertEquals(chunk2.value, new Uint8Array([2]));
|
|
|
|
await writable.close();
|
|
|
|
const chunk3 = await reader.read();
|
|
|
|
assert(chunk3.done);
|
|
|
|
await promise;
|
|
|
|
});
|
|
|
|
|
|
|
|
unitTest({ perms: { net: true } }, async function httpServerClose() {
|
|
|
|
const listener = Deno.listen({ port: 4501 });
|
|
|
|
const client = await Deno.connect({ port: 4501 });
|
2021-04-09 11:54:27 -04:00
|
|
|
const httpConn = Deno.serveHttp(await listener.accept());
|
2021-04-08 18:34:15 -04:00
|
|
|
client.close();
|
|
|
|
const evt = await httpConn.nextRequest();
|
|
|
|
assertEquals(evt, null);
|
|
|
|
// Note httpConn is automatically closed when "done" is reached.
|
|
|
|
listener.close();
|
|
|
|
});
|
|
|
|
|
|
|
|
unitTest({ perms: { net: true } }, async function httpServerInvalidMethod() {
|
|
|
|
const listener = Deno.listen({ port: 4501 });
|
|
|
|
const client = await Deno.connect({ port: 4501 });
|
2021-04-09 11:54:27 -04:00
|
|
|
const httpConn = Deno.serveHttp(await listener.accept());
|
2021-04-08 18:34:15 -04:00
|
|
|
await client.write(new Uint8Array([1, 2, 3]));
|
|
|
|
await assertThrowsAsync(
|
|
|
|
async () => {
|
|
|
|
await httpConn.nextRequest();
|
|
|
|
},
|
|
|
|
Deno.errors.Http,
|
|
|
|
"invalid HTTP method parsed",
|
|
|
|
);
|
|
|
|
// Note httpConn is automatically closed when it errors.
|
|
|
|
client.close();
|
|
|
|
listener.close();
|
|
|
|
});
|
|
|
|
|
|
|
|
unitTest(
|
|
|
|
{ perms: { read: true, net: true } },
|
|
|
|
async function httpServerWithTls(): Promise<void> {
|
|
|
|
const hostname = "localhost";
|
|
|
|
const port = 4501;
|
|
|
|
|
|
|
|
const promise = (async () => {
|
|
|
|
const listener = Deno.listenTls({
|
|
|
|
hostname,
|
|
|
|
port,
|
|
|
|
certFile: "cli/tests/tls/localhost.crt",
|
|
|
|
keyFile: "cli/tests/tls/localhost.key",
|
|
|
|
});
|
|
|
|
const conn = await listener.accept();
|
2021-04-09 11:54:27 -04:00
|
|
|
const httpConn = Deno.serveHttp(conn);
|
2021-04-08 18:34:15 -04:00
|
|
|
const evt = await httpConn.nextRequest();
|
|
|
|
assert(evt);
|
2021-04-09 17:35:29 -04:00
|
|
|
const { respondWith } = evt;
|
2021-04-08 18:34:15 -04:00
|
|
|
await respondWith(new Response("Hello World"));
|
|
|
|
|
|
|
|
// TODO(ry) If we don't call httpConn.nextRequest() here we get "error sending
|
|
|
|
// request for url (https://localhost:4501/): connection closed before
|
|
|
|
// message completed".
|
|
|
|
assertEquals(await httpConn.nextRequest(), null);
|
|
|
|
|
|
|
|
listener.close();
|
|
|
|
})();
|
|
|
|
|
|
|
|
const caData = Deno.readTextFileSync("cli/tests/tls/RootCA.pem");
|
|
|
|
const client = Deno.createHttpClient({ caData });
|
|
|
|
const resp = await fetch(`https://${hostname}:${port}/`, {
|
|
|
|
client,
|
|
|
|
headers: { "connection": "close" },
|
|
|
|
});
|
|
|
|
const respBody = await resp.text();
|
|
|
|
assertEquals("Hello World", respBody);
|
|
|
|
await promise;
|
|
|
|
client.close();
|
|
|
|
},
|
|
|
|
);
|
2021-04-15 18:48:56 -04:00
|
|
|
|
|
|
|
unitTest(
|
|
|
|
{ perms: { net: true } },
|
|
|
|
async function httpServerRegressionHang() {
|
|
|
|
const promise = (async () => {
|
|
|
|
const listener = Deno.listen({ port: 4501 });
|
|
|
|
const conn = await listener.accept();
|
|
|
|
const httpConn = Deno.serveHttp(conn);
|
|
|
|
const event = await httpConn.nextRequest();
|
|
|
|
assert(event);
|
|
|
|
const { request, respondWith } = event;
|
|
|
|
const reqBody = await request.text();
|
|
|
|
assertEquals("request", reqBody);
|
|
|
|
await respondWith(new Response("response"));
|
|
|
|
httpConn.close();
|
|
|
|
listener.close();
|
|
|
|
})();
|
|
|
|
|
|
|
|
const resp = await fetch("http://127.0.0.1:4501/", {
|
|
|
|
method: "POST",
|
|
|
|
body: "request",
|
|
|
|
});
|
|
|
|
const respBody = await resp.text();
|
|
|
|
assertEquals("response", respBody);
|
|
|
|
await promise;
|
|
|
|
},
|
|
|
|
);
|
2021-04-23 06:34:04 -04:00
|
|
|
|
|
|
|
unitTest(
|
|
|
|
{ perms: { net: true } },
|
|
|
|
async function httpServerCancelBodyOnResponseFailure() {
|
|
|
|
const promise = (async () => {
|
|
|
|
const listener = Deno.listen({ port: 4501 });
|
|
|
|
const conn = await listener.accept();
|
|
|
|
const httpConn = Deno.serveHttp(conn);
|
|
|
|
const event = await httpConn.nextRequest();
|
|
|
|
assert(event);
|
|
|
|
const { respondWith } = event;
|
|
|
|
let cancelReason = null;
|
|
|
|
const responseError = await assertThrowsAsync(
|
|
|
|
async () => {
|
|
|
|
let interval = 0;
|
|
|
|
await respondWith(
|
|
|
|
new Response(
|
|
|
|
new ReadableStream({
|
|
|
|
start(controller) {
|
|
|
|
interval = setInterval(() => {
|
|
|
|
const message = `data: ${Date.now()}\n\n`;
|
|
|
|
controller.enqueue(new TextEncoder().encode(message));
|
|
|
|
}, 200);
|
|
|
|
},
|
|
|
|
cancel(reason) {
|
|
|
|
cancelReason = reason;
|
|
|
|
clearInterval(interval);
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
Deno.errors.Http,
|
|
|
|
);
|
|
|
|
assertEquals(cancelReason, responseError);
|
|
|
|
httpConn.close();
|
|
|
|
listener.close();
|
|
|
|
})();
|
|
|
|
|
|
|
|
const resp = await fetch("http://127.0.0.1:4501/");
|
|
|
|
await resp.body!.cancel();
|
|
|
|
await promise;
|
|
|
|
},
|
|
|
|
);
|
2021-05-19 08:39:52 -04:00
|
|
|
|
|
|
|
unitTest(
|
|
|
|
{ perms: { net: true } },
|
|
|
|
async function httpServerNextRequestErrorExposedInResponse() {
|
|
|
|
const promise = (async () => {
|
|
|
|
const listener = Deno.listen({ port: 4501 });
|
|
|
|
const conn = await listener.accept();
|
|
|
|
const httpConn = Deno.serveHttp(conn);
|
|
|
|
const event = await httpConn.nextRequest();
|
|
|
|
assert(event);
|
|
|
|
// Start polling for the next request before awaiting response.
|
|
|
|
const nextRequestPromise = httpConn.nextRequest();
|
|
|
|
const { respondWith } = event;
|
|
|
|
await assertThrowsAsync(
|
|
|
|
async () => {
|
|
|
|
let interval = 0;
|
|
|
|
await respondWith(
|
|
|
|
new Response(
|
|
|
|
new ReadableStream({
|
|
|
|
start(controller) {
|
|
|
|
interval = setInterval(() => {
|
|
|
|
const message = `data: ${Date.now()}\n\n`;
|
|
|
|
controller.enqueue(new TextEncoder().encode(message));
|
|
|
|
}, 200);
|
|
|
|
},
|
|
|
|
cancel() {
|
|
|
|
clearInterval(interval);
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
Deno.errors.Http,
|
|
|
|
"connection closed",
|
|
|
|
);
|
|
|
|
// The error from `op_http_request_next` reroutes to `respondWith()`.
|
|
|
|
assertEquals(await nextRequestPromise, null);
|
|
|
|
listener.close();
|
|
|
|
})();
|
|
|
|
|
|
|
|
const resp = await fetch("http://127.0.0.1:4501/");
|
|
|
|
await resp.body!.cancel();
|
|
|
|
await promise;
|
|
|
|
},
|
|
|
|
);
|