Skip to content

Commit

Permalink
Switch implementation to use sockets
Browse files Browse the repository at this point in the history
  • Loading branch information
maxmcd committed Apr 30, 2024
1 parent 41d9662 commit 2dc627e
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 206 deletions.
62 changes: 19 additions & 43 deletions deno-bootstrap/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
const scriptType = Deno.args[0];
const script = Deno.args[1];
const socketFile = Deno.args[0];
const scriptType = Deno.args[1];
const script = Deno.args[2];

const importURL =
scriptType == "import"
? script
: "data:text/tsx," + encodeURIComponent(script);

const server = Deno.listen({
hostname: "0.0.0.0",
port: 0,
});

const addr = server.addr as Deno.NetAddr;

console.log(`deno-listening-port ${addr.port.toString().padStart(5, " ")} `);

// Now that we're listening, start executing user-provided code. We could
// import while starting the server for a small performance improvement,
// but it would complicate reading the port from the Deno logs.
const handler = await import(importURL);
if (!handler.default) {
throw new Error("No default export found in script.");
Expand All @@ -26,32 +15,19 @@ if (typeof handler.default !== "function") {
throw new Error("Default export is not a function.");
}

const conn = await server.accept();
(async () => {
// Reject all additional connections.
for await (const conn of server) {
conn.close();
}
})();

// serveHttp is deprecated, but we don't have many other options if we'd like to
// keep this pattern of rejecting future connections at the TCP level.
// https://discord.com/channels/684898665143206084/1232398264947445810/1234614780111880303
//
// deno-lint-ignore no-deprecated-deno-api
const httpConn = Deno.serveHttp(conn);
for await (const requestEvent of httpConn) {
(async () => {
let req = requestEvent.request;
const url = new URL(req.url);
url.host = req.headers.get("X-Deno-Worker-Host") || url.host;
url.protocol = req.headers.get("X-Deno-Worker-Protocol") + ":";
url.port = req.headers.get("X-Deno-Worker-Port") || url.port;
req = new Request(url.toString(), req);
req.headers.delete("X-Deno-Worker-Host");
req.headers.delete("X-Deno-Worker-Protocol");
req.headers.delete("X-Deno-Worker-Port");

await requestEvent.respondWith(handler.default(req));
})();
}
Deno.serve({ path: socketFile }, (req: Request) => {
const url = new URL(req.url);
url.host = req.headers.get("X-Deno-Worker-Host") || url.host;
url.port = req.headers.get("X-Deno-Worker-Port") || url.port;
url.href = url.href.replace(
/^http\+unix:/,
req.headers.get("X-Deno-Worker-Protocol") || url.protocol
);
// Deno Request headers are immutable so we must make a new Request in order to delete our headers
req = new Request(url.toString(), req);
req.headers.delete("X-Deno-Worker-Host");
req.headers.delete("X-Deno-Worker-Protocol");
req.headers.delete("X-Deno-Worker-Port");

return handler.default(req);
});
6 changes: 6 additions & 0 deletions sockets/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import htt2 from "http2-wrapper";
import net from "net";

const client = htt2.connect("http://whatever", {
createConnection: () => net.connect("787562857674825-deno-http.sock"),
});
3 changes: 3 additions & 0 deletions sockets/deno.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Deno.serve({ path: "./socket.sock" }, async (r: Request) => {
return Response.json({ ok: true });
});
88 changes: 31 additions & 57 deletions src/DenoHTTPWorker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import fs from "fs";
import path from "path";

// Uncomment this if you want to debug serial test execution
const it = _it.concurrent;
// const it = _it
// const it = _it.concurrent;
const it = _it;

describe("DenoHTTPWorker", { timeout: 1000 }, () => {
const echoFile = path.resolve(__dirname, "./test/echo-request.ts");
Expand All @@ -14,21 +14,24 @@ describe("DenoHTTPWorker", { timeout: 1000 }, () => {
const vtScript = fs.readFileSync(vtFile, { encoding: "utf-8" });

it("json response multiple requests", async () => {
let worker = await newDenoHTTPWorker(`
let worker = await newDenoHTTPWorker(
`
export default async function (req: Request): Promise<Response> {
let headers = {};
for (let [key, value] of req.headers.entries()) {
headers[key] = value;
}
return Response.json({ ok: req.url, headers: headers })
}
`);
`,
{ printCommandAndArguments: true, printOutput: true }
);
for (let i = 0; i < 10; i++) {
let json = await worker.client
.get("https://localhost/", { headers: {} })
.get("https://localhost/hello?isee=you", { headers: {} })
.json();
expect(json).toEqual({
ok: "https://localhost/",
ok: "https://localhost/hello?isee=you",
headers: {
accept: "application/json",
"accept-encoding": "gzip, deflate, br",
Expand All @@ -53,24 +56,30 @@ describe("DenoHTTPWorker", { timeout: 1000 }, () => {
).rejects.toThrowError("with the address");
});

it("should be able to import script", async () => {
const file = path.resolve(__dirname, "./test/echo-request.ts");
const url = new URL(`file://${file}`);
let worker = await newDenoHTTPWorker(url, {
runFlags: [`--allow-read=${file}`],
printOutput: true,
});

let resp: any = await worker.client
.get("https://localhost/", {
headers: { "User-Agent": "some value" },
})
.json();
await worker.terminate();
});
// it("should be able to import script", async () => {
// const file = path.resolve(__dirname, "./test/echo-request.ts");
// const url = new URL(`file://${file}`);
// let worker = await newDenoHTTPWorker(url, {
// runFlags: [`--allow-read=${file}`],
// printOutput: true,
// });

// let resp: any = await worker.client
// .get("https://localhost/", {
// headers: { "User-Agent": "some value" },
// })
// .json();
// await worker.terminate();
// });

it("user agent is not overwritten", async () => {
let worker = await newDenoHTTPWorker(echoScript);
console.log(echoFile);
let worker = await newDenoHTTPWorker(echoScript, {
printCommandAndArguments: true,
printOutput: true,
runFlags: [`--unstable-http`],
});
console.log("making request");
let resp: any = await worker.client
.get("https://localhost/", {
headers: { "User-Agent": "some value" },
Expand Down Expand Up @@ -116,41 +125,6 @@ describe("DenoHTTPWorker", { timeout: 1000 }, () => {
worker.terminate();
});

it("port log is not in output", async () => {
let worker = await newDenoHTTPWorker(
`console.log("Hi, I am here");
export default async function (req: Request): Promise<Response> {
let body = await req.text();
return Response.json({ length: body.length })
}`
);
let allStdout = "";

worker.stdout.on("data", (data) => {
allStdout += data;
});

await worker.client("https://hey.ho").text();
worker.terminate();

expect(allStdout).toEqual("Hi, I am here\n");
});

it("cannot make outside connection to deno server", async () => {
let worker = await newDenoHTTPWorker(
`export default async function (req: Request): Promise<Response> {
let body = await req.text();
return Response.json({ length: body.length })
}`
);

await expect(
fetch("http://localhost:" + worker.denoListeningPort)
).rejects.toThrowError("fetch failed");

worker.terminate();
});

it("can implement val town", async () => {
let worker = await newDenoHTTPWorker(vtScript, { printOutput: true });

Expand Down
Loading

0 comments on commit 2dc627e

Please sign in to comment.