mirror of
https://github.com/denoland/deno.git
synced 2024-12-02 17:01:14 -05:00
1251c89321
This commit moves implementation of bin ops to "deno_core" crates as well as unifying logic between bin ops and json ops to reuse as much code as possible (both in Rust and JavaScript).
232 lines
5.8 KiB
Rust
232 lines
5.8 KiB
Rust
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
|
|
#[macro_use]
|
|
extern crate log;
|
|
|
|
use deno_core::error::bad_resource_id;
|
|
use deno_core::error::AnyError;
|
|
use deno_core::AsyncRefCell;
|
|
use deno_core::BufVec;
|
|
use deno_core::CancelHandle;
|
|
use deno_core::CancelTryFuture;
|
|
use deno_core::JsRuntime;
|
|
use deno_core::OpState;
|
|
use deno_core::RcRef;
|
|
use deno_core::Resource;
|
|
use deno_core::ResourceId;
|
|
use deno_core::ZeroCopyBuf;
|
|
use std::cell::RefCell;
|
|
use std::convert::TryFrom;
|
|
use std::env;
|
|
use std::io::Error;
|
|
use std::net::SocketAddr;
|
|
use std::rc::Rc;
|
|
use tokio::io::AsyncReadExt;
|
|
use tokio::io::AsyncWriteExt;
|
|
|
|
struct Logger;
|
|
|
|
impl log::Log for Logger {
|
|
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
|
metadata.level() <= log::max_level()
|
|
}
|
|
|
|
fn log(&self, record: &log::Record) {
|
|
if self.enabled(record.metadata()) {
|
|
println!("{} - {}", record.level(), record.args());
|
|
}
|
|
}
|
|
|
|
fn flush(&self) {}
|
|
}
|
|
|
|
// Note: a `tokio::net::TcpListener` doesn't need to be wrapped in a cell,
|
|
// because it only supports one op (`accept`) which does not require a mutable
|
|
// reference to the listener.
|
|
struct TcpListener {
|
|
inner: tokio::net::TcpListener,
|
|
cancel: CancelHandle,
|
|
}
|
|
|
|
impl TcpListener {
|
|
async fn accept(self: Rc<Self>) -> Result<TcpStream, Error> {
|
|
let cancel = RcRef::map(&self, |r| &r.cancel);
|
|
let stream = self.inner.accept().try_or_cancel(cancel).await?.0.into();
|
|
Ok(stream)
|
|
}
|
|
}
|
|
|
|
impl Resource for TcpListener {
|
|
fn close(self: Rc<Self>) {
|
|
self.cancel.cancel();
|
|
}
|
|
}
|
|
|
|
impl TryFrom<std::net::TcpListener> for TcpListener {
|
|
type Error = Error;
|
|
fn try_from(
|
|
std_listener: std::net::TcpListener,
|
|
) -> Result<Self, Self::Error> {
|
|
tokio::net::TcpListener::try_from(std_listener).map(|tokio_listener| Self {
|
|
inner: tokio_listener,
|
|
cancel: Default::default(),
|
|
})
|
|
}
|
|
}
|
|
|
|
struct TcpStream {
|
|
rd: AsyncRefCell<tokio::net::tcp::OwnedReadHalf>,
|
|
wr: AsyncRefCell<tokio::net::tcp::OwnedWriteHalf>,
|
|
// When a `TcpStream` resource is closed, all pending 'read' ops are
|
|
// canceled, while 'write' ops are allowed to complete. Therefore only
|
|
// 'read' futures are attached to this cancel handle.
|
|
cancel: CancelHandle,
|
|
}
|
|
|
|
impl TcpStream {
|
|
async fn read(self: Rc<Self>, buf: &mut [u8]) -> Result<usize, Error> {
|
|
let mut rd = RcRef::map(&self, |r| &r.rd).borrow_mut().await;
|
|
let cancel = RcRef::map(self, |r| &r.cancel);
|
|
rd.read(buf).try_or_cancel(cancel).await
|
|
}
|
|
|
|
async fn write(self: Rc<Self>, buf: &[u8]) -> Result<usize, Error> {
|
|
let mut wr = RcRef::map(self, |r| &r.wr).borrow_mut().await;
|
|
wr.write(buf).await
|
|
}
|
|
}
|
|
|
|
impl Resource for TcpStream {
|
|
fn close(self: Rc<Self>) {
|
|
self.cancel.cancel()
|
|
}
|
|
}
|
|
|
|
impl From<tokio::net::TcpStream> for TcpStream {
|
|
fn from(s: tokio::net::TcpStream) -> Self {
|
|
let (rd, wr) = s.into_split();
|
|
Self {
|
|
rd: rd.into(),
|
|
wr: wr.into(),
|
|
cancel: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn create_js_runtime() -> JsRuntime {
|
|
let mut runtime = JsRuntime::new(Default::default());
|
|
runtime.register_op("listen", deno_core::bin_op_sync(op_listen));
|
|
runtime.register_op("close", deno_core::bin_op_sync(op_close));
|
|
runtime.register_op("accept", deno_core::bin_op_async(op_accept));
|
|
runtime.register_op("read", deno_core::bin_op_async(op_read));
|
|
runtime.register_op("write", deno_core::bin_op_async(op_write));
|
|
runtime
|
|
}
|
|
|
|
fn op_listen(
|
|
state: &mut OpState,
|
|
_rid: ResourceId,
|
|
_bufs: &mut [ZeroCopyBuf],
|
|
) -> Result<u32, AnyError> {
|
|
debug!("listen");
|
|
let addr = "127.0.0.1:4544".parse::<SocketAddr>().unwrap();
|
|
let std_listener = std::net::TcpListener::bind(&addr)?;
|
|
std_listener.set_nonblocking(true)?;
|
|
let listener = TcpListener::try_from(std_listener)?;
|
|
let rid = state.resource_table.add(listener);
|
|
Ok(rid)
|
|
}
|
|
|
|
fn op_close(
|
|
state: &mut OpState,
|
|
rid: ResourceId,
|
|
_bufs: &mut [ZeroCopyBuf],
|
|
) -> Result<u32, AnyError> {
|
|
debug!("close rid={}", rid);
|
|
state
|
|
.resource_table
|
|
.close(rid)
|
|
.map(|_| 0)
|
|
.ok_or_else(bad_resource_id)
|
|
}
|
|
|
|
async fn op_accept(
|
|
state: Rc<RefCell<OpState>>,
|
|
rid: ResourceId,
|
|
_bufs: BufVec,
|
|
) -> Result<u32, AnyError> {
|
|
debug!("accept rid={}", rid);
|
|
|
|
let listener = state
|
|
.borrow()
|
|
.resource_table
|
|
.get::<TcpListener>(rid)
|
|
.ok_or_else(bad_resource_id)?;
|
|
let stream = listener.accept().await?;
|
|
let rid = state.borrow_mut().resource_table.add(stream);
|
|
Ok(rid)
|
|
}
|
|
|
|
async fn op_read(
|
|
state: Rc<RefCell<OpState>>,
|
|
rid: ResourceId,
|
|
mut bufs: BufVec,
|
|
) -> Result<u32, AnyError> {
|
|
assert_eq!(bufs.len(), 1, "Invalid number of arguments");
|
|
debug!("read rid={}", rid);
|
|
|
|
let stream = state
|
|
.borrow()
|
|
.resource_table
|
|
.get::<TcpStream>(rid)
|
|
.ok_or_else(bad_resource_id)?;
|
|
let nread = stream.read(&mut bufs[0]).await?;
|
|
Ok(nread as u32)
|
|
}
|
|
|
|
async fn op_write(
|
|
state: Rc<RefCell<OpState>>,
|
|
rid: ResourceId,
|
|
bufs: BufVec,
|
|
) -> Result<u32, AnyError> {
|
|
assert_eq!(bufs.len(), 1, "Invalid number of arguments");
|
|
debug!("write rid={}", rid);
|
|
|
|
let stream = state
|
|
.borrow()
|
|
.resource_table
|
|
.get::<TcpStream>(rid)
|
|
.ok_or_else(bad_resource_id)?;
|
|
let nwritten = stream.write(&bufs[0]).await?;
|
|
Ok(nwritten as u32)
|
|
}
|
|
|
|
fn main() {
|
|
log::set_logger(&Logger).unwrap();
|
|
log::set_max_level(
|
|
env::args()
|
|
.find(|a| a == "-D")
|
|
.map(|_| log::LevelFilter::Debug)
|
|
.unwrap_or(log::LevelFilter::Warn),
|
|
);
|
|
|
|
// NOTE: `--help` arg will display V8 help and exit
|
|
deno_core::v8_set_flags(env::args().collect());
|
|
|
|
let mut js_runtime = create_js_runtime();
|
|
let runtime = tokio::runtime::Builder::new_current_thread()
|
|
.enable_all()
|
|
.build()
|
|
.unwrap();
|
|
|
|
let future = async move {
|
|
js_runtime
|
|
.execute(
|
|
"http_bench_bin_ops.js",
|
|
include_str!("http_bench_bin_ops.js"),
|
|
)
|
|
.unwrap();
|
|
js_runtime.run_event_loop().await
|
|
};
|
|
runtime.block_on(future).unwrap();
|
|
}
|