1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -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 }
): AsyncIterableIterator<FsEvent>;
export class Process {
export class Process<T extends RunOptions = RunOptions> {
readonly rid: number;
readonly pid: number;
readonly stdin?: Writer & Closer;
readonly stdout?: Reader & Closer;
readonly stderr?: Reader & Closer;
readonly stdin: T["stdin"] extends "piped" ? Writer & Closer : null;
readonly stdout: T["stdout"] extends "piped" ? Reader & Closer : null;
readonly stderr: T["stderr"] extends "piped" ? Reader & Closer : null;
/** Resolves to the current status of the process. */
status(): Promise<ProcessStatus>;
/** Buffer the stdout until EOF and return it as `Uint8Array`.
@ -1829,7 +1829,7 @@ declare namespace Deno {
* Details of the spawned process are returned.
*
* Requires `allow-run` permission. */
export function run(opt: RunOptions): Process;
export function run<T extends RunOptions = RunOptions>(opt: T): Process<T>;
interface InspectOptions {
depth?: number;

View file

@ -5,17 +5,15 @@ import { Closer, Reader, Writer } from "./io.ts";
import { readAll } from "./buffer.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'?
// See https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson
export interface RunOptions {
cmd: string[];
cwd?: string;
env?: { [key: string]: string };
stdout?: ProcessStdio | number;
stderr?: ProcessStdio | number;
stdin?: ProcessStdio | number;
stdout?: "inherit" | "piped" | "null" | number;
stderr?: "inherit" | "piped" | "null" | number;
stdin?: "inherit" | "piped" | "null" | number;
}
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 pid: number;
readonly stdin?: Writer & Closer;
readonly stdout?: Reader & Closer;
readonly stderr?: Reader & Closer;
readonly stdin!: T["stdin"] extends "piped" ? Writer & Closer : null;
readonly stdout!: T["stdout"] extends "piped" ? Reader & Closer : null;
readonly stderr!: T["stderr"] extends "piped" ? Reader & Closer : null;
// @internal
constructor(res: RunResponse) {
@ -43,15 +41,19 @@ export class Process {
this.pid = res.pid;
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) {
this.stdout = new File(res.stdoutRid);
this.stdout = (new File(res.stdoutRid) as unknown) as Process<
T
>["stdout"];
}
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> {
if (!this.stdout) {
throw new Error("Process.output: stdout is undefined");
throw new TypeError("stdout was not piped");
}
try {
return await readAll(this.stdout);
return await readAll(this.stdout as Reader & Closer);
} finally {
this.stdout.close();
(this.stdout as Reader & Closer).close();
}
}
async stderrOutput(): Promise<Uint8Array> {
if (!this.stderr) {
throw new Error("Process.stderrOutput: stderr is undefined");
throw new TypeError("stderr was not piped");
}
try {
return await readAll(this.stderr);
return await readAll(this.stderr as Reader & Closer);
} finally {
this.stderr.close();
(this.stderr as Reader & Closer).close();
}
}
@ -107,14 +109,15 @@ interface RunResponse {
stdoutRid: number | null;
stderrRid: number | null;
}
export function run({
export function run<T extends RunOptions = RunOptions>({
cmd,
cwd = undefined,
env = {},
stdout = "inherit",
stderr = "inherit",
stdin = "inherit",
}: RunOptions): Process {
}: T): Process<T> {
const res = runOp({
cmd: cmd.map(String),
cwd,
@ -126,5 +129,5 @@ export function run({
stdoutRid: isRid(stdout) ? stdout : 0,
stderrRid: isRid(stderr) ? stderr : 0,
}) 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.code, 0);
assertEquals(status.signal, undefined);
p.stdout!.close();
p.stdout.close();
p.close();
});
@ -141,7 +141,7 @@ unitTest({ perms: { run: true } }, async function runStdinPiped(): Promise<
const n = await p.stdin.write(msg);
assertEquals(n, msg.byteLength);
p.stdin!.close();
p.stdin.close();
const status = await p.status();
assertEquals(status.success, true);
@ -161,16 +161,16 @@ unitTest({ perms: { run: true } }, async function runStdoutPiped(): Promise<
assert(!p.stderr);
const data = new Uint8Array(10);
let r = await p.stdout!.read(data);
let r = await p.stdout.read(data);
if (r === null) {
throw new Error("p.stdout.read(...) should not be null");
}
assertEquals(r, 5);
const s = new TextDecoder().decode(data.subarray(0, r));
assertEquals(s, "hello");
r = await p.stdout!.read(data);
r = await p.stdout.read(data);
assertEquals(r, null);
p.stdout!.close();
p.stdout.close();
const status = await p.status();
assertEquals(status.success, true);
@ -190,14 +190,14 @@ unitTest({ perms: { run: true } }, async function runStderrPiped(): Promise<
assert(!p.stdout);
const data = new Uint8Array(10);
let r = await p.stderr!.read(data);
let r = await p.stderr.read(data);
if (r === null) {
throw new Error("p.stderr.read should not return null here");
}
assertEquals(r, 5);
const s = new TextDecoder().decode(data.subarray(0, r));
assertEquals(s, "hello");
r = await p.stderr!.read(data);
r = await p.stderr.read(data);
assertEquals(r, null);
p.stderr!.close();
@ -320,9 +320,9 @@ unitTest({ perms: { run: true } }, async function runClose(): Promise<void> {
p.close();
const data = new Uint8Array(10);
const r = await p.stderr!.read(data);
const r = await p.stderr.read(data);
assertEquals(r, null);
p.stderr!.close();
p.stderr.close();
});
unitTest(function signalNumbers(): void {

View file

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

View file

@ -17,7 +17,7 @@ Deno.test("[examples/catj] print an array", async () => {
assertStrictEquals(actual, expected);
} finally {
process.stdin!.close();
process.stdin.close();
process.close();
}
});
@ -36,7 +36,7 @@ Deno.test("[examples/catj] print an object", async () => {
assertStrictEquals(actual, expected);
} finally {
process.stdin!.close();
process.stdin.close();
process.close();
}
});
@ -54,7 +54,7 @@ Deno.test("[examples/catj] print multiple files", async () => {
assertStrictEquals(actual, expected);
} finally {
process.stdin!.close();
process.stdin.close();
process.close();
}
});
@ -64,8 +64,8 @@ Deno.test("[examples/catj] read from stdin", async () => {
const process = catj("-");
const input = `{ "foo": "bar" }`;
try {
await process.stdin!.write(new TextEncoder().encode(input));
process.stdin!.close();
await process.stdin.write(new TextEncoder().encode(input));
process.stdin.close();
const output = await process.output();
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({
cmd: [Deno.execPath(), "run", "--allow-read", "catj.ts", ...files],
cwd: "examples",

View file

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

View file

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

View file

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

View file

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