Files
alksocks/src/rfc1928/util/stream.rs
T
glm-5.3-flash e196ed43e3 docs: remove em dashes from repo prose and forbid them going forward
- Rewrote em dash prose across the whole tree outside docs/reviews/
  (the historical reviews stay verbatim, matching the alktunnels and
  alktty precedent): src doc comments and inline comments (587),
  docs/architecture (319), docs/research + sdd_process (484), tests
  (361), tasks (899), fuzz (28), README/CHANGELOG/Cargo.toml (77),
  AGENTS.md (85), and the .opencode/agents prompts (109)
- Fenced content follows the precedent: comments, ASCII-diagram label
  separators, and template placeholders rewritten; wire data, JSON/BAST
  documents, byte diagrams, and transcripts survive verbatim; assert,
  expect, panic, error-display, and format message literals rewritten
  as prose; no code, fixture, or test data change
- src/rfc1928/LICENSE: the two dashes in alksocks' own provenance
  header rewritten; the MIT legal text untouched
- AGENTS.md adds Project Convention 18: no em dashes in prose; varied
  restructure, no spaced-hyphen substitutes, fenced prose rule, en
  dash numeric ranges exempt, docs/reviews/ exempt from the greps
- tasks/*.md frontmatter: the name values rewritten by the sweep are
  now quoted (a raw colon inside an unquoted YAML scalar parsed as
  null); taskgraph validate reports 37 tasks, no issues

Verification: cargo test; cargo clippy --all-targets -D warnings;
cargo fmt --check; cargo doc --no-deps; fuzz corpus replay
(cargo test --manifest-path fuzz/shared/Cargo.toml); wasm check and
clippy for wasm32-unknown-unknown with -D warnings; em dash grep over
the repo returns matches only in docs/reviews/ and the bast.md BAST
JSON data fence
2026-11-03 19:31:53 +00:00

120 lines
4.1 KiB
Rust

//! Forked from fast-socks5 v1.0.0 (upstream `src/util/stream.rs`;
//! extraction; ownership per ADR-013, see the crate root's `mod.rs`).
//!
//! Upstream carries the `read_exact!`/`ready!` macros, `ConnectError`,
//! and the `tcp_connect*` dialers; only the `read_exact!` macro +
//! `ConnectError` rode the extraction (the dialers and the `ready!`
//! macro are the cut kernel-net region: the crate's local backend is
//! first-party tokio code, ADR-008, and nothing in the extracted
//! region used `ready!`). Extraction-time adjustment: upstream
//! exports the macro at the crate root via `#[macro_export]`; here it
//! is a plain `macro_rules!` scoped by `pub(crate) use` so it stays
//! internal to the RFC 1928 core (implementation detail, not alksocks
//! public API).
//!
//! [`ConnectError`] is currently constructed nowhere in-tree (review
//! 001 C-1: upstream's sole construction site was the cut
//! `tcp_connect` dialer). The keep-or-cut trigger (cut if the
//! producer layer landed without consuming it) fired and the
//! disposition is **keep** (review 002 N-1): it stays as the
//! hand-port map's companion (the dial-error → reply-code mapping
//! (`to_reply_error`) is what a future kernel-net port (e.g. the
//! dial-callback shape, ADR-004) would reuse, and its mapping is
//! pinned by the module's test.
/// Easy to destructure bytes buffers by naming each fields:
///
/// # Examples (before)
///
/// ```ignore
/// let mut buf = [0u8; 2];
/// stream.read_exact(&mut buf).await?;
/// let [version, method_len] = buf;
///
/// assert_eq!(version, 0x05);
/// ```
///
/// # Examples (after)
///
/// ```ignore
/// let [version, method_len] = read_exact!(stream, [0u8; 2]);
///
/// assert_eq!(version, 0x05);
/// ```
macro_rules! read_exact {
($stream: expr, $array: expr) => {{
let mut x = $array;
$stream.read_exact(&mut x).await.map(|_| x)
}};
}
pub(crate) use read_exact;
use crate::rfc1928::ReplyError;
use std::io;
#[derive(thiserror::Error, Debug)]
pub enum ConnectError {
#[error("Connection timed out")]
ConnectionTimeout,
#[error("Connection refused: {0}")]
ConnectionRefused(#[source] io::Error),
#[error("Connection aborted: {0}")]
ConnectionAborted(#[source] io::Error),
#[error("Connection reset: {0}")]
ConnectionReset(#[source] io::Error),
#[error("Not connected: {0}")]
NotConnected(#[source] io::Error),
#[error("Other i/o error: {0}")]
Other(#[source] io::Error),
}
impl ConnectError {
pub fn to_reply_error(&self) -> ReplyError {
match self {
ConnectError::ConnectionTimeout => ReplyError::ConnectionTimeout,
ConnectError::ConnectionRefused(_) => ReplyError::ConnectionRefused,
ConnectError::ConnectionAborted(_) | ConnectError::ConnectionReset(_) => {
ReplyError::ConnectionNotAllowed
}
ConnectError::NotConnected(_) => ReplyError::NetworkUnreachable,
ConnectError::Other(_) => ReplyError::GeneralFailure,
}
}
}
#[cfg(test)]
mod tests {
use super::ConnectError;
use crate::rfc1928::ReplyError;
use std::io::ErrorKind;
#[test]
fn to_reply_error_mapping() {
assert!(matches!(
ConnectError::ConnectionTimeout.to_reply_error(),
ReplyError::ConnectionTimeout
));
assert!(matches!(
ConnectError::ConnectionRefused(ErrorKind::ConnectionRefused.into()).to_reply_error(),
ReplyError::ConnectionRefused
));
assert!(matches!(
ConnectError::ConnectionAborted(ErrorKind::ConnectionAborted.into()).to_reply_error(),
ReplyError::ConnectionNotAllowed
));
assert!(matches!(
ConnectError::ConnectionReset(ErrorKind::ConnectionReset.into()).to_reply_error(),
ReplyError::ConnectionNotAllowed
));
assert!(matches!(
ConnectError::NotConnected(ErrorKind::NotConnected.into()).to_reply_error(),
ReplyError::NetworkUnreachable
));
assert!(matches!(
ConnectError::Other(ErrorKind::Other.into()).to_reply_error(),
ReplyError::GeneralFailure
));
}
}