diff options
-rw-r--r-- | cli/main.rs | 1 | ||||
-rw-r--r-- | cli/ops/files.rs | 31 | ||||
-rw-r--r-- | cli/ops/io.rs | 13 | ||||
-rw-r--r-- | cli/ops/net.rs | 68 | ||||
-rw-r--r-- | cli/resources.rs | 109 | ||||
-rw-r--r-- | cli/tokio_read.rs | 64 | ||||
-rw-r--r-- | cli/tokio_write.rs | 18 | ||||
-rw-r--r-- | tests/044_bad_resource.test | 4 | ||||
-rw-r--r-- | tests/044_bad_resource.ts | 7 | ||||
-rw-r--r-- | tests/044_bad_resource.ts.out | 6 |
10 files changed, 204 insertions, 117 deletions
diff --git a/cli/main.rs b/cli/main.rs index 948f8544e..42369e638 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -45,6 +45,7 @@ mod signal; pub mod source_maps; mod startup_data; pub mod state; +mod tokio_read; mod tokio_util; mod tokio_write; pub mod version; diff --git a/cli/ops/files.rs b/cli/ops/files.rs index 4afe00fc0..01abff3a9 100644 --- a/cli/ops/files.rs +++ b/cli/ops/files.rs @@ -1,6 +1,5 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; -use crate::deno_error; use crate::fs as deno_fs; use crate::resources; use crate::state::ThreadSafeState; @@ -104,13 +103,9 @@ pub fn op_close( ) -> Result<JsonOp, ErrBox> { let args: CloseArgs = serde_json::from_value(args)?; - match resources::lookup(args.rid as u32) { - None => Err(deno_error::bad_resource()), - Some(resource) => { - resource.close(); - Ok(JsonOp::Sync(json!({}))) - } - } + let resource = resources::lookup(args.rid as u32)?; + resource.close(); + Ok(JsonOp::Sync(json!({}))) } #[derive(Deserialize)] @@ -129,17 +124,13 @@ pub fn op_seek( ) -> Result<JsonOp, ErrBox> { let args: SeekArgs = serde_json::from_value(args)?; - match resources::lookup(args.rid as u32) { - None => Err(deno_error::bad_resource()), - Some(resource) => { - let op = resources::seek(resource, args.offset, args.whence as u32) - .and_then(move |_| futures::future::ok(json!({}))); - if args.promise_id.is_none() { - let buf = op.wait()?; - Ok(JsonOp::Sync(buf)) - } else { - Ok(JsonOp::Async(Box::new(op))) - } - } + let resource = resources::lookup(args.rid as u32)?; + let op = resources::seek(resource, args.offset, args.whence as u32) + .and_then(move |_| futures::future::ok(json!({}))); + if args.promise_id.is_none() { + let buf = op.wait()?; + Ok(JsonOp::Sync(buf)) + } else { + Ok(JsonOp::Async(Box::new(op))) } } diff --git a/cli/ops/io.rs b/cli/ops/io.rs index 610238942..8b8520c35 100644 --- a/cli/ops/io.rs +++ b/cli/ops/io.rs @@ -1,6 +1,7 @@ use super::dispatch_minimal::MinimalOp; use crate::deno_error; use crate::resources; +use crate::tokio_read; use crate::tokio_write; use deno::ErrBox; use deno::PinnedBuf; @@ -14,10 +15,11 @@ pub fn op_read(rid: i32, zero_copy: Option<PinnedBuf>) -> Box<MinimalOp> { } Some(buf) => buf, }; + match resources::lookup(rid as u32) { - None => Box::new(futures::future::err(deno_error::bad_resource())), - Some(resource) => Box::new( - tokio::io::read(resource, zero_copy) + Err(e) => Box::new(futures::future::err(e)), + Ok(resource) => Box::new( + tokio_read::read(resource, zero_copy) .map_err(ErrBox::from) .and_then(move |(_resource, _buf, nread)| Ok(nread as i32)), ), @@ -32,9 +34,10 @@ pub fn op_write(rid: i32, zero_copy: Option<PinnedBuf>) -> Box<MinimalOp> { } Some(buf) => buf, }; + match resources::lookup(rid as u32) { - None => Box::new(futures::future::err(deno_error::bad_resource())), - Some(resource) => Box::new( + Err(e) => Box::new(futures::future::err(e)), + Ok(resource) => Box::new( tokio_write::write(resource, zero_copy) .map_err(ErrBox::from) .and_then(move |(_resource, _buf, nwritten)| Ok(nwritten as i32)), diff --git a/cli/ops/net.rs b/cli/ops/net.rs index a9a62b148..11bf410d1 100644 --- a/cli/ops/net.rs +++ b/cli/ops/net.rs @@ -1,6 +1,5 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. use super::dispatch_json::{Deserialize, JsonOp, Value}; -use crate::deno_error; use crate::resolve_addr::resolve_addr; use crate::resources; use crate::resources::Resource; @@ -28,28 +27,24 @@ pub fn op_accept( let args: AcceptArgs = serde_json::from_value(args)?; let server_rid = args.rid as u32; - match resources::lookup(server_rid) { - None => Err(deno_error::bad_resource()), - Some(server_resource) => { - let op = tokio_util::accept(server_resource) - .and_then(move |(tcp_stream, _socket_addr)| { - let local_addr = tcp_stream.local_addr()?; - let remote_addr = tcp_stream.peer_addr()?; - let tcp_stream_resource = resources::add_tcp_stream(tcp_stream); - Ok((tcp_stream_resource, local_addr, remote_addr)) - }) - .map_err(ErrBox::from) - .and_then(move |(tcp_stream_resource, local_addr, remote_addr)| { - futures::future::ok(json!({ - "rid": tcp_stream_resource.rid, - "localAddr": local_addr.to_string(), - "remoteAddr": remote_addr.to_string(), - })) - }); - - Ok(JsonOp::Async(Box::new(op))) - } - } + let server_resource = resources::lookup(server_rid)?; + let op = tokio_util::accept(server_resource) + .and_then(move |(tcp_stream, _socket_addr)| { + let local_addr = tcp_stream.local_addr()?; + let remote_addr = tcp_stream.peer_addr()?; + let tcp_stream_resource = resources::add_tcp_stream(tcp_stream); + Ok((tcp_stream_resource, local_addr, remote_addr)) + }) + .map_err(ErrBox::from) + .and_then(move |(tcp_stream_resource, local_addr, remote_addr)| { + futures::future::ok(json!({ + "rid": tcp_stream_resource.rid, + "localAddr": local_addr.to_string(), + "remoteAddr": remote_addr.to_string(), + })) + }); + + Ok(JsonOp::Async(Box::new(op))) } #[derive(Deserialize)] @@ -105,22 +100,19 @@ pub fn op_shutdown( ) -> Result<JsonOp, ErrBox> { let args: ShutdownArgs = serde_json::from_value(args)?; - let rid = args.rid; + let rid = args.rid as u32; let how = args.how; - match resources::lookup(rid as u32) { - None => Err(deno_error::bad_resource()), - Some(mut resource) => { - let shutdown_mode = match how { - 0 => Shutdown::Read, - 1 => Shutdown::Write, - _ => unimplemented!(), - }; - - // Use UFCS for disambiguation - Resource::shutdown(&mut resource, shutdown_mode)?; - Ok(JsonOp::Sync(json!({}))) - } - } + let mut resource = resources::lookup(rid)?; + + let shutdown_mode = match how { + 0 => Shutdown::Read, + 1 => Shutdown::Write, + _ => unimplemented!(), + }; + + // Use UFCS for disambiguation + Resource::shutdown(&mut resource, shutdown_mode)?; + Ok(JsonOp::Sync(json!({}))) } #[derive(Deserialize)] diff --git a/cli/resources.rs b/cli/resources.rs index 5d4ba33bf..3bce51afb 100644 --- a/cli/resources.rs +++ b/cli/resources.rs @@ -217,15 +217,13 @@ impl Resource { pub fn shutdown(&mut self, how: Shutdown) -> Result<(), ErrBox> { 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(ErrBox::from) - } - _ => panic!("Cannot shutdown"), - }, + let repr = table.get_mut(&self.rid).ok_or_else(bad_resource)?; + + match repr { + Repr::TcpStream(ref mut f) => { + TcpStream::shutdown(f, how).map_err(ErrBox::from) + } + _ => Err(bad_resource()), } } } @@ -236,22 +234,30 @@ impl Read for Resource { } } -impl AsyncRead for Resource { - fn poll_read(&mut self, buf: &mut [u8]) -> Poll<usize, Error> { +/// `DenoAsyncRead` is the same as the `tokio_io::AsyncRead` trait +/// but uses an `ErrBox` error instead of `std::io:Error` +pub trait DenoAsyncRead { + fn poll_read(&mut self, buf: &mut [u8]) -> Poll<usize, ErrBox>; +} + +impl DenoAsyncRead for Resource { + fn poll_read(&mut self, buf: &mut [u8]) -> Poll<usize, ErrBox> { 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::FsFile(ref mut f) => f.poll_read(buf), - Repr::Stdin(ref mut f) => f.poll_read(buf), - Repr::TcpStream(ref mut f) => f.poll_read(buf), - Repr::HttpBody(ref mut f) => f.poll_read(buf), - Repr::ChildStdout(ref mut f) => f.poll_read(buf), - Repr::ChildStderr(ref mut f) => f.poll_read(buf), - _ => panic!("Cannot read"), - }, - } + let repr = table.get_mut(&self.rid).ok_or_else(bad_resource)?; + + let r = match repr { + Repr::FsFile(ref mut f) => f.poll_read(buf), + Repr::Stdin(ref mut f) => f.poll_read(buf), + Repr::TcpStream(ref mut f) => f.poll_read(buf), + Repr::HttpBody(ref mut f) => f.poll_read(buf), + Repr::ChildStdout(ref mut f) => f.poll_read(buf), + Repr::ChildStderr(ref mut f) => f.poll_read(buf), + _ => { + return Err(bad_resource()); + } + }; + + r.map_err(ErrBox::from) } } @@ -265,24 +271,34 @@ impl Write for Resource { } } -impl AsyncWrite for Resource { - fn poll_write(&mut self, buf: &[u8]) -> Poll<usize, Error> { +/// `DenoAsyncWrite` is the same as the `tokio_io::AsyncWrite` trait +/// but uses an `ErrBox` error instead of `std::io:Error` +pub trait DenoAsyncWrite { + fn poll_write(&mut self, buf: &[u8]) -> Poll<usize, ErrBox>; + + fn shutdown(&mut self) -> Poll<(), ErrBox>; +} + +impl DenoAsyncWrite for Resource { + fn poll_write(&mut self, buf: &[u8]) -> Poll<usize, ErrBox> { 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::FsFile(ref mut f) => f.poll_write(buf), - Repr::Stdout(ref mut f) => f.poll_write(buf), - Repr::Stderr(ref mut f) => f.poll_write(buf), - Repr::TcpStream(ref mut f) => f.poll_write(buf), - Repr::ChildStdin(ref mut f) => f.poll_write(buf), - _ => panic!("Cannot write"), - }, - } + let repr = table.get_mut(&self.rid).ok_or_else(bad_resource)?; + + let r = match repr { + Repr::FsFile(ref mut f) => f.poll_write(buf), + Repr::Stdout(ref mut f) => f.poll_write(buf), + Repr::Stderr(ref mut f) => f.poll_write(buf), + Repr::TcpStream(ref mut f) => f.poll_write(buf), + Repr::ChildStdin(ref mut f) => f.poll_write(buf), + _ => { + return Err(bad_resource()); + } + }; + + r.map_err(ErrBox::from) } - fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> { + fn shutdown(&mut self) -> futures::Poll<(), ErrBox> { unimplemented!() } } @@ -295,10 +311,9 @@ fn new_rid() -> ResourceId { pub fn add_fs_file(fs_file: tokio::fs::File) -> Resource { let rid = new_rid(); let mut tg = RESOURCE_TABLE.lock().unwrap(); - match tg.insert(rid, Repr::FsFile(fs_file)) { - Some(_) => panic!("There is already a file with that rid"), - None => Resource { rid }, - } + let r = tg.insert(rid, Repr::FsFile(fs_file)); + assert!(r.is_none()); + Resource { rid } } pub fn add_tcp_listener(listener: tokio::net::TcpListener) -> Resource { @@ -354,6 +369,7 @@ pub fn post_message_to_worker( // unwrap here is incorrect, but doing it anyway wc.0.clone().send(buf) } + // TODO: replace this panic with `bad_resource` _ => panic!("bad resource"), // futures::future::err(bad_resource()).into(), } } @@ -522,10 +538,13 @@ pub fn get_file(rid: ResourceId) -> Result<std::fs::File, ErrBox> { } } -pub fn lookup(rid: ResourceId) -> Option<Resource> { +pub fn lookup(rid: ResourceId) -> Result<Resource, ErrBox> { debug!("resource lookup {}", rid); let table = RESOURCE_TABLE.lock().unwrap(); - table.get(&rid).map(|_| Resource { rid }) + table + .get(&rid) + .ok_or_else(bad_resource) + .map(|_| Resource { rid }) } pub fn seek( diff --git a/cli/tokio_read.rs b/cli/tokio_read.rs new file mode 100644 index 000000000..25c4df191 --- /dev/null +++ b/cli/tokio_read.rs @@ -0,0 +1,64 @@ +// Copyright (c) 2019 Tokio Contributors. All rights reserved. MIT license. +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +// Forked from: https://github.com/tokio-rs/tokio/blob/9b3f8564af4bb1aee07fab3c401eb412ca5eeac5/tokio-io/src/io/read.rs +use crate::resources::DenoAsyncRead; +use deno::ErrBox; +use futures::{Future, Poll}; +use std::mem; + +/// This is almost the same implementation as in tokio, the only difference is +/// that error type is `ErrBox` instead of `std::io::Error`. + +#[derive(Debug)] +enum State<R, T> { + Pending { rd: R, buf: T }, + Empty, +} + +/// Tries to read some bytes directly into the given `buf` in asynchronous +/// manner, returning a future type. +/// +/// The returned future will resolve to both the I/O stream and the buffer +/// as well as the number of bytes read once the read operation is completed. +pub fn read<R, T>(rd: R, buf: T) -> Read<R, T> +where + R: DenoAsyncRead, + T: AsMut<[u8]>, +{ + Read { + state: State::Pending { rd, buf }, + } +} + +/// A future which can be used to easily read available number of bytes to fill +/// a buffer. +/// +/// Created by the [`read`] function. +#[derive(Debug)] +pub struct Read<R, T> { + state: State<R, T>, +} + +impl<R, T> Future for Read<R, T> +where + R: DenoAsyncRead, + T: AsMut<[u8]>, +{ + type Item = (R, T, usize); + type Error = ErrBox; + + fn poll(&mut self) -> Poll<(R, T, usize), ErrBox> { + let nread = match self.state { + State::Pending { + ref mut rd, + ref mut buf, + } => try_ready!(rd.poll_read(&mut buf.as_mut()[..])), + State::Empty => panic!("poll a Read after it's done"), + }; + + match mem::replace(&mut self.state, State::Empty) { + State::Pending { rd, buf } => Ok((rd, buf, nread).into()), + State::Empty => panic!("invalid internal state"), + } + } +} diff --git a/cli/tokio_write.rs b/cli/tokio_write.rs index 945de375d..31b4cda30 100644 --- a/cli/tokio_write.rs +++ b/cli/tokio_write.rs @@ -1,10 +1,8 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// TODO Submit this file upstream into tokio-io/src/io/write.rs -use std::io; -use std::mem; - +use crate::resources::DenoAsyncWrite; +use deno::ErrBox; use futures::{Future, Poll}; -use tokio::io::AsyncWrite; +use std::mem; /// A future used to write some data to a stream. /// @@ -29,7 +27,7 @@ enum State<A, T> { /// buffer to get destroyed. pub fn write<A, T>(a: A, buf: T) -> Write<A, T> where - A: AsyncWrite, + A: DenoAsyncWrite, T: AsRef<[u8]>, { Write { @@ -37,15 +35,17 @@ where } } +/// This is almost the same implementation as in tokio, difference is +/// that error type is `ErrBox` instead of `std::io::Error`. impl<A, T> Future for Write<A, T> where - A: AsyncWrite, + A: DenoAsyncWrite, T: AsRef<[u8]>, { type Item = (A, T, usize); - type Error = io::Error; + type Error = ErrBox; - fn poll(&mut self) -> Poll<(A, T, usize), io::Error> { + fn poll(&mut self) -> Poll<(A, T, usize), ErrBox> { let nwritten = match self.state { State::Pending { ref mut a, diff --git a/tests/044_bad_resource.test b/tests/044_bad_resource.test new file mode 100644 index 000000000..8804fde1b --- /dev/null +++ b/tests/044_bad_resource.test @@ -0,0 +1,4 @@ +args: run --reload --allow-read tests/044_bad_resource.ts +output: tests/044_bad_resource.ts.out +check_stderr: true +exit_code: 1 diff --git a/tests/044_bad_resource.ts b/tests/044_bad_resource.ts new file mode 100644 index 000000000..d2fc828f0 --- /dev/null +++ b/tests/044_bad_resource.ts @@ -0,0 +1,7 @@ +async function main(): Promise<void> { + const file = await Deno.open("Cargo.toml", "r"); + file.close(); + await file.seek(10, 0); +} + +main(); diff --git a/tests/044_bad_resource.ts.out b/tests/044_bad_resource.ts.out new file mode 100644 index 000000000..155e4396f --- /dev/null +++ b/tests/044_bad_resource.ts.out @@ -0,0 +1,6 @@ +[WILDCARD] +error: Uncaught BadResource: bad resource id +[WILDCARD]dispatch_json.ts:[WILDCARD] + at DenoError ([WILDCARD]errors.ts:[WILDCARD]) + at unwrapResponse ([WILDCARD]dispatch_json.ts:[WILDCARD]) + at sendAsync ([WILDCARD]dispatch_json.ts:[WILDCARD]) |