1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-22 15:06:54 -05:00

fix(cli/js/process): Strengthen socket types based on pipes (#4836)

This commit is contained in:
Nayeem Rahman 2020-06-09 12:18:18 +01:00 committed by GitHub
parent 54742d29dc
commit b3e189ee4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 61 additions and 54 deletions

View file

@ -1741,12 +1741,12 @@ declare namespace Deno {
options?: { recursive: boolean } options?: { recursive: boolean }
): AsyncIterableIterator<FsEvent>; ): AsyncIterableIterator<FsEvent>;
export class Process { export class Process<T extends RunOptions = RunOptions> {
readonly rid: number; readonly rid: number;
readonly pid: number; readonly pid: number;
readonly stdin?: Writer & Closer; readonly stdin: T["stdin"] extends "piped" ? Writer & Closer : null;
readonly stdout?: Reader & Closer; readonly stdout: T["stdout"] extends "piped" ? Reader & Closer : null;
readonly stderr?: Reader & Closer; readonly stderr: T["stderr"] extends "piped" ? Reader & Closer : null;
/** Resolves to the current status of the process. */ /** Resolves to the current status of the process. */
status(): Promise<ProcessStatus>; status(): Promise<ProcessStatus>;
/** Buffer the stdout until EOF and return it as `Uint8Array`. /** Buffer the stdout until EOF and return it as `Uint8Array`.
@ -1829,7 +1829,7 @@ declare namespace Deno {
* Details of the spawned process are returned. * Details of the spawned process are returned.
* *
* Requires `allow-run` permission. */ * Requires `allow-run` permission. */
export function run(opt: RunOptions): Process; export function run<T extends RunOptions = RunOptions>(opt: T): Process<T>;
interface InspectOptions { interface InspectOptions {
depth?: number; depth?: number;

View file

@ -5,17 +5,15 @@ import { Closer, Reader, Writer } from "./io.ts";
import { readAll } from "./buffer.ts"; import { readAll } from "./buffer.ts";
import { kill, runStatus as runStatusOp, run as runOp } from "./ops/process.ts"; import { kill, runStatus as runStatusOp, run as runOp } from "./ops/process.ts";
export type ProcessStdio = "inherit" | "piped" | "null";
// TODO Maybe extend VSCode's 'CommandOptions'? // TODO Maybe extend VSCode's 'CommandOptions'?
// See https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson // See https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson
export interface RunOptions { export interface RunOptions {
cmd: string[]; cmd: string[];
cwd?: string; cwd?: string;
env?: { [key: string]: string }; env?: { [key: string]: string };
stdout?: ProcessStdio | number; stdout?: "inherit" | "piped" | "null" | number;
stderr?: ProcessStdio | number; stderr?: "inherit" | "piped" | "null" | number;
stdin?: ProcessStdio | number; stdin?: "inherit" | "piped" | "null" | number;
} }
async function runStatus(rid: number): Promise<ProcessStatus> { async function runStatus(rid: number): Promise<ProcessStatus> {
@ -30,12 +28,12 @@ async function runStatus(rid: number): Promise<ProcessStatus> {
} }
} }
export class Process { export class Process<T extends RunOptions = RunOptions> {
readonly rid: number; readonly rid: number;
readonly pid: number; readonly pid: number;
readonly stdin?: Writer & Closer; readonly stdin!: T["stdin"] extends "piped" ? Writer & Closer : null;
readonly stdout?: Reader & Closer; readonly stdout!: T["stdout"] extends "piped" ? Reader & Closer : null;
readonly stderr?: Reader & Closer; readonly stderr!: T["stderr"] extends "piped" ? Reader & Closer : null;
// @internal // @internal
constructor(res: RunResponse) { constructor(res: RunResponse) {
@ -43,15 +41,19 @@ export class Process {
this.pid = res.pid; this.pid = res.pid;
if (res.stdinRid && res.stdinRid > 0) { if (res.stdinRid && res.stdinRid > 0) {
this.stdin = new File(res.stdinRid); this.stdin = (new File(res.stdinRid) as unknown) as Process<T>["stdin"];
} }
if (res.stdoutRid && res.stdoutRid > 0) { if (res.stdoutRid && res.stdoutRid > 0) {
this.stdout = new File(res.stdoutRid); this.stdout = (new File(res.stdoutRid) as unknown) as Process<
T
>["stdout"];
} }
if (res.stderrRid && res.stderrRid > 0) { if (res.stderrRid && res.stderrRid > 0) {
this.stderr = new File(res.stderrRid); this.stderr = (new File(res.stderrRid) as unknown) as Process<
T
>["stderr"];
} }
} }
@ -61,23 +63,23 @@ export class Process {
async output(): Promise<Uint8Array> { async output(): Promise<Uint8Array> {
if (!this.stdout) { if (!this.stdout) {
throw new Error("Process.output: stdout is undefined"); throw new TypeError("stdout was not piped");
} }
try { try {
return await readAll(this.stdout); return await readAll(this.stdout as Reader & Closer);
} finally { } finally {
this.stdout.close(); (this.stdout as Reader & Closer).close();
} }
} }
async stderrOutput(): Promise<Uint8Array> { async stderrOutput(): Promise<Uint8Array> {
if (!this.stderr) { if (!this.stderr) {
throw new Error("Process.stderrOutput: stderr is undefined"); throw new TypeError("stderr was not piped");
} }
try { try {
return await readAll(this.stderr); return await readAll(this.stderr as Reader & Closer);
} finally { } finally {
this.stderr.close(); (this.stderr as Reader & Closer).close();
} }
} }
@ -107,14 +109,15 @@ interface RunResponse {
stdoutRid: number | null; stdoutRid: number | null;
stderrRid: number | null; stderrRid: number | null;
} }
export function run({
export function run<T extends RunOptions = RunOptions>({
cmd, cmd,
cwd = undefined, cwd = undefined,
env = {}, env = {},
stdout = "inherit", stdout = "inherit",
stderr = "inherit", stderr = "inherit",
stdin = "inherit", stdin = "inherit",
}: RunOptions): Process { }: T): Process<T> {
const res = runOp({ const res = runOp({
cmd: cmd.map(String), cmd: cmd.map(String),
cwd, cwd,
@ -126,5 +129,5 @@ export function run({
stdoutRid: isRid(stdout) ? stdout : 0, stdoutRid: isRid(stdout) ? stdout : 0,
stderrRid: isRid(stderr) ? stderr : 0, stderrRid: isRid(stderr) ? stderr : 0,
}) as RunResponse; }) as RunResponse;
return new Process(res); return new Process<T>(res);
} }

View file

@ -36,7 +36,7 @@ unitTest({ perms: { run: true } }, async function runSuccess(): Promise<void> {
assertEquals(status.success, true); assertEquals(status.success, true);
assertEquals(status.code, 0); assertEquals(status.code, 0);
assertEquals(status.signal, undefined); assertEquals(status.signal, undefined);
p.stdout!.close(); p.stdout.close();
p.close(); p.close();
}); });
@ -141,7 +141,7 @@ unitTest({ perms: { run: true } }, async function runStdinPiped(): Promise<
const n = await p.stdin.write(msg); const n = await p.stdin.write(msg);
assertEquals(n, msg.byteLength); assertEquals(n, msg.byteLength);
p.stdin!.close(); p.stdin.close();
const status = await p.status(); const status = await p.status();
assertEquals(status.success, true); assertEquals(status.success, true);
@ -161,16 +161,16 @@ unitTest({ perms: { run: true } }, async function runStdoutPiped(): Promise<
assert(!p.stderr); assert(!p.stderr);
const data = new Uint8Array(10); const data = new Uint8Array(10);
let r = await p.stdout!.read(data); let r = await p.stdout.read(data);
if (r === null) { if (r === null) {
throw new Error("p.stdout.read(...) should not be null"); throw new Error("p.stdout.read(...) should not be null");
} }
assertEquals(r, 5); assertEquals(r, 5);
const s = new TextDecoder().decode(data.subarray(0, r)); const s = new TextDecoder().decode(data.subarray(0, r));
assertEquals(s, "hello"); assertEquals(s, "hello");
r = await p.stdout!.read(data); r = await p.stdout.read(data);
assertEquals(r, null); assertEquals(r, null);
p.stdout!.close(); p.stdout.close();
const status = await p.status(); const status = await p.status();
assertEquals(status.success, true); assertEquals(status.success, true);
@ -190,14 +190,14 @@ unitTest({ perms: { run: true } }, async function runStderrPiped(): Promise<
assert(!p.stdout); assert(!p.stdout);
const data = new Uint8Array(10); const data = new Uint8Array(10);
let r = await p.stderr!.read(data); let r = await p.stderr.read(data);
if (r === null) { if (r === null) {
throw new Error("p.stderr.read should not return null here"); throw new Error("p.stderr.read should not return null here");
} }
assertEquals(r, 5); assertEquals(r, 5);
const s = new TextDecoder().decode(data.subarray(0, r)); const s = new TextDecoder().decode(data.subarray(0, r));
assertEquals(s, "hello"); assertEquals(s, "hello");
r = await p.stderr!.read(data); r = await p.stderr.read(data);
assertEquals(r, null); assertEquals(r, null);
p.stderr!.close(); p.stderr!.close();
@ -320,9 +320,9 @@ unitTest({ perms: { run: true } }, async function runClose(): Promise<void> {
p.close(); p.close();
const data = new Uint8Array(10); const data = new Uint8Array(10);
const r = await p.stderr!.read(data); const r = await p.stderr.read(data);
assertEquals(r, null); assertEquals(r, null);
p.stderr!.close(); p.stderr.close();
}); });
unitTest(function signalNumbers(): void { unitTest(function signalNumbers(): void {

View file

@ -7,7 +7,9 @@ import { delay } from "../../async/delay.ts";
const { test } = Deno; const { test } = Deno;
async function startServer(): Promise<Deno.Process> { async function startServer(): Promise<
Deno.Process<Deno.RunOptions & { stdout: "piped" }>
> {
const server = Deno.run({ const server = Deno.run({
// TODO(lucacasonato): remove unstable once possible // TODO(lucacasonato): remove unstable once possible
cmd: [ cmd: [
@ -27,7 +29,7 @@ async function startServer(): Promise<Deno.Process> {
const s = await r.readLine(); const s = await r.readLine();
assert(s !== null && s.includes("chat server starting")); assert(s !== null && s.includes("chat server starting"));
} catch (err) { } catch (err) {
server.stdout!.close(); server.stdout.close();
server.close(); server.close();
} }
@ -46,7 +48,7 @@ test({
assert(html.includes("ws chat example"), "body is ok"); assert(html.includes("ws chat example"), "body is ok");
} finally { } finally {
server.close(); server.close();
server.stdout!.close(); server.stdout.close();
} }
await delay(10); await delay(10);
}, },
@ -66,7 +68,7 @@ test({
assertEquals((await it.next()).value, "[1]: Hello"); assertEquals((await it.next()).value, "[1]: Hello");
} finally { } finally {
server.close(); server.close();
server.stdout!.close(); server.stdout.close();
ws!.conn.close(); ws!.conn.close();
} }
}, },

View file

@ -17,7 +17,7 @@ Deno.test("[examples/catj] print an array", async () => {
assertStrictEquals(actual, expected); assertStrictEquals(actual, expected);
} finally { } finally {
process.stdin!.close(); process.stdin.close();
process.close(); process.close();
} }
}); });
@ -36,7 +36,7 @@ Deno.test("[examples/catj] print an object", async () => {
assertStrictEquals(actual, expected); assertStrictEquals(actual, expected);
} finally { } finally {
process.stdin!.close(); process.stdin.close();
process.close(); process.close();
} }
}); });
@ -54,7 +54,7 @@ Deno.test("[examples/catj] print multiple files", async () => {
assertStrictEquals(actual, expected); assertStrictEquals(actual, expected);
} finally { } finally {
process.stdin!.close(); process.stdin.close();
process.close(); process.close();
} }
}); });
@ -64,8 +64,8 @@ Deno.test("[examples/catj] read from stdin", async () => {
const process = catj("-"); const process = catj("-");
const input = `{ "foo": "bar" }`; const input = `{ "foo": "bar" }`;
try { try {
await process.stdin!.write(new TextEncoder().encode(input)); await process.stdin.write(new TextEncoder().encode(input));
process.stdin!.close(); process.stdin.close();
const output = await process.output(); const output = await process.output();
const actual = decoder.decode(output).trim(); const actual = decoder.decode(output).trim();
@ -75,7 +75,9 @@ Deno.test("[examples/catj] read from stdin", async () => {
} }
}); });
function catj(...files: string[]): Deno.Process { function catj(
...files: string[]
): Deno.Process<Deno.RunOptions & { stdin: "piped"; stdout: "piped" }> {
return Deno.run({ return Deno.run({
cmd: [Deno.execPath(), "run", "--allow-read", "catj.ts", ...files], cmd: [Deno.execPath(), "run", "--allow-read", "catj.ts", ...files],
cwd: "examples", cwd: "examples",

View file

@ -13,7 +13,7 @@ Deno.test("[examples/echo_server]", async () => {
let conn: Deno.Conn | undefined; let conn: Deno.Conn | undefined;
try { try {
const processReader = new BufReader(process.stdout!); const processReader = new BufReader(process.stdout);
const message = await processReader.readLine(); const message = await processReader.readLine();
assertNotEquals(message, null); assertNotEquals(message, null);
@ -38,7 +38,7 @@ Deno.test("[examples/echo_server]", async () => {
assertStrictEquals(actualResponse, expectedResponse); assertStrictEquals(actualResponse, expectedResponse);
} finally { } finally {
conn?.close(); conn?.close();
process.stdout!.close(); process.stdout.close();
process.close(); process.close();
} }
}); });

View file

@ -5,7 +5,7 @@ import { TextProtoReader } from "../textproto/mod.ts";
import { ServerRequest } from "./server.ts"; import { ServerRequest } from "./server.ts";
import { serveFile } from "./file_server.ts"; import { serveFile } from "./file_server.ts";
const { test } = Deno; const { test } = Deno;
let fileServer: Deno.Process; let fileServer: Deno.Process<Deno.RunOptions & { stdout: "piped" }>;
type FileServerCfg = { type FileServerCfg = {
target?: string; target?: string;

View file

@ -3,7 +3,7 @@ import { BufReader, BufWriter } from "../io/bufio.ts";
import { TextProtoReader } from "../textproto/mod.ts"; import { TextProtoReader } from "../textproto/mod.ts";
const { connect, run, test } = Deno; const { connect, run, test } = Deno;
let server: Deno.Process; let server: Deno.Process<Deno.RunOptions & { stdout: "piped" }>;
async function startServer(): Promise<void> { async function startServer(): Promise<void> {
server = run({ server = run({
// TODO(lucacasonato): remove unstable when stabilized // TODO(lucacasonato): remove unstable when stabilized
@ -18,7 +18,7 @@ async function startServer(): Promise<void> {
} }
function killServer(): void { function killServer(): void {
server.close(); server.close();
server.stdout?.close(); server.stdout.close();
} }
const input = [ const input = [

View file

@ -372,7 +372,7 @@ test({
.catch((_): void => {}); // Ignores the error when closing the process. .catch((_): void => {}); // Ignores the error when closing the process.
try { try {
const r = new TextProtoReader(new BufReader(p.stdout!)); const r = new TextProtoReader(new BufReader(p.stdout));
const s = await r.readLine(); const s = await r.readLine();
assert(s !== null && s.includes("server listening")); assert(s !== null && s.includes("server listening"));
await delay(100); await delay(100);
@ -387,7 +387,7 @@ test({
// Stops the sever and allows `p.status()` promise to resolve // Stops the sever and allows `p.status()` promise to resolve
Deno.kill(p.pid, Deno.Signal.SIGKILL); Deno.kill(p.pid, Deno.Signal.SIGKILL);
await statusPromise; await statusPromise;
p.stdout!.close(); p.stdout.close();
p.close(); p.close();
} }
}, },
@ -417,7 +417,7 @@ test({
.catch((_): void => {}); // Ignores the error when closing the process. .catch((_): void => {}); // Ignores the error when closing the process.
try { try {
const r = new TextProtoReader(new BufReader(p.stdout!)); const r = new TextProtoReader(new BufReader(p.stdout));
const s = await r.readLine(); const s = await r.readLine();
assert( assert(
s !== null && s.includes("server listening"), s !== null && s.includes("server listening"),
@ -444,7 +444,7 @@ test({
// Stops the sever and allows `p.status()` promise to resolve // Stops the sever and allows `p.status()` promise to resolve
Deno.kill(p.pid, Deno.Signal.SIGKILL); Deno.kill(p.pid, Deno.Signal.SIGKILL);
await statusPromise; await statusPromise;
p.stdout!.close(); p.stdout.close();
p.close(); p.close();
} }
}, },