-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Implement closeRead/closeWrite using TcpStream::shutdown #903
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
46daa09
147a762
0ea6755
4f06636
160115b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,14 +2,15 @@ | |
|
|
||
| import * as deno from "deno"; | ||
| import { testPerm, assert, assertEqual } from "./test_util.ts"; | ||
| import { deferred } from "./util.ts"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool! I forgot about this when I suggested it - but this is only newly possible because of your work in #859.
cc @kitsonk - can compiler_test.ts work like this too? |
||
|
|
||
| testPerm({ net: true }, function netListenClose() { | ||
| const listener = deno.listen("tcp", "127.0.0.1:4500"); | ||
| listener.close(); | ||
| }); | ||
|
|
||
| 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])); | ||
|
|
@@ -35,3 +36,115 @@ testPerm({ net: true }, async function netDialListen() { | |
| listener.close(); | ||
| conn.close(); | ||
| }); | ||
|
|
||
| testPerm({ net: true }, async function netCloseReadSuccess() { | ||
| 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); // 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 netDoubleCloseRead() { | ||
| 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 netCloseWriteSuccess() { | ||
| 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 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); | ||
| assertEqual(err.kind, deno.ErrorKind.BrokenPipe); | ||
| assertEqual(err.name, "BrokenPipe"); | ||
| closeDeferred.resolve(); | ||
| listener.close(); | ||
| conn.close(); | ||
| }); | ||
|
|
||
| testPerm({ net: true }, async function netDoubleCloseWrite() { | ||
| 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"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice. Thank you. |
||
| closeDeferred.resolve(); | ||
| listener.close(); | ||
| conn.close(); | ||
| }); | ||
|
kevinkassimo marked this conversation as resolved.
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,13 +8,15 @@ | |
| // 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; | ||
| 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 +81,20 @@ impl Resource { | |
| let r = table.remove(&self.rid); | ||
| assert!(r.is_some()); | ||
| } | ||
|
|
||
| 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 { | ||
| None => panic!("bad rid"), | ||
| Some(repr) => match repr { | ||
| Repr::TcpStream(ref mut f) => { | ||
| TcpStream::shutdown(f, how).map_err(|err| DenoError::from(err)) | ||
| } | ||
| _ => panic!("Cannot shutdown"), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks good - nice. |
||
| }, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Read for Resource { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.