summaryrefslogtreecommitdiff
path: root/std/http/server_test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'std/http/server_test.ts')
-rw-r--r--std/http/server_test.ts784
1 files changed, 0 insertions, 784 deletions
diff --git a/std/http/server_test.ts b/std/http/server_test.ts
deleted file mode 100644
index 8a3be71c2..000000000
--- a/std/http/server_test.ts
+++ /dev/null
@@ -1,784 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Ported from
-// https://github.com/golang/go/blob/master/src/net/http/responsewrite_test.go
-
-import { TextProtoReader } from "../textproto/mod.ts";
-import {
- assert,
- assertEquals,
- assertMatch,
- assertStringIncludes,
- assertThrowsAsync,
-} from "../testing/asserts.ts";
-import {
- _parseAddrFromStr,
- Response,
- serve,
- Server,
- ServerRequest,
- serveTLS,
-} from "./server.ts";
-import { BufReader, BufWriter } from "../io/bufio.ts";
-import { delay } from "../async/delay.ts";
-import { decode, encode } from "../encoding/utf8.ts";
-import { mockConn } from "./_mock_conn.ts";
-import { dirname, fromFileUrl, join, resolve } from "../path/mod.ts";
-
-const moduleDir = dirname(fromFileUrl(import.meta.url));
-const testdataDir = resolve(moduleDir, "testdata");
-
-interface ResponseTest {
- response: Response;
- raw: string;
-}
-
-const responseTests: ResponseTest[] = [
- // Default response
- {
- response: {},
- raw: "HTTP/1.1 200 OK\r\n" + "content-length: 0" + "\r\n\r\n",
- },
- // Empty body with status
- {
- response: {
- status: 404,
- },
- raw: "HTTP/1.1 404 Not Found\r\n" + "content-length: 0" + "\r\n\r\n",
- },
- // HTTP/1.1, chunked coding; empty trailer; close
- {
- response: {
- status: 200,
- body: new Deno.Buffer(new TextEncoder().encode("abcdef")),
- },
-
- raw: "HTTP/1.1 200 OK\r\n" +
- "transfer-encoding: chunked\r\n\r\n" +
- "6\r\nabcdef\r\n0\r\n\r\n",
- },
-];
-
-Deno.test("responseWrite", async function (): Promise<void> {
- for (const testCase of responseTests) {
- const buf = new Deno.Buffer();
- const bufw = new BufWriter(buf);
- const request = new ServerRequest();
- request.w = bufw;
-
- request.conn = mockConn();
-
- await request.respond(testCase.response);
- assertEquals(new TextDecoder().decode(buf.bytes()), testCase.raw);
- await request.done;
- }
-});
-
-Deno.test("requestContentLength", function (): void {
- // Has content length
- {
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("content-length", "5");
- const buf = new Deno.Buffer(encode("Hello"));
- req.r = new BufReader(buf);
- assertEquals(req.contentLength, 5);
- }
- // No content length
- {
- const shortText = "Hello";
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("transfer-encoding", "chunked");
- let chunksData = "";
- let chunkOffset = 0;
- const maxChunkSize = 70;
- while (chunkOffset < shortText.length) {
- const chunkSize = Math.min(maxChunkSize, shortText.length - chunkOffset);
- chunksData += `${chunkSize.toString(16)}\r\n${
- shortText.substr(chunkOffset, chunkSize)
- }\r\n`;
- chunkOffset += chunkSize;
- }
- chunksData += "0\r\n\r\n";
- const buf = new Deno.Buffer(encode(chunksData));
- req.r = new BufReader(buf);
- assertEquals(req.contentLength, null);
- }
-});
-
-interface TotalReader extends Deno.Reader {
- total: number;
-}
-function totalReader(r: Deno.Reader): TotalReader {
- let _total = 0;
- async function read(p: Uint8Array): Promise<number | null> {
- const result = await r.read(p);
- if (typeof result === "number") {
- _total += result;
- }
- return result;
- }
- return {
- read,
- get total(): number {
- return _total;
- },
- };
-}
-Deno.test("requestBodyWithContentLength", async function (): Promise<void> {
- {
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("content-length", "5");
- const buf = new Deno.Buffer(encode("Hello"));
- req.r = new BufReader(buf);
- const body = decode(await Deno.readAll(req.body));
- assertEquals(body, "Hello");
- }
-
- // Larger than internal buf
- {
- const longText = "1234\n".repeat(1000);
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("Content-Length", "5000");
- const buf = new Deno.Buffer(encode(longText));
- req.r = new BufReader(buf);
- const body = decode(await Deno.readAll(req.body));
- assertEquals(body, longText);
- }
- // Handler ignored to consume body
-});
-Deno.test(
- "ServerRequest.finalize() should consume unread body / content-length",
- async () => {
- const text = "deno.land";
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("content-length", "" + text.length);
- const tr = totalReader(new Deno.Buffer(encode(text)));
- req.r = new BufReader(tr);
- req.w = new BufWriter(new Deno.Buffer());
- await req.respond({ status: 200, body: "ok" });
- assertEquals(tr.total, 0);
- await req.finalize();
- assertEquals(tr.total, text.length);
- },
-);
-Deno.test(
- "ServerRequest.finalize() should consume unread body / chunked, trailers",
- async () => {
- const text = [
- "5",
- "Hello",
- "4",
- "Deno",
- "0",
- "",
- "deno: land",
- "node: js",
- "",
- "",
- ].join("\r\n");
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("transfer-encoding", "chunked");
- req.headers.set("trailer", "deno,node");
- const body = encode(text);
- const tr = totalReader(new Deno.Buffer(body));
- req.r = new BufReader(tr);
- req.w = new BufWriter(new Deno.Buffer());
- await req.respond({ status: 200, body: "ok" });
- assertEquals(tr.total, 0);
- assertEquals(req.headers.has("trailer"), true);
- assertEquals(req.headers.has("deno"), false);
- assertEquals(req.headers.has("node"), false);
- await req.finalize();
- assertEquals(tr.total, body.byteLength);
- assertEquals(req.headers.has("trailer"), false);
- assertEquals(req.headers.get("deno"), "land");
- assertEquals(req.headers.get("node"), "js");
- },
-);
-Deno.test("requestBodyWithTransferEncoding", async function (): Promise<void> {
- {
- const shortText = "Hello";
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("transfer-encoding", "chunked");
- let chunksData = "";
- let chunkOffset = 0;
- const maxChunkSize = 70;
- while (chunkOffset < shortText.length) {
- const chunkSize = Math.min(maxChunkSize, shortText.length - chunkOffset);
- chunksData += `${chunkSize.toString(16)}\r\n${
- shortText.substr(chunkOffset, chunkSize)
- }\r\n`;
- chunkOffset += chunkSize;
- }
- chunksData += "0\r\n\r\n";
- const buf = new Deno.Buffer(encode(chunksData));
- req.r = new BufReader(buf);
- const body = decode(await Deno.readAll(req.body));
- assertEquals(body, shortText);
- }
-
- // Larger than internal buf
- {
- const longText = "1234\n".repeat(1000);
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("transfer-encoding", "chunked");
- let chunksData = "";
- let chunkOffset = 0;
- const maxChunkSize = 70;
- while (chunkOffset < longText.length) {
- const chunkSize = Math.min(maxChunkSize, longText.length - chunkOffset);
- chunksData += `${chunkSize.toString(16)}\r\n${
- longText.substr(chunkOffset, chunkSize)
- }\r\n`;
- chunkOffset += chunkSize;
- }
- chunksData += "0\r\n\r\n";
- const buf = new Deno.Buffer(encode(chunksData));
- req.r = new BufReader(buf);
- const body = decode(await Deno.readAll(req.body));
- assertEquals(body, longText);
- }
-});
-
-Deno.test("requestBodyReaderWithContentLength", async function (): Promise<
- void
-> {
- {
- const shortText = "Hello";
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("content-length", "" + shortText.length);
- const buf = new Deno.Buffer(encode(shortText));
- req.r = new BufReader(buf);
- const readBuf = new Uint8Array(6);
- let offset = 0;
- while (offset < shortText.length) {
- const nread = await req.body.read(readBuf);
- assert(nread !== null);
- const s = decode(readBuf.subarray(0, nread as number));
- assertEquals(shortText.substr(offset, nread as number), s);
- offset += nread as number;
- }
- const nread = await req.body.read(readBuf);
- assertEquals(nread, null);
- }
-
- // Larger than given buf
- {
- const longText = "1234\n".repeat(1000);
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("Content-Length", "5000");
- const buf = new Deno.Buffer(encode(longText));
- req.r = new BufReader(buf);
- const readBuf = new Uint8Array(1000);
- let offset = 0;
- while (offset < longText.length) {
- const nread = await req.body.read(readBuf);
- assert(nread !== null);
- const s = decode(readBuf.subarray(0, nread as number));
- assertEquals(longText.substr(offset, nread as number), s);
- offset += nread as number;
- }
- const nread = await req.body.read(readBuf);
- assertEquals(nread, null);
- }
-});
-
-Deno.test("requestBodyReaderWithTransferEncoding", async function (): Promise<
- void
-> {
- {
- const shortText = "Hello";
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("transfer-encoding", "chunked");
- let chunksData = "";
- let chunkOffset = 0;
- const maxChunkSize = 70;
- while (chunkOffset < shortText.length) {
- const chunkSize = Math.min(maxChunkSize, shortText.length - chunkOffset);
- chunksData += `${chunkSize.toString(16)}\r\n${
- shortText.substr(chunkOffset, chunkSize)
- }\r\n`;
- chunkOffset += chunkSize;
- }
- chunksData += "0\r\n\r\n";
- const buf = new Deno.Buffer(encode(chunksData));
- req.r = new BufReader(buf);
- const readBuf = new Uint8Array(6);
- let offset = 0;
- while (offset < shortText.length) {
- const nread = await req.body.read(readBuf);
- assert(nread !== null);
- const s = decode(readBuf.subarray(0, nread as number));
- assertEquals(shortText.substr(offset, nread as number), s);
- offset += nread as number;
- }
- const nread = await req.body.read(readBuf);
- assertEquals(nread, null);
- }
-
- // Larger than internal buf
- {
- const longText = "1234\n".repeat(1000);
- const req = new ServerRequest();
- req.headers = new Headers();
- req.headers.set("transfer-encoding", "chunked");
- let chunksData = "";
- let chunkOffset = 0;
- const maxChunkSize = 70;
- while (chunkOffset < longText.length) {
- const chunkSize = Math.min(maxChunkSize, longText.length - chunkOffset);
- chunksData += `${chunkSize.toString(16)}\r\n${
- longText.substr(chunkOffset, chunkSize)
- }\r\n`;
- chunkOffset += chunkSize;
- }
- chunksData += "0\r\n\r\n";
- const buf = new Deno.Buffer(encode(chunksData));
- req.r = new BufReader(buf);
- const readBuf = new Uint8Array(1000);
- let offset = 0;
- while (offset < longText.length) {
- const nread = await req.body.read(readBuf);
- assert(nread !== null);
- const s = decode(readBuf.subarray(0, nread as number));
- assertEquals(longText.substr(offset, nread as number), s);
- offset += nread as number;
- }
- const nread = await req.body.read(readBuf);
- assertEquals(nread, null);
- }
-});
-
-Deno.test({
- name: "destroyed connection",
- fn: async (): Promise<void> => {
- // Runs a simple server as another process
- const p = Deno.run({
- cmd: [
- Deno.execPath(),
- "run",
- "--quiet",
- "--allow-net",
- "testdata/simple_server.ts",
- ],
- cwd: moduleDir,
- stdout: "piped",
- });
-
- let serverIsRunning = true;
- const statusPromise = p
- .status()
- .then((): void => {
- serverIsRunning = false;
- })
- .catch((_): void => {}); // Ignores the error when closing the process.
-
- try {
- const r = new TextProtoReader(new BufReader(p.stdout));
- const s = await r.readLine();
- assert(s !== null && s.includes("server listening"));
- await delay(100);
- // Reqeusts to the server and immediately closes the connection
- const conn = await Deno.connect({ port: 4502 });
- await conn.write(new TextEncoder().encode("GET / HTTP/1.0\n\n"));
- conn.close();
- // Waits for the server to handle the above (broken) request
- await delay(100);
- assert(serverIsRunning);
- } finally {
- // Stops the sever and allows `p.status()` promise to resolve
- Deno.kill(p.pid, Deno.Signal.SIGKILL);
- await statusPromise;
- p.stdout.close();
- p.close();
- }
- },
-});
-
-Deno.test({
- name: "serveTLS",
- fn: async (): Promise<void> => {
- // Runs a simple server as another process
- const p = Deno.run({
- cmd: [
- Deno.execPath(),
- "run",
- "--quiet",
- "--allow-net",
- "--allow-read",
- "testdata/simple_https_server.ts",
- ],
- cwd: moduleDir,
- stdout: "piped",
- });
-
- let serverIsRunning = true;
- const statusPromise = p
- .status()
- .then((): void => {
- serverIsRunning = false;
- })
- .catch((_): void => {}); // Ignores the error when closing the process.
-
- try {
- const r = new TextProtoReader(new BufReader(p.stdout));
- const s = await r.readLine();
- assert(
- s !== null && s.includes("server listening"),
- "server must be started",
- );
- // Requests to the server and immediately closes the connection
- const conn = await Deno.connectTls({
- hostname: "localhost",
- port: 4503,
- certFile: join(testdataDir, "tls/RootCA.pem"),
- });
- await Deno.writeAll(
- conn,
- new TextEncoder().encode("GET / HTTP/1.0\r\n\r\n"),
- );
- const res = new Uint8Array(100);
- const nread = await conn.read(res);
- assert(nread !== null);
- conn.close();
- const resStr = new TextDecoder().decode(res.subarray(0, nread));
- assert(resStr.includes("Hello HTTPS"));
- assert(serverIsRunning);
- } finally {
- // Stops the sever and allows `p.status()` promise to resolve
- Deno.kill(p.pid, Deno.Signal.SIGKILL);
- await statusPromise;
- p.stdout.close();
- p.close();
- }
- },
-});
-
-Deno.test(
- "close server while iterating",
- async (): Promise<void> => {
- const server = serve(":8123");
- const nextWhileClosing = server[Symbol.asyncIterator]().next();
- server.close();
- assertEquals(await nextWhileClosing, { value: undefined, done: true });
-
- const nextAfterClosing = server[Symbol.asyncIterator]().next();
- assertEquals(await nextAfterClosing, { value: undefined, done: true });
- },
-);
-
-Deno.test({
- name: "[http] close server while connection is open",
- async fn(): Promise<void> {
- async function iteratorReq(server: Server): Promise<void> {
- for await (const req of server) {
- await req.respond({ body: new TextEncoder().encode(req.url) });
- }
- }
-
- const server = serve(":8123");
- const p = iteratorReq(server);
- const conn = await Deno.connect({ hostname: "127.0.0.1", port: 8123 });
- await Deno.writeAll(
- conn,
- new TextEncoder().encode("GET /hello HTTP/1.1\r\n\r\n"),
- );
- const res = new Uint8Array(100);
- const nread = await conn.read(res);
- assert(nread !== null);
- const resStr = new TextDecoder().decode(res.subarray(0, nread));
- assertStringIncludes(resStr, "/hello");
- server.close();
- await p;
- // Client connection should still be open, verify that
- // it's visible in resource table.
- const resources = Deno.resources();
- assertEquals(resources[conn.rid], "tcpStream");
- conn.close();
- },
-});
-
-Deno.test({
- name: "respond error closes connection",
- async fn(): Promise<void> {
- const serverRoutine = async (): Promise<void> => {
- const server = serve(":8124");
- for await (const req of server) {
- await assertThrowsAsync(async () => {
- await req.respond({
- status: 12345,
- body: new TextEncoder().encode("Hello World"),
- });
- }, Deno.errors.InvalidData);
- // The connection should be destroyed
- assert(!(req.conn.rid in Deno.resources()));
- server.close();
- }
- };
- const p = serverRoutine();
- const conn = await Deno.connect({
- hostname: "127.0.0.1",
- port: 8124,
- });
- await Deno.writeAll(
- conn,
- new TextEncoder().encode("GET / HTTP/1.1\r\n\r\n"),
- );
- conn.close();
- await p;
- },
-});
-
-Deno.test({
- name: "[http] request error gets 400 response",
- async fn(): Promise<void> {
- const server = serve(":8124");
- const entry = server[Symbol.asyncIterator]().next();
- const conn = await Deno.connect({
- hostname: "127.0.0.1",
- port: 8124,
- });
- await Deno.writeAll(
- conn,
- encode("GET / HTTP/1.1\r\nmalformedHeader\r\n\r\n\r\n\r\n"),
- );
- const responseString = decode(await Deno.readAll(conn));
- assertMatch(
- responseString,
- /^HTTP\/1\.1 400 Bad Request\r\ncontent-length: \d+\r\n\r\n.*\r\n\r\n$/ms,
- );
- conn.close();
- server.close();
- assert((await entry).done);
- },
-});
-
-Deno.test({
- name: "[http] finalizing invalid chunked data closes connection",
- async fn(): Promise<void> {
- const serverRoutine = async (): Promise<void> => {
- const server = serve(":8124");
- for await (const req of server) {
- await req.respond({ status: 200, body: "Hello, world!" });
- break;
- }
- server.close();
- };
- const p = serverRoutine();
- const conn = await Deno.connect({
- hostname: "127.0.0.1",
- port: 8124,
- });
- await Deno.writeAll(
- conn,
- encode(
- "PUT / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\nzzzzzzz\r\nhello",
- ),
- );
- await conn.closeWrite();
- const responseString = decode(await Deno.readAll(conn));
- assertEquals(
- responseString,
- "HTTP/1.1 200 OK\r\ncontent-length: 13\r\n\r\nHello, world!",
- );
- conn.close();
- await p;
- },
-});
-
-Deno.test({
- name: "[http] finalizing chunked unexpected EOF closes connection",
- async fn(): Promise<void> {
- const serverRoutine = async (): Promise<void> => {
- const server = serve(":8124");
- for await (const req of server) {
- await req.respond({ status: 200, body: "Hello, world!" });
- break;
- }
- server.close();
- };
- const p = serverRoutine();
- const conn = await Deno.connect({
- hostname: "127.0.0.1",
- port: 8124,
- });
- await Deno.writeAll(
- conn,
- encode("PUT / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHello"),
- );
- conn.closeWrite();
- const responseString = decode(await Deno.readAll(conn));
- assertEquals(
- responseString,
- "HTTP/1.1 200 OK\r\ncontent-length: 13\r\n\r\nHello, world!",
- );
- conn.close();
- await p;
- },
-});
-
-Deno.test({
- name:
- "[http] receiving bad request from a closed connection should not throw",
- async fn(): Promise<void> {
- const server = serve(":8124");
- const serverRoutine = async (): Promise<void> => {
- for await (const req of server) {
- await req.respond({ status: 200, body: "Hello, world!" });
- }
- };
- const p = serverRoutine();
- const conn = await Deno.connect({
- hostname: "127.0.0.1",
- port: 8124,
- });
- await Deno.writeAll(
- conn,
- encode([
- // A normal request is required:
- "GET / HTTP/1.1",
- "Host: localhost",
- "",
- // The bad request:
- "GET / HTTP/1.1",
- "Host: localhost",
- "INVALID!HEADER!",
- "",
- "",
- ].join("\r\n")),
- );
- // After sending the two requests, don't receive the reponses.
-
- // Closing the connection now.
- conn.close();
-
- // The server will write responses to the closed connection,
- // the first few `write()` calls will not throws, until the server received
- // the TCP RST. So we need the normal request before the bad request to
- // make the server do a few writes before it writes that `400` response.
-
- // Wait for server to handle requests.
- await delay(10);
-
- server.close();
- await p;
- },
-});
-
-Deno.test({
- name: "serveTLS Invalid Cert",
- fn: async (): Promise<void> => {
- async function iteratorReq(server: Server): Promise<void> {
- for await (const req of server) {
- await req.respond({ body: new TextEncoder().encode("Hello HTTPS") });
- }
- }
- const port = 9122;
- const tlsOptions = {
- hostname: "localhost",
- port,
- certFile: join(testdataDir, "tls/localhost.crt"),
- keyFile: join(testdataDir, "tls/localhost.key"),
- };
- const server = serveTLS(tlsOptions);
- const p = iteratorReq(server);
-
- try {
- // Invalid certificate, connection should throw
- // but should not crash the server
- assertThrowsAsync(
- () =>
- Deno.connectTls({
- hostname: "localhost",
- port,
- // certFile
- }),
- Deno.errors.InvalidData,
- );
-
- // Valid request after invalid
- const conn = await Deno.connectTls({
- hostname: "localhost",
- port,
- certFile: join(testdataDir, "tls/RootCA.pem"),
- });
-
- await Deno.writeAll(
- conn,
- new TextEncoder().encode("GET / HTTP/1.0\r\n\r\n"),
- );
- const res = new Uint8Array(100);
- const nread = await conn.read(res);
- assert(nread !== null);
- conn.close();
- const resStr = new TextDecoder().decode(res.subarray(0, nread));
- assert(resStr.includes("Hello HTTPS"));
- } finally {
- // Stops the sever and allows `p.status()` promise to resolve
- server.close();
- await p;
- }
- },
-});
-
-Deno.test({
- name: "server.serve() should be able to parse IPV4 address",
- fn: (): void => {
- const server = serve("127.0.0.1:8124");
- const expected = {
- hostname: "127.0.0.1",
- port: 8124,
- transport: "tcp",
- };
- assertEquals(server.listener.addr, expected);
- server.close();
- },
-});
-
-Deno.test({
- name: "server._parseAddrFromStr() should be able to parse IPV6 address",
- fn: (): void => {
- const addr = _parseAddrFromStr("[::1]:8124");
- const expected = {
- hostname: "[::1]",
- port: 8124,
- };
- assertEquals(addr, expected);
- },
-});
-
-Deno.test({
- name: "server.serve() should be able to parse IPV6 address",
- fn: (): void => {
- const server = serve("[::1]:8124");
- const expected = {
- hostname: "::1",
- port: 8124,
- transport: "tcp",
- };
- assertEquals(server.listener.addr, expected);
- server.close();
- },
-});
-
-Deno.test({
- name: "server._parseAddrFromStr() port 80",
- fn: (): void => {
- const addr = _parseAddrFromStr(":80");
- assertEquals(addr.port, 80);
- assertEquals(addr.hostname, "0.0.0.0");
- },
-});