From 46daa09ca0066e0a6d7b630d7cd6910f96b40d0c Mon Sep 17 00:00:00 2001 From: "Kevin (Kun) \"Kassimo\" Qian" Date: Thu, 4 Oct 2018 00:32:35 -0700 Subject: [PATCH 1/5] Start shutdown attempt --- js/net.ts | 31 ++++++++++++++++++----- js/net_test.ts | 58 ++++++++++++++++++++++++++++++++++++++++++- js/test_util.ts | 20 +++++++++++++++ js/testing/testing.ts | 9 +++---- src/msg.fbs | 6 +++++ src/ops.rs | 29 +++++++++++++++++++++- src/resources.rs | 15 ++++++++++- 7 files changed, 154 insertions(+), 14 deletions(-) diff --git a/js/net.ts b/js/net.ts index ccbd331ddc00f2..0026a721ae112f 100644 --- a/js/net.ts +++ b/js/net.ts @@ -12,7 +12,10 @@ export type Network = "tcp"; // export type Network = "tcp" | "tcp4" | "tcp6" | "unix" | "unixpacket"; // TODO Support finding network from Addr, see https://golang.org/pkg/net/#Addr -export type Addr = string; +export interface Addr { + network: Network; + address: string; +} /** A Listener is a generic network listener for stream-oriented protocols. */ export interface Listener { @@ -28,7 +31,11 @@ export interface Listener { } class ListenerImpl implements Listener { - constructor(readonly fd: number) {} + private addr_: Addr; + + constructor(readonly fd: number, network: Network, address: string) { + this.addr_ = Object.freeze({ network, address }); + } async accept(): Promise { const builder = new flatbuffers.Builder(); @@ -48,13 +55,15 @@ class ListenerImpl implements Listener { } addr(): Addr { - return notImplemented(); + return this.addr_; } } export interface Conn extends Reader, Writer, Closer { localAddr: string; remoteAddr: string; + closeRead(): void; + closeWrite(): void; } class ConnImpl implements Conn { @@ -81,7 +90,7 @@ class ConnImpl implements Conn { */ closeRead(): void { // TODO(ry) Connect to AsyncWrite::shutdown in resources.rs - return notImplemented(); + shutdown(this.fd, false); } /** closeWrite shuts down (shutdown(2)) the writing side of the TCP @@ -89,10 +98,20 @@ class ConnImpl implements Conn { */ closeWrite(): void { // TODO(ry) Connect to AsyncWrite::shutdown in resources.rs - return notImplemented(); + shutdown(this.fd, true); } } +function shutdown(fd: number, isWrite: boolean) { + const builder = new flatbuffers.Builder(); + msg.Shutdown.startShutdown(builder); + msg.Shutdown.addRid(builder, fd); + msg.Shutdown.addIsWrite(builder, isWrite); + const inner = msg.Shutdown.endShutdown(builder); + const baseRes = dispatch.sendSync(builder, msg.Any.Shutdown, inner); + assert(baseRes == null); +} + /** Listen announces on the local network address. * * The network must be "tcp", "tcp4", "tcp6", "unix" or "unixpacket". @@ -121,7 +140,7 @@ export function listen(network: Network, address: string): Listener { assert(msg.Any.ListenRes === baseRes!.innerType()); const res = new msg.ListenRes(); assert(baseRes!.inner(res) != null); - return new ListenerImpl(res.rid()); + return new ListenerImpl(res.rid(), network, address); } /** Dial connects to the address on the named network. diff --git a/js/net_test.ts b/js/net_test.ts index 0b6db7afa72c48..f5f41704c4ffde 100644 --- a/js/net_test.ts +++ b/js/net_test.ts @@ -1,7 +1,7 @@ // Copyright 2018 the Deno authors. All rights reserved. MIT license. import * as deno from "deno"; -import { testPerm, assert, assertEqual } from "./test_util.ts"; +import { testPerm, assert, assertEqual, deferred } from "./test_util.ts"; testPerm({ net: true }, function netListenClose() { const listener = deno.listen("tcp", "127.0.0.1:4500"); @@ -35,3 +35,59 @@ testPerm({ net: true }, async function netDialListen() { listener.close(); conn.close(); }); + +testPerm({ net: true }, async function netConnCloseRead() { + const addr = "127.0.0.1:4500"; + const listener = deno.listen("tcp", addr); + const closeDeferred = deferred(); + listener.accept().then(async conn => { + await conn.write(new Uint8Array([1, 2, 3])); + const buf = new Uint8Array(1024); + const readResult = await conn.read(buf); + assertEqual(3, readResult.nread); + assertEqual(4, buf[0]); + assertEqual(5, buf[1]); + assertEqual(6, buf[2]); + conn.close(); + closeDeferred.resolve(); + }); + const conn = await deno.dial("tcp", addr); + conn.closeRead(); // closing read + const buf = new Uint8Array(1024); + const readResult = await conn.read(buf); + assertEqual(0, readResult.nread); // TODO: no error? + assertEqual(true, readResult.eof); + await conn.write(new Uint8Array([4, 5, 6])); + await closeDeferred.promise; + listener.close(); + conn.close(); +}); + +testPerm({ net: true }, async function netConnCloseWrite() { + const addr = "127.0.0.1:4500"; + const listener = deno.listen("tcp", addr); + const closeDeferred = deferred(); + listener.accept().then(async conn => { + await conn.write(new Uint8Array([1, 2, 3])); + await closeDeferred.promise; + conn.close(); + }); + const conn = await deno.dial("tcp", addr); + conn.closeWrite(); // closing read + const buf = new Uint8Array(1024); + const readResult = await conn.read(buf); + assertEqual(3, readResult.nread); + assertEqual(1, buf[0]); + assertEqual(2, buf[1]); + assertEqual(3, buf[2]); + let err; + try { + await conn.write(new Uint8Array([1, 2, 3])); + } catch (e) { + err = e; + } + assert(!!err); // TODO: Broken Pipe? + closeDeferred.resolve(); + listener.close(); + conn.close(); +}); diff --git a/js/test_util.ts b/js/test_util.ts index 34a920d4733ba0..7fcde4464b0610 100644 --- a/js/test_util.ts +++ b/js/test_util.ts @@ -49,6 +49,26 @@ export function test(fn: testing.TestFunction) { testPerm({ write: false, net: false, env: false }, fn); } +interface Deferred { + promise: Promise; + resolve: Function; + reject: Function; +} + +export function deferred(): Deferred { + let resolve: Function | undefined; + let reject: Function | undefined; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { + promise, + resolve: resolve!, + reject: reject! + }; +} + test(function permSerialization() { for (const write of [true, false]) { for (const net of [true, false]) { diff --git a/js/testing/testing.ts b/js/testing/testing.ts index 64ecbb6bc1138b..ee2ab1456aeead 100644 --- a/js/testing/testing.ts +++ b/js/testing/testing.ts @@ -57,12 +57,11 @@ const FG_RED = "\x1b[31m"; const FG_GREEN = "\x1b[32m"; function red_failed() { - return FG_RED + "FAILED" + RESET + return FG_RED + "FAILED" + RESET; } - function green_ok() { - return FG_GREEN + "ok" + RESET + return FG_GREEN + "ok" + RESET; } async function runTests() { @@ -96,8 +95,8 @@ async function runTests() { const result = failed > 0 ? red_failed() : green_ok(); console.log( `\ntest result: ${result}. ${passed} passed; ${failed} failed; ` + - `${ignored} ignored; ${measured} measured; ${filtered} filtered out\n`); - + `${ignored} ignored; ${measured} measured; ${filtered} filtered out\n` + ); if (failed === 0) { // All good. diff --git a/src/msg.fbs b/src/msg.fbs index 9479d789280916..f2e0de600f7a29 100644 --- a/src/msg.fbs +++ b/src/msg.fbs @@ -35,6 +35,7 @@ union Any { Write, WriteRes, Close, + Shutdown, Listen, ListenRes, Accept, @@ -290,6 +291,11 @@ table Close { rid: int; } +table Shutdown { + rid: int; + is_write: bool; +} + table Listen { network: string; address: string; diff --git a/src/ops.rs b/src/ops.rs index 53163dfd491117..5c0ed1d333c2c0 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -22,7 +22,7 @@ use remove_dir_all::remove_dir_all; use resources; use std; use std::fs; -use std::net::SocketAddr; +use std::net::{Shutdown, SocketAddr}; #[cfg(any(unix))] use std::os::unix::fs::PermissionsExt; use std::path::Path; @@ -84,6 +84,7 @@ pub fn dispatch( msg::Any::Read => op_read, msg::Any::Write => op_write, msg::Any::Close => op_close, + msg::Any::Shutdown => op_shutdown, msg::Any::Remove => op_remove, msg::Any::ReadFile => op_read_file, msg::Any::ReadDir => op_read_dir, @@ -614,6 +615,32 @@ fn op_close( } } +fn op_shutdown( + _state: Arc, + base: &msg::Base, + data: &'static mut [u8], +) -> Box { + assert_eq!(data.len(), 0); + let inner = base.inner_as_shutdown().unwrap(); + let rid = inner.rid(); + let is_write = inner.is_write(); + match resources::lookup(rid) { + None => odd_future(errors::new( + errors::ErrorKind::BadFileDescriptor, + String::from("Bad File Descriptor"), + )), + Some(mut resource) => { + let shutdown_mode = if is_write { + Shutdown::Write + } else { + Shutdown::Read + }; + resource.shutdown_on(shutdown_mode); + ok_future(empty_buf()) + } + } +} + fn op_read( _state: Arc, base: &msg::Base, diff --git a/src/resources.rs b/src/resources.rs index 75bad04b7b3ee2..5ffe87e5c3f527 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -14,7 +14,7 @@ use std; use std::collections::HashMap; use std::io::Error; use std::io::{Read, Write}; -use std::net::SocketAddr; +use std::net::{Shutdown, SocketAddr}; use std::sync::atomic::AtomicIsize; use std::sync::atomic::Ordering; use std::sync::Mutex; @@ -79,6 +79,19 @@ impl Resource { let r = table.remove(&self.rid); assert!(r.is_some()); } + + // no collision with unimplemented shutdown + pub fn shutdown_on(&mut self, how: Shutdown) { + let mut table = RESOURCE_TABLE.lock().unwrap(); + let maybe_repr = table.get_mut(&self.rid); + match maybe_repr { + None => panic!("bad rid"), + Some(repr) => match repr { + Repr::TcpStream(ref mut f) => TcpStream::shutdown(f, how).unwrap(), + _ => panic!("Cannot shutdown"), + }, + } + } } impl Read for Resource { From 147a762c2feb7ba050988f66bca0ae6579e1bcd7 Mon Sep 17 00:00:00 2001 From: "Kevin (Kun) \"Kassimo\" Qian" Date: Thu, 4 Oct 2018 10:51:48 -0700 Subject: [PATCH 2/5] Add dup-closeRead/Write test; Revise fbs table; Handle err for shutdown --- js/net.ts | 16 +++++++++--- js/net_test.ts | 66 ++++++++++++++++++++++++++++++++++++++++++++---- src/msg.fbs | 2 +- src/ops.rs | 16 +++++++----- src/resources.rs | 8 ++++-- 5 files changed, 89 insertions(+), 19 deletions(-) diff --git a/js/net.ts b/js/net.ts index 0026a721ae112f..1014006701e6a3 100644 --- a/js/net.ts +++ b/js/net.ts @@ -90,7 +90,7 @@ class ConnImpl implements Conn { */ closeRead(): void { // TODO(ry) Connect to AsyncWrite::shutdown in resources.rs - shutdown(this.fd, false); + shutdown(this.fd, ShutdownMode.Read); } /** closeWrite shuts down (shutdown(2)) the writing side of the TCP @@ -98,15 +98,23 @@ class ConnImpl implements Conn { */ closeWrite(): void { // TODO(ry) Connect to AsyncWrite::shutdown in resources.rs - shutdown(this.fd, true); + shutdown(this.fd, ShutdownMode.Write); } } -function shutdown(fd: number, isWrite: boolean) { +enum ShutdownMode { + // See https://linux.die.net/man/2/shutdown + // Corresponding to SHUT_RD, SHUT_WR, SHUT_RDWR + Read = 0, + Write, + ReadWrite // unused +} + +function shutdown(fd: number, how: ShutdownMode) { const builder = new flatbuffers.Builder(); msg.Shutdown.startShutdown(builder); msg.Shutdown.addRid(builder, fd); - msg.Shutdown.addIsWrite(builder, isWrite); + msg.Shutdown.addHow(builder, how); const inner = msg.Shutdown.endShutdown(builder); const baseRes = dispatch.sendSync(builder, msg.Any.Shutdown, inner); assert(baseRes == null); diff --git a/js/net_test.ts b/js/net_test.ts index f5f41704c4ffde..be02ec24fa9683 100644 --- a/js/net_test.ts +++ b/js/net_test.ts @@ -9,7 +9,7 @@ testPerm({ net: true }, function netListenClose() { }); testPerm({ net: true }, async function netDialListen() { - let addr = "127.0.0.1:4500"; + const addr = "127.0.0.1:4500"; const listener = deno.listen("tcp", addr); listener.accept().then(async conn => { await conn.write(new Uint8Array([1, 2, 3])); @@ -55,14 +55,41 @@ testPerm({ net: true }, async function netConnCloseRead() { conn.closeRead(); // closing read const buf = new Uint8Array(1024); const readResult = await conn.read(buf); - assertEqual(0, readResult.nread); // TODO: no error? - assertEqual(true, readResult.eof); + assertEqual(0, readResult.nread); // No error, read nothing + assertEqual(true, readResult.eof); // with immediate EOF + // Ensure closeRead does not impact write await conn.write(new Uint8Array([4, 5, 6])); await closeDeferred.promise; listener.close(); conn.close(); }); +testPerm({ net: true }, async function netConnDupCloseReadFailure() { + const addr = "127.0.0.1:4500"; + const listener = deno.listen("tcp", addr); + const closeDeferred = deferred(); + listener.accept().then(async conn => { + await conn.write(new Uint8Array([1, 2, 3])); + await closeDeferred.promise; + conn.close(); + }); + const conn = await deno.dial("tcp", addr); + conn.closeRead(); // closing read + let err; + try { + // Duplicated close should throw error + conn.closeRead(); + } catch (e) { + err = e; + } + assert(!!err); + assertEqual(err.kind, deno.ErrorKind.NotConnected); + assertEqual(err.name, "NotConnected"); + closeDeferred.resolve(); + listener.close(); + conn.close(); +}); + testPerm({ net: true }, async function netConnCloseWrite() { const addr = "127.0.0.1:4500"; const listener = deno.listen("tcp", addr); @@ -73,20 +100,49 @@ testPerm({ net: true }, async function netConnCloseWrite() { conn.close(); }); const conn = await deno.dial("tcp", addr); - conn.closeWrite(); // closing read + conn.closeWrite(); // closing write const buf = new Uint8Array(1024); + // Check read not impacted const readResult = await conn.read(buf); assertEqual(3, readResult.nread); assertEqual(1, buf[0]); assertEqual(2, buf[1]); assertEqual(3, buf[2]); + // Check write should be closed let err; try { await conn.write(new Uint8Array([1, 2, 3])); } catch (e) { err = e; } - assert(!!err); // TODO: Broken Pipe? + assert(!!err); + assertEqual(err.kind, deno.ErrorKind.BrokenPipe); + assertEqual(err.name, "BrokenPipe"); + closeDeferred.resolve(); + listener.close(); + conn.close(); +}); + +testPerm({ net: true }, async function netConnDupCloseWriteFailure() { + const addr = "127.0.0.1:4500"; + const listener = deno.listen("tcp", addr); + const closeDeferred = deferred(); + listener.accept().then(async conn => { + await closeDeferred.promise; + conn.close(); + }); + const conn = await deno.dial("tcp", addr); + conn.closeWrite(); // closing write + let err; + try { + // Duplicated close should throw error + conn.closeWrite(); + } catch (e) { + err = e; + } + assert(!!err); + assertEqual(err.kind, deno.ErrorKind.NotConnected); + assertEqual(err.name, "NotConnected"); closeDeferred.resolve(); listener.close(); conn.close(); diff --git a/src/msg.fbs b/src/msg.fbs index f2e0de600f7a29..16d10cdff0b638 100644 --- a/src/msg.fbs +++ b/src/msg.fbs @@ -293,7 +293,7 @@ table Close { table Shutdown { rid: int; - is_write: bool; + how: uint; } table Listen { diff --git a/src/ops.rs b/src/ops.rs index 5c0ed1d333c2c0..d29450405d0826 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -623,20 +623,22 @@ fn op_shutdown( assert_eq!(data.len(), 0); let inner = base.inner_as_shutdown().unwrap(); let rid = inner.rid(); - let is_write = inner.is_write(); + let how = inner.how(); match resources::lookup(rid) { None => odd_future(errors::new( errors::ErrorKind::BadFileDescriptor, String::from("Bad File Descriptor"), )), Some(mut resource) => { - let shutdown_mode = if is_write { - Shutdown::Write - } else { - Shutdown::Read + let shutdown_mode = match how { + 0 => Shutdown::Read, + 1 => Shutdown::Write, + _ => unimplemented!(), }; - resource.shutdown_on(shutdown_mode); - ok_future(empty_buf()) + blocking!(base.sync(), || { + resource.shutdown_on(shutdown_mode)?; + Ok(empty_buf()) + }) } } } diff --git a/src/resources.rs b/src/resources.rs index 5ffe87e5c3f527..866faf3bec51c3 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -8,6 +8,8 @@ // descriptors". This module implements a global resource table. Ops (AKA // handlers) look up resources by their integer id here. +use errors::DenoError; + use futures; use futures::Poll; use std; @@ -81,13 +83,15 @@ impl Resource { } // no collision with unimplemented shutdown - pub fn shutdown_on(&mut self, how: Shutdown) { + pub fn shutdown_on(&mut self, how: Shutdown) -> Result<(), DenoError> { let mut table = RESOURCE_TABLE.lock().unwrap(); let maybe_repr = table.get_mut(&self.rid); match maybe_repr { None => panic!("bad rid"), Some(repr) => match repr { - Repr::TcpStream(ref mut f) => TcpStream::shutdown(f, how).unwrap(), + Repr::TcpStream(ref mut f) => { + TcpStream::shutdown(f, how).map_err(|err| DenoError::from(err)) + } _ => panic!("Cannot shutdown"), }, } From 0ea6755d654fa179aa9f83a2e1422e0208d8479f Mon Sep 17 00:00:00 2001 From: "Kevin (Kun) \"Kassimo\" Qian" Date: Thu, 4 Oct 2018 10:56:28 -0700 Subject: [PATCH 3/5] Make link point to official man page --- js/net.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/net.ts b/js/net.ts index 1014006701e6a3..c90b931ded7a65 100644 --- a/js/net.ts +++ b/js/net.ts @@ -103,7 +103,7 @@ class ConnImpl implements Conn { } enum ShutdownMode { - // See https://linux.die.net/man/2/shutdown + // See http://man7.org/linux/man-pages/man2/shutdown.2.html // Corresponding to SHUT_RD, SHUT_WR, SHUT_RDWR Read = 0, Write, From 4f0663676c7593fc24dde7072054699dd4039ac9 Mon Sep 17 00:00:00 2001 From: "Kevin (Kun) \"Kassimo\" Qian" Date: Fri, 5 Oct 2018 08:31:33 -0700 Subject: [PATCH 4/5] Revert addr; rename --- js/net.ts | 15 ++++----------- js/net_test.ts | 8 ++++---- src/ops.rs | 6 ++++-- src/resources.rs | 3 +-- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/js/net.ts b/js/net.ts index c90b931ded7a65..29f7c843227b3a 100644 --- a/js/net.ts +++ b/js/net.ts @@ -12,10 +12,7 @@ export type Network = "tcp"; // export type Network = "tcp" | "tcp4" | "tcp6" | "unix" | "unixpacket"; // TODO Support finding network from Addr, see https://golang.org/pkg/net/#Addr -export interface Addr { - network: Network; - address: string; -} +export type Addr = string; /** A Listener is a generic network listener for stream-oriented protocols. */ export interface Listener { @@ -31,11 +28,7 @@ export interface Listener { } class ListenerImpl implements Listener { - private addr_: Addr; - - constructor(readonly fd: number, network: Network, address: string) { - this.addr_ = Object.freeze({ network, address }); - } + constructor(readonly fd: number) {} async accept(): Promise { const builder = new flatbuffers.Builder(); @@ -55,7 +48,7 @@ class ListenerImpl implements Listener { } addr(): Addr { - return this.addr_; + return notImplemented(); } } @@ -148,7 +141,7 @@ export function listen(network: Network, address: string): Listener { assert(msg.Any.ListenRes === baseRes!.innerType()); const res = new msg.ListenRes(); assert(baseRes!.inner(res) != null); - return new ListenerImpl(res.rid(), network, address); + return new ListenerImpl(res.rid()); } /** Dial connects to the address on the named network. diff --git a/js/net_test.ts b/js/net_test.ts index be02ec24fa9683..a22661f1054487 100644 --- a/js/net_test.ts +++ b/js/net_test.ts @@ -36,7 +36,7 @@ testPerm({ net: true }, async function netDialListen() { conn.close(); }); -testPerm({ net: true }, async function netConnCloseRead() { +testPerm({ net: true }, async function netCloseReadSuccess() { const addr = "127.0.0.1:4500"; const listener = deno.listen("tcp", addr); const closeDeferred = deferred(); @@ -64,7 +64,7 @@ testPerm({ net: true }, async function netConnCloseRead() { conn.close(); }); -testPerm({ net: true }, async function netConnDupCloseReadFailure() { +testPerm({ net: true }, async function netDoubleCloseRead() { const addr = "127.0.0.1:4500"; const listener = deno.listen("tcp", addr); const closeDeferred = deferred(); @@ -90,7 +90,7 @@ testPerm({ net: true }, async function netConnDupCloseReadFailure() { conn.close(); }); -testPerm({ net: true }, async function netConnCloseWrite() { +testPerm({ net: true }, async function netCloseWriteSuccess() { const addr = "127.0.0.1:4500"; const listener = deno.listen("tcp", addr); const closeDeferred = deferred(); @@ -123,7 +123,7 @@ testPerm({ net: true }, async function netConnCloseWrite() { conn.close(); }); -testPerm({ net: true }, async function netConnDupCloseWriteFailure() { +testPerm({ net: true }, async function netDoubleCloseWrite() { const addr = "127.0.0.1:4500"; const listener = deno.listen("tcp", addr); const closeDeferred = deferred(); diff --git a/src/ops.rs b/src/ops.rs index d29450405d0826..fb67d4befb9885 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -9,6 +9,8 @@ use isolate::Isolate; use isolate::IsolateState; use isolate::Op; use msg; +use resources; +use resources::Resource; use tokio_util; use flatbuffers::FlatBufferBuilder; @@ -19,7 +21,6 @@ use hyper; use hyper::rt::{Future, Stream}; use hyper::Client; use remove_dir_all::remove_dir_all; -use resources; use std; use std::fs; use std::net::{Shutdown, SocketAddr}; @@ -636,7 +637,8 @@ fn op_shutdown( _ => unimplemented!(), }; blocking!(base.sync(), || { - resource.shutdown_on(shutdown_mode)?; + // Use UFCS for disambiguation + Resource::shutdown(&mut resource, shutdown_mode)?; Ok(empty_buf()) }) } diff --git a/src/resources.rs b/src/resources.rs index 866faf3bec51c3..5a13e6cbfdbdc4 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -82,8 +82,7 @@ impl Resource { assert!(r.is_some()); } - // no collision with unimplemented shutdown - pub fn shutdown_on(&mut self, how: Shutdown) -> Result<(), DenoError> { + pub fn shutdown(&mut self, how: Shutdown) -> Result<(), DenoError> { let mut table = RESOURCE_TABLE.lock().unwrap(); let maybe_repr = table.get_mut(&self.rid); match maybe_repr { From 160115b6290740cd4dfdc09b54760b4709be5484 Mon Sep 17 00:00:00 2001 From: "Kevin (Kun) \"Kassimo\" Qian" Date: Fri, 5 Oct 2018 08:54:34 -0700 Subject: [PATCH 5/5] Move deferred to utils; Remove TODOs --- js/net.ts | 2 -- js/net_test.ts | 3 ++- js/test_util.ts | 20 -------------------- js/util.ts | 26 ++++++++++++++++++++++++++ 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/js/net.ts b/js/net.ts index 29f7c843227b3a..1258a0ff195985 100644 --- a/js/net.ts +++ b/js/net.ts @@ -82,7 +82,6 @@ class ConnImpl implements Conn { * Most callers should just use close(). */ closeRead(): void { - // TODO(ry) Connect to AsyncWrite::shutdown in resources.rs shutdown(this.fd, ShutdownMode.Read); } @@ -90,7 +89,6 @@ class ConnImpl implements Conn { * connection. Most callers should just use close(). */ closeWrite(): void { - // TODO(ry) Connect to AsyncWrite::shutdown in resources.rs shutdown(this.fd, ShutdownMode.Write); } } diff --git a/js/net_test.ts b/js/net_test.ts index a22661f1054487..c7c8dbb59c5ca7 100644 --- a/js/net_test.ts +++ b/js/net_test.ts @@ -1,7 +1,8 @@ // Copyright 2018 the Deno authors. All rights reserved. MIT license. import * as deno from "deno"; -import { testPerm, assert, assertEqual, deferred } from "./test_util.ts"; +import { testPerm, assert, assertEqual } from "./test_util.ts"; +import { deferred } from "./util.ts"; testPerm({ net: true }, function netListenClose() { const listener = deno.listen("tcp", "127.0.0.1:4500"); diff --git a/js/test_util.ts b/js/test_util.ts index 7fcde4464b0610..34a920d4733ba0 100644 --- a/js/test_util.ts +++ b/js/test_util.ts @@ -49,26 +49,6 @@ export function test(fn: testing.TestFunction) { testPerm({ write: false, net: false, env: false }, fn); } -interface Deferred { - promise: Promise; - resolve: Function; - reject: Function; -} - -export function deferred(): Deferred { - let resolve: Function | undefined; - let reject: Function | undefined; - const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - return { - promise, - resolve: resolve!, - reject: reject! - }; -} - test(function permSerialization() { for (const write of [true, false]) { for (const net of [true, false]) { diff --git a/js/util.ts b/js/util.ts index bfde0190849f09..de6a078bbe9ce8 100644 --- a/js/util.ts +++ b/js/util.ts @@ -101,3 +101,29 @@ export function containsOnlyASCII(str: string): boolean { } return /^[\x00-\x7F]*$/.test(str); } + +// @internal +export interface Deferred { + promise: Promise; + resolve: Function; + reject: Function; +} + +/** + * Create a wrapper around a promise that could be + * resolved externally. + * @internal + */ +export function deferred(): Deferred { + let resolve: Function | undefined; + let reject: Function | undefined; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { + promise, + resolve: resolve!, + reject: reject! + }; +}