diff --git a/cmd/up.go b/cmd/up.go index 01abb8ff..5daf47a3 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -40,6 +40,7 @@ func NewCmdUp() *cobra.Command { } server := http.Server{ + Addr: addr, Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Host == k.String("domain") { target := r.URL diff --git a/worker/deno/fetch.ts b/worker/deno/fetch.ts deleted file mode 100644 index 7979cfc6..00000000 --- a/worker/deno/fetch.ts +++ /dev/null @@ -1,57 +0,0 @@ -const { port, entrypoint } = JSON.parse(Deno.args[0]); - -const server = Deno.serve( - { - port: parseInt(port), - onListen: () => { - // This line will signal that the server is ready to the go - console.log("READY"); - }, - }, - async (req) => { - // exit the server once the request will be handled - queueMicrotask(async () => { - await server.shutdown(); - }); - - try { - const mod = await import(entrypoint); - if (!mod.default || typeof mod.default !== "object") { - console.error("Mod does not export a default object"); - Deno.exit(1); - } - - const handler = mod.default; - if (!("fetch" in handler) || typeof handler.fetch !== "function") { - console.error("Mod has no fetch function"); - Deno.exit(1); - } - - const headers = new Headers(req.headers); - const url = req.headers.get("x-smallweb-url"); - if (!url) { - return new Response("Missing x-smallweb-url header", { status: 400 }); - } - headers.delete("x-smallweb-url"); - - const resp = await handler.fetch( - new Request(url, { - method: req.method, - headers, - body: req.body, - }), - ); - if (!(resp instanceof Response)) { - return new Response("Mod did not return a Response", { status: 500 }); - } - return resp; - } catch (e) { - if (e instanceof Error) { - console.error(e.stack); - return new Response(e.stack, { status: 500 }); - } - - return new Response("Unknown error", { status: 500 }); - } - }, -); diff --git a/worker/deno/run.ts b/worker/deno/run.ts deleted file mode 100644 index 6e5e5409..00000000 --- a/worker/deno/run.ts +++ /dev/null @@ -1,14 +0,0 @@ -const { entrypoint, args } = JSON.parse(Deno.args[0]); -const mod = await import(entrypoint); -if (!mod.default || typeof mod.default !== "object") { - console.error("Mod does not export a default object"); - Deno.exit(1); -} - -const handler = mod.default; -if (!("run" in handler) || typeof handler.run !== "function") { - console.error("Mod has no run function"); - Deno.exit(1); -} - -await handler.run(args); diff --git a/worker/sandbox.ts b/worker/sandbox.ts new file mode 100644 index 00000000..feea35d3 --- /dev/null +++ b/worker/sandbox.ts @@ -0,0 +1,84 @@ +const input = JSON.parse(Deno.args[0]); + +if (input.command === "serve") { + const { entrypoint, port } = input; + const server = Deno.serve( + { + port: parseInt(port), + onListen: () => { + // This line will signal that the server is ready to the go + console.log("READY"); + }, + }, + async (req) => { + // exit the server once the request will be handled + queueMicrotask(async () => { + await server.shutdown(); + }); + + try { + const mod = await import(entrypoint); + if (!mod.default || typeof mod.default !== "object") { + console.error("Mod does not export a default object"); + Deno.exit(1); + } + + const handler = mod.default; + if ( + !("fetch" in handler) || typeof handler.fetch !== "function" + ) { + console.error("Mod has no fetch function"); + Deno.exit(1); + } + + const headers = new Headers(req.headers); + const url = req.headers.get("x-smallweb-url"); + if (!url) { + return new Response("Missing x-smallweb-url header", { + status: 400, + }); + } + headers.delete("x-smallweb-url"); + + const resp = await handler.fetch( + new Request(url, { + method: req.method, + headers, + body: req.body, + }), + ); + if (!(resp instanceof Response)) { + return new Response("Mod did not return a Response", { + status: 500, + }); + } + return resp; + } catch (e) { + if (e instanceof Error) { + console.error(e.stack); + return new Response(e.stack, { status: 500 }); + } + + return new Response("Unknown error", { status: 500 }); + } + }, + ); +} else if (input.command === "run") { + const { entrypoint, args } = input; + const mod = await import(entrypoint); + if (!mod.default || typeof mod.default !== "object") { + console.error("Mod does not export a default object"); + Deno.exit(1); + } + + const handler = mod.default; + if (!("run" in handler) || typeof handler.run !== "function") { + console.error("Mod has no run function"); + Deno.exit(1); + } + + await handler.run(args); +} else { + console.error("Invalid command"); + Deno.exit(1); +} diff --git a/worker/worker.go b/worker/worker.go index b56036ec..a518911b 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -86,12 +86,12 @@ func (me *Worker) Root() string { var upgrader = websocket.Upgrader{} // use default options -func (me *Worker) Flags() []string { +func (me *Worker) Flags(sandboxPath string) []string { flags := []string{ "--allow-net", "--allow-env", "--allow-sys", - fmt.Sprintf("--allow-read=.,%s", me.Env["DENO_DIR"]), + fmt.Sprintf("--allow-read=.,%s,%s", me.Env["DENO_DIR"], sandboxPath), "--allow-write=.", fmt.Sprintf("--allow-run=%s", me.Env["SMALLWEB_EXEC_PATH"]), } @@ -274,8 +274,8 @@ func (me *Worker) Entrypoint() (string, error) { return "", fmt.Errorf("could not find entrypoint") } -//go:embed deno/fetch.ts -var fetchBytes []byte +//go:embed sandbox.ts +var sandboxBytes []byte func (me *Worker) Start() error { entrypoint, err := me.Entrypoint() @@ -295,19 +295,20 @@ func (me *Worker) Start() error { } defer os.Remove(tempfile.Name()) - if _, err := tempfile.Write(fetchBytes); err != nil { + if _, err := tempfile.Write(sandboxBytes); err != nil { return fmt.Errorf("could not write to temporary file: %w", err) } args := []string{"run"} - args = append(args, me.Flags()...) + args = append(args, me.Flags(tempfile.Name())...) input := strings.Builder{} encoder := json.NewEncoder(&input) encoder.SetEscapeHTML(false) encoder.Encode(map[string]any{ - "port": port, + "command": "serve", "entrypoint": entrypoint, + "port": port, }) args = append(args, tempfile.Name(), input.String()) @@ -511,9 +512,6 @@ func DenoExecutable() (string, error) { return "", fmt.Errorf("deno executable not found") } -//go:embed deno/run.ts -var runBytes []byte - func (me *Worker) Run(args []string) error { entrypoint, err := me.Entrypoint() if err != nil { @@ -534,17 +532,18 @@ func (me *Worker) Run(args []string) error { return fmt.Errorf("could not create temporary file: %w", err) } defer os.Remove(tempfile.Name()) - if _, err := tempfile.Write(runBytes); err != nil { + if _, err := tempfile.Write(sandboxBytes); err != nil { return err } denoArgs := []string{"run"} - denoArgs = append(denoArgs, me.Flags()...) + denoArgs = append(denoArgs, me.Flags(tempfile.Name())...) input := strings.Builder{} encoder := json.NewEncoder(&input) encoder.SetEscapeHTML(false) encoder.Encode(map[string]any{ + "command": "run", "entrypoint": entrypoint, "args": args, })