Skip to main content

ProxyWatcher

Struct ProxyWatcher 

Source
pub struct ProxyWatcher { /* private fields */ }
Expand description

OS proxy watcher: initial snapshot, then debounced changes (equality ignores captured_at). Own OS threads: one, and on Linux one more per live source, plus one for WatchOptions::poll_interval (elsewhere that interval shortens the single thread’s own wait instead of adding one). Drop joins them. ProxyWatcher::health for route liveness. Windows, macOS, Linux, Android and iOS; elsewhere Error::Unsupported. Applies no *_proxy convention of its own: only KDE’s ProxyType = 4 builds a ProxyEnv, from the variables kioslaverc names.

The stream is level-triggered: while a subscriber keeps polling, a finite number of polls always reaches a WatchEvent::Snapshot matching the current state, a WatchEvent::Error, or None. An Error item does not end the stream; None comes only once the backend thread has stopped, and is preceded by a snapshot reporting WatchHealth::stopped. Undelivered snapshots fold into the newest one, so a subscriber that stops polling loses the history between snapshots, never current; failures with no snapshot between them fold into the newest, and undelivered failures are capped at 1024, oldest discarded first. The trim runs when a snapshot is published, so a failure arriving after a full one holds the queue at 1025 until the next snapshot trims it back.

One subscriber, not a broadcast. Only the most recently registered waker is kept, and poll_next takes &mut, so the only way to poll a watcher from more than one task is to put it behind a lock; after which the task that polled first is parked with its waker overwritten, and stays parked until something else on this watcher wakes it. To fan a stream out, poll it in one place and forward.

The waker is woken on the backend thread, which Drop joins, so the last owner of the watcher must not live inside the waker: dropping it there makes that thread wait for itself.

Send but not Sync, on every platform. So current, health, state and poll_now take &self and do no OS I/O, yet still belong to the thread that owns the watcher, and Arc<ProxyWatcher> is not Send. Move the watcher into the task that polls it and forward from there what other threads need. Under the tokio feature watch_channel is that forwarding already written; without it, one owner republishes current into a Mutex or a channel after every item. Neither recovers poll_now, which stays with the owner: send it a request rather than sharing the watcher.

On Linux the watcher copies the process environment once, on the constructing thread, and every later re-read takes the desktop, sandbox and kioslaverc variables from that copy. A variable changed after construction is therefore not seen (set what you need before constructing one), and the watcher’s own threads never race a concurrent std::env::set_var. GLib, which the GSettings and portal routes load at run time, reads variables of its own on those threads; this crate does not control those reads.

use std::future::poll_fn;
use std::pin::Pin;

use futures_core::Stream;
use proxy_watch::{ProxyWatcher, WatchEvent};

let mut watcher = ProxyWatcher::new()?;
println!("current: {:?}", watcher.current().effective);

let next = futures_executor::block_on(poll_fn(|cx| Pin::new(&mut watcher).poll_next(cx)));
match next {
    Some(WatchEvent::Snapshot { state, .. }) => {
        println!("now: {:?} (live: {})", state.config.effective, state.health.is_fully_live());
    }
    Some(WatchEvent::Error { error, .. }) => eprintln!("re-read failed: {error}"),
    _ => {}
}

Implementations§

Source§

impl ProxyWatcher

Source

pub fn new() -> Result<Self, Error>

Start watching with WatchOptions::default.

§Errors

Error::Unsupported, Error::Io, Error::Sandboxed (Linux, in a sandbox with no dconf), Error::CgiHttpProxy (KDE with ProxyType = 4, in a CGI environment), or a parse error from malformed OS settings.

Source

pub fn with_options(options: WatchOptions) -> Result<Self, Error>

Start watching with explicit options.

Blocks the way read does, and can block for twice as long: the notification route is armed before the configuration is read, so a change landing between the two is already queued rather than lost. The GSettings subscription on Linux and the whole of macOS come up after that read instead, and close the same window with one forced re-read once live: Linux queues it from spawn, macOS publishes on entering its run loop. On macOS that subscription and the read each run the same five-second retry budget, so a configd that is still coming up can hold this call for ten seconds.

§Errors

Same as ProxyWatcher::new.

Source

pub fn current(&self) -> ProxyConfig

The most recent snapshot (no OS I/O; may briefly wait for the internal mutex). Equals the constructor read right after start.

Source

pub fn health(&self) -> WatchHealth

Construction-time routes plus any runtime degradation (see WatchHealth). Does no OS I/O; it may briefly wait for the same internal mutex ProxyWatcher::current uses.

Source

pub fn state(&self) -> WatchState

Configuration and health copied from one shared-state observation.

This does no OS I/O, though it may briefly wait for the internal mutex. Prefer it over separate current and health calls when a route may degrade or the backend thread may stop concurrently.

Source

pub fn poll_now(&self)

Request an immediate re-read (same debounce/equality path as a native notify).

Call it when the caller knows something this crate cannot see: a connection attempt failed in a way that might be proxy-related, or the caller’s own network-change watch fired. This crate does not watch the network itself; this call is the hook for that signal.

Returns immediately, and may deliver nothing. A re-read shares a notification’s equality skip: one that finds the configuration and the health unchanged publishes no WatchEvent, and asking again does not force one. The Stream reports change, not completion, and current and state answer the same before and after such a re-read, so neither shows that this request landed. WatchEvent::Error says the same for the recovery case.

Trait Implementations§

Source§

impl Debug for ProxyWatcher

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Stream for ProxyWatcher

Source§

type Item = WatchEvent

Values yielded by the stream.
Source§

fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll<Option<Self::Item>>

Attempt to pull out the next value of this stream, registering the current task for wakeup if the value is not yet available, and returning None if the stream is exhausted. Read more
Source§

fn size_hint(&self) -> (usize, Option<usize>)

Returns the bounds on the remaining length of the stream. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more