pub struct ControlMessage {
pub arrived_at: i64,
/* private fields */
}Expand description
A parsed control protocol message from Tor.
ControlMessage represents a complete response from Tor’s control interface.
It handles both single-line and multi-line responses, parsing the status codes,
dividers, and content according to the control protocol specification.
§Response Format
Each line in a control message has the format:
STATUS DIVIDER CONTENTWhere STATUS is a 3-digit code, DIVIDER indicates continuation, and CONTENT
is the payload. The parsed content is stored as tuples of (status_code, divider, content).
§Status Codes
2xx: Success (e.g.,250 OK)4xx: Temporary failure5xx: Permanent failure (e.g.,552 Unrecognized key)6xx: Asynchronous event notification
§Invariants
- A
ControlMessageis never empty; construction fails if no valid lines exist - The
arrived_attimestamp is set at construction time - Raw content preserves the original bytes for hashing and equality
§Thread Safety
ControlMessage is Send and Sync. It is immutable after construction.
§Example
use stem_rs::response::ControlMessage;
// Parse a GETINFO response
let response = "250-version=0.4.7.8\r\n250 OK\r\n";
let msg = ControlMessage::from_str(response, None, false).unwrap();
// Check if successful
assert!(msg.is_ok());
// Access parsed content
let content = msg.content();
assert_eq!(content[0].0, "250"); // status code
assert_eq!(content[0].1, '-'); // divider (more lines follow)
// Iterate over lines
for line in msg.iter() {
if let Ok((key, value)) = line.clone().pop_mapping(false, false) {
println!("{} = {}", key, value);
}
}Fields§
§arrived_at: i64Unix timestamp (seconds since epoch) when this message arrived.
Implementations§
Source§impl ControlMessage
impl ControlMessage
Sourcepub fn new(
parsed_content: Vec<(String, char, Vec<u8>)>,
raw_content: Vec<u8>,
arrived_at: Option<i64>,
) -> Result<Self, Error>
pub fn new( parsed_content: Vec<(String, char, Vec<u8>)>, raw_content: Vec<u8>, arrived_at: Option<i64>, ) -> Result<Self, Error>
Creates a new ControlMessage from pre-parsed content.
This is a low-level constructor typically used internally. Most users
should use from_str instead.
§Arguments
parsed_content- Vector of (status_code, divider, content) tuplesraw_content- Original raw bytes of the messagearrived_at- Optional Unix timestamp; defaults to current time ifNone
§Errors
Returns Error::Protocol if parsed_content is empty,
as control messages must contain at least one line.
§Example
use stem_rs::response::ControlMessage;
let parsed = vec![("250".to_string(), ' ', b"OK".to_vec())];
let raw = b"250 OK\r\n".to_vec();
let msg = ControlMessage::new(parsed, raw, None).unwrap();
assert!(msg.is_ok());Sourcepub fn from_str(
content: &str,
msg_type: Option<&str>,
normalize: bool,
) -> Result<Self, Error>
pub fn from_str( content: &str, msg_type: Option<&str>, normalize: bool, ) -> Result<Self, Error>
Parses a control message from a string.
This is the primary way to create a ControlMessage from raw protocol data.
It handles both single-line and multi-line responses, including data sections.
§Arguments
content- The raw message string to parsemsg_type- Optional response type for validation (e.g., “SINGLELINE”, “GETINFO”)normalize- Iftrue, ensures proper\r\nline endings
§Normalization
When normalize is true:
- Adds a trailing newline if missing
- Converts
\nto\r\n(CRLF) as required by the protocol
§Errors
Returns Error::Protocol if:
- The content contains no valid control protocol lines
- The specified
msg_typevalidation fails
§Example
use stem_rs::response::ControlMessage;
// Parse without normalization (content already has \r\n)
let msg = ControlMessage::from_str("250 OK\r\n", None, false).unwrap();
// Parse with normalization (adds \r\n if needed)
let msg = ControlMessage::from_str("250 OK", None, true).unwrap();
// Parse and validate as single-line response
let msg = ControlMessage::from_str("250 OK\r\n", Some("SINGLELINE"), false).unwrap();Sourcepub fn is_ok(&self) -> bool
pub fn is_ok(&self) -> bool
Checks if the response indicates success.
A response is considered successful if any of its lines has a status code in the 2xx range (200-299). This is the standard success range in the Tor control protocol.
§Returns
true if at least one line has a 2xx status code, false otherwise.
§Example
use stem_rs::response::ControlMessage;
let ok = ControlMessage::from_str("250 OK\r\n", None, false).unwrap();
assert!(ok.is_ok());
let err = ControlMessage::from_str("552 Unrecognized key\r\n", None, false).unwrap();
assert!(!err.is_ok());Sourcepub fn content(&self) -> Vec<(String, char, String)>
pub fn content(&self) -> Vec<(String, char, String)>
Returns the parsed content as string tuples.
Each tuple contains:
- Status code (e.g., “250”, “552”, “650”)
- Divider character (
,-, or+) - Content as a UTF-8 string (lossy conversion from bytes)
§Returns
A vector of (status_code, divider, content) tuples.
§Example
use stem_rs::response::ControlMessage;
let msg = ControlMessage::from_str("250-version=0.4.7.8\r\n250 OK\r\n", None, false).unwrap();
let content = msg.content();
assert_eq!(content[0].0, "250");
assert_eq!(content[0].1, '-');
assert!(content[0].2.starts_with("version="));
assert_eq!(content[1].0, "250");
assert_eq!(content[1].1, ' ');
assert_eq!(content[1].2, "OK");Sourcepub fn raw_content(&self) -> &[u8] ⓘ
pub fn raw_content(&self) -> &[u8] ⓘ
Returns the original raw bytes of the message.
This is the unmodified data as received from the control socket,
including all protocol formatting (\r\n, status codes, etc.).
§Returns
A byte slice of the original message data.
Sourcepub fn raw_content_str(&self) -> String
pub fn raw_content_str(&self) -> String
Returns the raw content as a UTF-8 string.
Performs lossy UTF-8 conversion, replacing invalid sequences with the Unicode replacement character (U+FFFD).
§Returns
The raw message content as a string.
Sourcepub fn iter(&self) -> impl Iterator<Item = ControlLine> + '_
pub fn iter(&self) -> impl Iterator<Item = ControlLine> + '_
Returns an iterator over the message lines as ControlLine instances.
Each ControlLine provides parsing utilities for extracting values,
key-value mappings, and quoted strings from the line content.
§Example
use stem_rs::response::ControlMessage;
let msg = ControlMessage::from_str(
"250-version=0.4.7.8\r\n250 OK\r\n",
None,
false
).unwrap();
for line in msg.iter() {
println!("Line: {}", line);
}Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of lines in the message.
§Returns
The count of parsed lines. Always at least 1 for valid messages.
Sourcepub fn get(&self, index: usize) -> Option<ControlLine>
pub fn get(&self, index: usize) -> Option<ControlLine>
Returns the line at the specified index as a ControlLine.
§Arguments
index- Zero-based index of the line to retrieve
§Returns
Some(ControlLine) if the index is valid, None otherwise.
§Example
use stem_rs::response::ControlMessage;
let msg = ControlMessage::from_str("250-first\r\n250 second\r\n", None, false).unwrap();
assert!(msg.get(0).is_some());
assert!(msg.get(1).is_some());
assert!(msg.get(2).is_none());Trait Implementations§
Source§impl Clone for ControlMessage
impl Clone for ControlMessage
Source§fn clone(&self) -> ControlMessage
fn clone(&self) -> ControlMessage
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more