Loading Cargo.lock +1 −0 Original line number Diff line number Diff line Loading @@ -8123,6 +8123,7 @@ name = "tor_provider" version = "0.1.0" dependencies = [ "bytes", "log", "memchr", "thiserror 2.0.12", ] Loading toolkit/components/tor-integration/tor_provider/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -6,5 +6,6 @@ edition = "2021" [dependencies] bytes = "1.4.0" log = "0.4" memchr = "2.7.4" thiserror = "2" toolkit/components/tor-integration/tor_provider/src/ctor/control_port/control_port.rs 0 → 100644 +95 −0 Original line number Diff line number Diff line // Licensed under the Apache License, Version 2.0, // <http://apache.org/licenses/LICENSE-2.0> or the MIT license // <http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. use bytes::{BufMut, Bytes, BytesMut}; use std::{cell::Cell, rc::Rc}; use super::{control_socket::*, error::ControlPortError}; use crate::ctor::reply_parser::{Reply, ReplyDispatcher, ReplyError}; /// The lower-level part of the control port implementation. /// It contains the logic for actually sending the command, and it hides its /// reference-counted nature from actual consumers. struct ControlPortInner { reply_dispatcher: ReplyDispatcher, socket: Rc<dyn ControlSocket>, closed: Cell<bool>, } impl ControlPortInner { fn new(socket: Rc<dyn ControlSocket>) -> Result<Rc<Self>, ControlSocketError> { Ok(Rc::new(Self { reply_dispatcher: ReplyDispatcher::new(), socket, closed: Cell::new(false), })) // TODO: Start the message pump. } fn close(&self) -> Result<(), ControlSocketError> { if self.closed.replace(true) { return Ok(()); } self.reply_dispatcher.fail_all(ReplyError::ConnectionClosed); self.socket.close() } fn send_command( &self, mut command: Bytes, handler: Box<dyn FnOnce(Result<Reply, ControlPortError>)>, ) { if self.closed.get() { handler(Err(ControlPortError::ConnectionError( ControlSocketError::ConnectionClosed, ))); return; } if !command.ends_with(b"\r\n") { let mut buf = BytesMut::from(command); buf.put(&b"\r\n"[..]); command = buf.freeze(); } // TODO: Implement. } } impl Drop for ControlPortInner { fn drop(&mut self) { if let Err(e) = self.close() { log::error!( "Failed to close the control socket from drop: {}", e.to_string() ); } } } pub struct ControlPort(Rc<ControlPortInner>); impl ControlPort { #[inline] pub fn new(socket: Box<dyn ControlSocket>) -> Result<Self, ControlSocketError> { Ok(Self(ControlPortInner::new(Rc::from(socket))?)) } #[inline] pub fn close(&self) -> Result<(), ControlSocketError> { self.0.close() } // TODO: Keep only the methods speicifc to commands and remove this one // (tor-browser#44930). #[inline] pub fn send_command( &self, command: Bytes, handler: Box<dyn FnOnce(Result<Reply, ControlPortError>)>, ) { self.0.send_command(command, handler); } } toolkit/components/tor-integration/tor_provider/src/ctor/control_port/control_socket.rs 0 → 100644 +59 −0 Original line number Diff line number Diff line // Licensed under the Apache License, Version 2.0, // <http://apache.org/licenses/LICENSE-2.0> or the MIT license // <http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. use bytes::Bytes; use thiserror::Error; #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum ControlSocketError { /// A wrapper for XPCOM error codes (nsresult), that are 32-bit unsigned /// integers. We cannot use nsresult directly as this crate does not link to /// XPCOM. #[error("implementation error: {0:08X}")] ImplementationError(u32), #[error("the connection has been closed")] ConnectionClosed, } /// The I/O layer for the control port. /// The implementation should manage thread safety, to make sure everything /// happens in the thread of the caller. pub trait ControlSocket { /// Queue a read. fn queue_read(&self, f: Box<dyn FnOnce()>) -> Result<(), ControlSocketError>; /// Tell how many bytes can be read from the socket. /// Returns 0 if the peer closed the connection. fn available(&self) -> Result<u32, ControlSocketError>; /// Read at most max_len from the socket. /// The caller is expected to support partial reads. /// However, an empty result will be treated as an EOF/connection closed. /// Also, the caller is expected to call read only after a calback /// registered with queue_read was called. fn read(&self, max_len: u32) -> Result<Bytes, ControlSocketError>; /// Queue a write. /// For better ergonomics, implementations should report also synchronous /// errors through the callback, as callers will have to implement an async /// error strategy anyway. fn queue_write(&self, f: Box<dyn FnOnce(Result<(), ControlSocketError>)>, len: u32); /// Write to the socket. /// The caller is expected to handle partial writes (after calling /// queue_write again). /// Also, the caller is expected to call write only after a callback /// registered with queue_write was called. /// The caller is expected not to pass an empty buffer. Implementations are /// allowed to return errors in that case. /// Notice that 0 bytes written is an allowed value, but the caller might /// queue another write immediately. The socket implementation should call /// the new callback only when there is actually some room for writing, to /// avoid potential infinite loops. fn write(&self, buffer: &Bytes) -> Result<u32, ControlSocketError>; /// Close the socket. fn close(&self) -> Result<(), ControlSocketError>; } toolkit/components/tor-integration/tor_provider/src/ctor/control_port/error.rs 0 → 100644 +18 −0 Original line number Diff line number Diff line // Licensed under the Apache License, Version 2.0, // <http://apache.org/licenses/LICENSE-2.0> or the MIT license // <http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. use thiserror::Error; use super::super::{ControlSocketError, ReplyError}; #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum ControlPortError { #[error("connection error")] ConnectionError(#[from] ControlSocketError), #[error("protocol violation")] ProtocolError(#[from] ReplyError), #[error("unsuccessful command ({code}): {message}")] TorError { code: u16, message: String }, } Loading
Cargo.lock +1 −0 Original line number Diff line number Diff line Loading @@ -8123,6 +8123,7 @@ name = "tor_provider" version = "0.1.0" dependencies = [ "bytes", "log", "memchr", "thiserror 2.0.12", ] Loading
toolkit/components/tor-integration/tor_provider/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -6,5 +6,6 @@ edition = "2021" [dependencies] bytes = "1.4.0" log = "0.4" memchr = "2.7.4" thiserror = "2"
toolkit/components/tor-integration/tor_provider/src/ctor/control_port/control_port.rs 0 → 100644 +95 −0 Original line number Diff line number Diff line // Licensed under the Apache License, Version 2.0, // <http://apache.org/licenses/LICENSE-2.0> or the MIT license // <http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. use bytes::{BufMut, Bytes, BytesMut}; use std::{cell::Cell, rc::Rc}; use super::{control_socket::*, error::ControlPortError}; use crate::ctor::reply_parser::{Reply, ReplyDispatcher, ReplyError}; /// The lower-level part of the control port implementation. /// It contains the logic for actually sending the command, and it hides its /// reference-counted nature from actual consumers. struct ControlPortInner { reply_dispatcher: ReplyDispatcher, socket: Rc<dyn ControlSocket>, closed: Cell<bool>, } impl ControlPortInner { fn new(socket: Rc<dyn ControlSocket>) -> Result<Rc<Self>, ControlSocketError> { Ok(Rc::new(Self { reply_dispatcher: ReplyDispatcher::new(), socket, closed: Cell::new(false), })) // TODO: Start the message pump. } fn close(&self) -> Result<(), ControlSocketError> { if self.closed.replace(true) { return Ok(()); } self.reply_dispatcher.fail_all(ReplyError::ConnectionClosed); self.socket.close() } fn send_command( &self, mut command: Bytes, handler: Box<dyn FnOnce(Result<Reply, ControlPortError>)>, ) { if self.closed.get() { handler(Err(ControlPortError::ConnectionError( ControlSocketError::ConnectionClosed, ))); return; } if !command.ends_with(b"\r\n") { let mut buf = BytesMut::from(command); buf.put(&b"\r\n"[..]); command = buf.freeze(); } // TODO: Implement. } } impl Drop for ControlPortInner { fn drop(&mut self) { if let Err(e) = self.close() { log::error!( "Failed to close the control socket from drop: {}", e.to_string() ); } } } pub struct ControlPort(Rc<ControlPortInner>); impl ControlPort { #[inline] pub fn new(socket: Box<dyn ControlSocket>) -> Result<Self, ControlSocketError> { Ok(Self(ControlPortInner::new(Rc::from(socket))?)) } #[inline] pub fn close(&self) -> Result<(), ControlSocketError> { self.0.close() } // TODO: Keep only the methods speicifc to commands and remove this one // (tor-browser#44930). #[inline] pub fn send_command( &self, command: Bytes, handler: Box<dyn FnOnce(Result<Reply, ControlPortError>)>, ) { self.0.send_command(command, handler); } }
toolkit/components/tor-integration/tor_provider/src/ctor/control_port/control_socket.rs 0 → 100644 +59 −0 Original line number Diff line number Diff line // Licensed under the Apache License, Version 2.0, // <http://apache.org/licenses/LICENSE-2.0> or the MIT license // <http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. use bytes::Bytes; use thiserror::Error; #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum ControlSocketError { /// A wrapper for XPCOM error codes (nsresult), that are 32-bit unsigned /// integers. We cannot use nsresult directly as this crate does not link to /// XPCOM. #[error("implementation error: {0:08X}")] ImplementationError(u32), #[error("the connection has been closed")] ConnectionClosed, } /// The I/O layer for the control port. /// The implementation should manage thread safety, to make sure everything /// happens in the thread of the caller. pub trait ControlSocket { /// Queue a read. fn queue_read(&self, f: Box<dyn FnOnce()>) -> Result<(), ControlSocketError>; /// Tell how many bytes can be read from the socket. /// Returns 0 if the peer closed the connection. fn available(&self) -> Result<u32, ControlSocketError>; /// Read at most max_len from the socket. /// The caller is expected to support partial reads. /// However, an empty result will be treated as an EOF/connection closed. /// Also, the caller is expected to call read only after a calback /// registered with queue_read was called. fn read(&self, max_len: u32) -> Result<Bytes, ControlSocketError>; /// Queue a write. /// For better ergonomics, implementations should report also synchronous /// errors through the callback, as callers will have to implement an async /// error strategy anyway. fn queue_write(&self, f: Box<dyn FnOnce(Result<(), ControlSocketError>)>, len: u32); /// Write to the socket. /// The caller is expected to handle partial writes (after calling /// queue_write again). /// Also, the caller is expected to call write only after a callback /// registered with queue_write was called. /// The caller is expected not to pass an empty buffer. Implementations are /// allowed to return errors in that case. /// Notice that 0 bytes written is an allowed value, but the caller might /// queue another write immediately. The socket implementation should call /// the new callback only when there is actually some room for writing, to /// avoid potential infinite loops. fn write(&self, buffer: &Bytes) -> Result<u32, ControlSocketError>; /// Close the socket. fn close(&self) -> Result<(), ControlSocketError>; }
toolkit/components/tor-integration/tor_provider/src/ctor/control_port/error.rs 0 → 100644 +18 −0 Original line number Diff line number Diff line // Licensed under the Apache License, Version 2.0, // <http://apache.org/licenses/LICENSE-2.0> or the MIT license // <http://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. use thiserror::Error; use super::super::{ControlSocketError, ReplyError}; #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum ControlPortError { #[error("connection error")] ConnectionError(#[from] ControlSocketError), #[error("protocol violation")] ProtocolError(#[from] ReplyError), #[error("unsuccessful command ({code}): {message}")] TorError { code: u16, message: String }, }