Skip to content
Draft
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
77aa507
test(network): require operational teardown assessment
seonghobae Aug 30, 2026
b5396ee
test(network): format teardown assessment regression
seonghobae Aug 30, 2026
7b0880a
feat(network): separate protocol ack from teardown evidence
seonghobae Aug 30, 2026
1dbf3a8
feat(network): expose teardown assessment boundary
seonghobae Aug 30, 2026
bfd2044
test(network): require every teardown observation
seonghobae Aug 30, 2026
0fff981
test(network): require typed BiDi transport closure evidence
seonghobae Aug 30, 2026
4bc7c3c
style(network): apply canonical transport-close test formatting
seonghobae Aug 30, 2026
916f6d4
feat(network): observe typed BiDi transport closure
seonghobae Aug 30, 2026
eed38a3
feat(network): export bounded BiDi transport closure evidence
seonghobae Aug 30, 2026
69cfe4c
style(network): apply canonical transport-closure formatting
seonghobae Aug 30, 2026
6612af7
test(network): satisfy strict transport-closure contracts
seonghobae Aug 30, 2026
fa68bb9
test(network): cover fail-closed transport closure errors
seonghobae Aug 30, 2026
c691d07
chore(network): converge teardown evidence on current session.end res…
seonghobae Aug 30, 2026
5654d0c
chore(network): converge transport closure on current teardown stack
seonghobae Aug 30, 2026
f956341
test(network): require pong-tolerant BiDi transport close
seonghobae Aug 30, 2026
80f5000
fix(network): tolerate bounded pong before BiDi transport close
seonghobae Aug 30, 2026
01cfce2
test(network): bound pong handling during BiDi transport close
seonghobae Aug 30, 2026
a45812d
style(network): apply canonical rustfmt to transport close tests
seonghobae Aug 30, 2026
5ff8ae1
test(network): require typed transport evidence for BiDi teardown
seonghobae Aug 30, 2026
baba701
style(network): apply canonical rustfmt to typed teardown regression
seonghobae Aug 30, 2026
4d43cba
feat(network): bind typed transport closure to teardown
seonghobae Aug 30, 2026
7d72370
style(network): apply canonical teardown formatting
seonghobae Aug 30, 2026
ef1b4a9
test(network): reject forgeable teardown completion
seonghobae Aug 30, 2026
72449de
fix(network): fail closed on teardown placeholders
seonghobae Aug 30, 2026
ce7c40e
test(network): reject process profile teardown placeholders
seonghobae Aug 30, 2026
43c9ae7
fix(network): remove forgeable teardown placeholders
seonghobae Aug 31, 2026
cf5fd42
fix(network): restack teardown assessment on typed session.end
seonghobae Sep 1, 2026
2a93e7e
merge(network): restack transport closure on typed session.end
seonghobae Sep 1, 2026
b1712b8
merge(network): restack teardown binding on typed transport closure
seonghobae Sep 1, 2026
964c5b1
merge(network): restack teardown evidence on repaired status response
seonghobae Sep 2, 2026
cb49cda
merge(network): restack transport closure on repaired status response
seonghobae Sep 2, 2026
626026c
fix(network): repair typed teardown exact-head regression
seonghobae Sep 2, 2026
88e68f3
fix(network): encode validated BiDi response routing structurally
seonghobae Sep 3, 2026
ab1dea7
docs(doctoring): refresh WebDriver BiDi published baseline
seonghobae Sep 3, 2026
870cb39
fix(network): make BiDi response routing structurally valid
seonghobae Sep 3, 2026
4c112e7
merge(parent): refresh teardown evidence base
seonghobae Sep 4, 2026
463859f
merge(parent): refresh transport closure base
seonghobae Sep 4, 2026
d60e986
merge(parent): refresh teardown transport binding base
seonghobae Sep 4, 2026
5e2b17c
merge(parent): refresh teardown evidence base
seonghobae Sep 5, 2026
a484101
merge(parent): refresh transport close evidence base
seonghobae Sep 5, 2026
f8edec3
merge(parent): refresh teardown transport binding base
seonghobae Sep 5, 2026
0d72082
merge: restack teardown evidence on current response
seonghobae Sep 5, 2026
cbaf50d
merge: restack transport close on teardown evidence
seonghobae Sep 5, 2026
a13de5f
merge: restack teardown binding on close evidence
seonghobae Sep 5, 2026
f816be5
test(network): reject cross-connection teardown closure evidence
seonghobae Sep 5, 2026
f1f9664
fix(network): fail closed on connection-generation exhaustion
seonghobae Sep 5, 2026
fc61df5
fix(network): mint non-forgeable BiDi connection generations
seonghobae Sep 5, 2026
c7ce08a
test(network): cover BiDi connection-generation allocator
seonghobae Sep 5, 2026
ef62a4e
fix(network): retain connection provenance in BiDi correlation
seonghobae Sep 5, 2026
04f066c
fix(network): bind session.end correlation to exact connection
seonghobae Sep 5, 2026
54265a2
fix(network): bind session.end acknowledgments to connection provenance
seonghobae Sep 5, 2026
8a1b9ee
test(network): require session.end transport provenance
seonghobae Sep 5, 2026
dddb93c
fix(network): retain exact connection on transport closure evidence
seonghobae Sep 5, 2026
9452fc3
fix(network): reject teardown evidence from another connection
seonghobae Sep 5, 2026
77b294a
test(network): verify exact transport generation at teardown
seonghobae Sep 5, 2026
23eea66
fix(network): expose teardown provenance mismatch error
seonghobae Sep 5, 2026
92392b2
test(network): cover same-session reconnect and cross-session teardow…
seonghobae Sep 5, 2026
4bb85c2
test(network): prove same-endpoint reconnect provenance isolation
seonghobae Sep 5, 2026
ae6495e
refactor(network): type BiDi connection-generation provenance
seonghobae Sep 5, 2026
9115fd8
test(network): type connection-generation allocator evidence
seonghobae Sep 5, 2026
ddc1082
refactor(network): carry typed BiDi generation through correlation
seonghobae Sep 5, 2026
2b77143
refactor(network): type session.end connection provenance
seonghobae Sep 5, 2026
4255eb4
refactor(network): type transport-closure connection provenance
seonghobae Sep 5, 2026
0a94765
test(network): reproduce foreign BiDi response correlation
seonghobae Sep 5, 2026
1c2749e
feat(network): bind received BiDi messages to connection
seonghobae Sep 5, 2026
256f6b3
fix(network): reject foreign BiDi response transports
seonghobae Sep 5, 2026
9037db8
fix(network): require received connection provenance for session.end
seonghobae Sep 5, 2026
b35a248
feat(network): expose connection-bound BiDi message reader
seonghobae Sep 5, 2026
98fe192
test(network): bind session.end responses to receive stream
seonghobae Sep 5, 2026
d4cd2de
test(network): assert exact received transport mismatch
seonghobae Sep 5, 2026
ccefd01
refactor(network): return transport after complete BiDi message
seonghobae Sep 5, 2026
59e92c6
test(network): preserve transport through bound response read
seonghobae Sep 5, 2026
e7bfec4
test(network): cover connection-bound message reader
seonghobae Sep 5, 2026
f4e18ec
docs(network): trace received BiDi connection provenance
seonghobae Sep 5, 2026
feba41f
test(network): cover uncorrelatable connection-bound responses
seonghobae Sep 5, 2026
89e3975
fix(network): satisfy teardown provenance lint
seonghobae Sep 5, 2026
6f6d49f
fix(network): remove impossible BiDi provenance fallback
seonghobae Sep 5, 2026
ddfac5b
fix(network): expose generation exhaustion to connection tests
seonghobae Sep 5, 2026
78ffe1c
test(network): cover connection generation exhaustion
seonghobae Sep 5, 2026
8315eed
test(network): activate generation exhaustion coverage
seonghobae Sep 5, 2026
defa1a0
test(network): restore session.end response formatting
seonghobae Sep 5, 2026
00e007b
test(network): restore response provenance formatting
seonghobae Sep 5, 2026
2c8e9e2
test(network): restore teardown assessment formatting
seonghobae Sep 5, 2026
64b88e6
docs(network): record BiDi provenance quality-gate repair
seonghobae Sep 5, 2026
63cbca0
docs(network): align provenance note with Rust MSRV
seonghobae Sep 5, 2026
ebac126
fix(network): remove unused correlation accessor
seonghobae Sep 5, 2026
afb623e
merge: adopt current response in teardown assessment
seonghobae Sep 5, 2026
b11b6c9
chore: integrate current teardown parent into closure evidence
seonghobae Sep 5, 2026
3e70574
chore(network): integrate current closure provenance parent
seonghobae Sep 5, 2026
6865faa
chore(stack): bind repaired teardown successor to response base
seonghobae Sep 6, 2026
32b5615
test(network): reject replacement status replies in teardown stack
seonghobae Sep 7, 2026
c7ccb0c
merge: preserve teardown provenance while adopting current response p…
seonghobae Sep 7, 2026
be85f04
merge(stack): adopt current session-end response parent
seonghobae Sep 7, 2026
b97f57e
test(network): reject peer-close-only transport evidence
seonghobae Sep 7, 2026
8d63ec8
test(network): require Pong before bounded close completion
seonghobae Sep 7, 2026
1cb76a6
fix(network): complete bounded WebSocket closing handshake
seonghobae Sep 7, 2026
1376f4c
test(network): complete teardown close fixture
seonghobae Sep 7, 2026
2db547c
Merge remote-tracking branch 'origin/feat/webdriver-bidi-teardown-tra…
seonghobae Sep 8, 2026
4305ff8
style(network): apply rustfmt to close frame
seonghobae Sep 8, 2026
1dcaaf8
fix(network): retain connection-bound response correlation
seonghobae Sep 8, 2026
ec16b33
fix(network): restore shared response routing
seonghobae Sep 8, 2026
bae183e
test(network): cover teardown mask-key failures
seonghobae Sep 8, 2026
f060bf1
test(network): cover peer-close mask-key failure
seonghobae Sep 8, 2026
4a7cc03
test(network): cover bounded pre-close control branch
seonghobae Sep 8, 2026
ac5cb34
test(network): cover close timeout validation
seonghobae Sep 8, 2026
2fb1b59
test(network): cover excessive close timeout
seonghobae Sep 8, 2026
a17914a
fix(network): separate frame timeout guards
seonghobae Sep 8, 2026
9033fbc
fix(network): make timeout validation exhaustive
seonghobae Sep 8, 2026
2bbdc02
fix(network): avoid timeout guard short circuit
seonghobae Sep 8, 2026
6cdf674
test(network): verify close writer deadline rejection directly
seonghobae Sep 8, 2026
7a88551
test(network): preserve exact close deadline assertions
seonghobae Sep 8, 2026
d126242
test(network): exercise complete close writer contract
seonghobae Sep 8, 2026
07ef43e
test(network): synchronize mask rejection peers
seonghobae Sep 8, 2026
f8df02e
fix(network): bound the complete closure exchange deadline
seonghobae Sep 8, 2026
292d73b
test(network): verify deadline and protocol paths together
seonghobae Sep 8, 2026
8716b9d
fix(network): reject unassigned protocol close codes
seonghobae Sep 8, 2026
383f157
fix(network): admit bounded repeated closure controls
seonghobae Sep 8, 2026
3a8e6f4
test(network): reuse arrays in repeated control fixtures
seonghobae Sep 8, 2026
10d5e1f
test(network): reject server Close 1010
seonghobae Sep 8, 2026
6b6c90e
fix(network): reject server Close 1010
seonghobae Sep 8, 2026
ce4ef71
test(network): require role-specific server Close error
seonghobae Sep 8, 2026
39f054f
fix(network): separate peer Close role error
seonghobae Sep 8, 2026
cc3bf6d
test(network): assert typed peer Close role error
seonghobae Sep 8, 2026
30b4587
fix(test): retain peer Close error for assertions
seonghobae Sep 8, 2026
9e51537
fix(test): satisfy strict clippy in Close role regression
seonghobae Sep 8, 2026
a437163
test(network): remove duplicate Close role fixture
seonghobae Sep 8, 2026
ca49031
test(network): share close-role regression with unit coverage
seonghobae Sep 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All notable changes to OriginWeave are documented in this file. The format follo

- Typed outbound WebDriver BiDi `session.end` over the bounded client WebSocket stream: it serializes only the standards-defined method with empty params, rejects invalid frame deadlines before correlation registration, retires only the just-registered id when frame preflight proves no command bytes were emitted, preserves exact command-kind correlation across ambiguous writes, and does not treat frame-write success as proof that the browser session ended.
- Typed `session.end` response admission that consumes only the exact outstanding command-kind correlation after complete envelope validation, preserves remote protocol errors as failures, and does not claim browser-process exit or resource cleanup from a protocol acknowledgment.
- Fail-closed `session.end` teardown assessment that binds only the typed observation produced by consuming the exact transport, keeps browser-process-exit and task-profile-removal evidence unavailable until their runtime owners exist, and therefore cannot report operational completion from caller-supplied booleans.
- Bounded WebDriver BiDi transport-closure observation that consumes the established stream, accepts only a validated peer Close frame or clean pre-frame EOF, permits at most one unsolicited Pong, and keeps transport closure separate from process-exit and profile-cleanup claims.
- Bounded RFC 6455 WebDriver BiDi opening-response validation on the exact peer-verified stream: it admits only HTTP/1.1 `101`, case-insensitive `Upgrade`/`Connection` tokens, and the client-key-correlated `Sec-WebSocket-Accept` value within monotonic time and header-size ceilings; it restores blocking mode and still does not implement WebSocket frames or grant browser/Agent authority.
- Typed outbound WebDriver BiDi `session.status` over the bounded client WebSocket stream: it serializes only the standards-defined method with empty params, preserves exact typed command-id correlation, rejects invalid frame deadlines before registration, retires only the just-registered id when a local masking-key preflight proves no command bytes were emitted, and keeps correlation outstanding after partial or ambiguous writes; frame-write success is not treated as command completion or browser/Agent authority.
- Bounded WebDriver BiDi loopback TCP transport that consumes one exact no-DNS connect target, retries only explicitly recoverable local transport failures within repository timeout and attempt ceilings, exposes the stream only after operating-system peer inspection and exact peer verification, supports a consuming handoff of the original stream with typed credential-free peer/session/TLS and bounded-attempt evidence, preserves typed causal errors, and performs no DNS, proxy/PAC, process authentication, TLS, WebSocket, BiDi message, browser-action, or Agent-authority step.
Expand Down
16 changes: 14 additions & 2 deletions crates/originweave-network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
//! unmasked server-frame reads, assembles bounded WebDriver BiDi text messages,
//! classifies complete local-end JSON envelopes, tracks bounded command-response
//! correlation, sends narrowly typed `session.status` and `session.end` commands,
//! and admits typed correlated status and end responses without exposing generic
//! JSON bodies or granting browser, TLS, policy, secret, or Agent authority.
//! admits typed correlated status and end responses, observes bounded peer Close
//! or clean-EOF transport cessation, and keeps protocol/transport evidence separate
//! from explicit operational teardown observations without exposing generic JSON
//! bodies or granting browser, TLS, policy, secret, process, profile, or Agent authority.

#![forbid(unsafe_code)]
#![deny(missing_docs)]
Expand All @@ -23,10 +25,12 @@ mod webdriver_bidi_session_end_command;
mod webdriver_bidi_session_end_response;
mod webdriver_bidi_session_status_command;
mod webdriver_bidi_session_status_response;
mod webdriver_bidi_session_teardown;
mod webdriver_bidi_websocket_frame;
mod webdriver_bidi_websocket_handshake;
mod webdriver_bidi_websocket_message;
mod webdriver_bidi_websocket_opening_recovery;
mod webdriver_bidi_websocket_transport_closure;

#[cfg(test)]
mod webdriver_bidi_json_envelope_public_boundary_tests;
Expand Down Expand Up @@ -62,6 +66,10 @@ pub use webdriver_bidi_session_status_response::{
MAX_WEBDRIVER_BIDI_SESSION_STATUS_MESSAGE_SIZE, WebDriverBiDiSessionStatusResponseError,
WebDriverBiDiSessionStatusResult,
};
pub use webdriver_bidi_session_teardown::{
WebDriverBiDiSessionTeardownAssessment, WebDriverBiDiSessionTeardownDisposition,
WebDriverBiDiSessionTeardownObservations,
};
pub use webdriver_bidi_websocket_frame::{
MAX_WEBSOCKET_FRAME_PAYLOAD_SIZE, MAX_WEBSOCKET_FRAME_TIMEOUT,
WebDriverBiDiWebSocketEstablished, WebDriverBiDiWebSocketFrame,
Expand All @@ -81,3 +89,7 @@ pub use webdriver_bidi_websocket_message::{
WebDriverBiDiWebSocketTextMessage,
};
pub use webdriver_bidi_websocket_opening_recovery::WebDriverBiDiWebSocketOpeningWriteRecoveryDisposition;
pub use webdriver_bidi_websocket_transport_closure::{
WebDriverBiDiWebSocketTransportClosureError, WebDriverBiDiWebSocketTransportClosureKind,
WebDriverBiDiWebSocketTransportClosureObservation,
};
111 changes: 111 additions & 0 deletions crates/originweave-network/src/webdriver_bidi_session_teardown.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use crate::{WebDriverBiDiSessionEndResult, WebDriverBiDiWebSocketTransportClosureObservation};

/// Fail-closed operational disposition derived from teardown observations.
///
/// The current assessment can report only `OperationalTeardownPending`. Transport closure already
/// has a typed owner, while browser-process exit and task-profile removal are not represented until
/// their own typed runtime owners are connected. Missing owner evidence cannot establish operational
/// completion or grant process, profile, browser, network, policy, secret, or Agent authority.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WebDriverBiDiSessionTeardownDisposition {
/// Operational teardown is not yet proven by typed observations from every owning boundary.
OperationalTeardownPending,
}

/// Explicit operational observations retained after a correlated WebDriver BiDi `session.end` ack.
///
/// Transport closure is represented by the typed observation produced only by consuming the exact
/// established WebSocket at its bounded closure-observation boundary. Browser-process exit and task
/// profile removal are deliberately absent until their own typed runtime owners can supply
/// non-forgeable evidence.
#[derive(Debug, Eq, PartialEq)]
pub struct WebDriverBiDiSessionTeardownObservations {
transport_closure_observation: Option<WebDriverBiDiWebSocketTransportClosureObservation>,
}

impl WebDriverBiDiSessionTeardownObservations {
/// Construct the typed transport observation retained by this boundary.
///
/// Transport closure cannot be asserted with a caller-supplied boolean. `Some` requires the
/// typed closure observation returned by the bounded transport owner; `None` keeps teardown
/// pending. Process and profile state cannot be supplied as placeholders and remain unproven
/// until their typed runtime owners are connected.
#[must_use]
pub const fn new(
transport_closure_observation: Option<WebDriverBiDiWebSocketTransportClosureObservation>,
) -> Self {
Self {
transport_closure_observation,
}
}

/// Return whether typed closure evidence for the exact session transport was supplied.
#[must_use]
pub const fn transport_closed_observed(&self) -> bool {
self.transport_closure_observation.is_some()
}
Comment thread
seonghobae marked this conversation as resolved.

/// Borrow the typed transport-closure observation when one was supplied.
#[must_use]
pub const fn transport_closure_observation(
&self,
) -> Option<&WebDriverBiDiWebSocketTransportClosureObservation> {
self.transport_closure_observation.as_ref()
}
}

/// One correlated `session.end` acknowledgment kept separate from operational teardown evidence.
///
/// A protocol acknowledgment alone is never operational completion. Typed transport closure is
/// retained from the consumed WebSocket, while process/profile evidence remains absent until its
/// owning runtime boundaries can provide typed observations. Consequently this assessment remains
/// fail closed in the current dependency-ordered slice.
#[derive(Debug, Eq, PartialEq)]
pub struct WebDriverBiDiSessionTeardownAssessment {
protocol_ack: WebDriverBiDiSessionEndResult,
observations: WebDriverBiDiSessionTeardownObservations,
}

impl WebDriverBiDiSessionTeardownAssessment {
/// Bind one correlated protocol acknowledgment to separately supplied operational observations.
#[must_use]
pub const fn from_protocol_ack(
protocol_ack: WebDriverBiDiSessionEndResult,
observations: WebDriverBiDiSessionTeardownObservations,
) -> Self {
Self {
protocol_ack,
observations,
}
}

/// Return the exact command id proven by the correlated protocol acknowledgment.
#[must_use]
pub const fn command_id(&self) -> u64 {
self.protocol_ack.command_id()
}

/// Borrow the explicit operational observations bound to this assessment.
#[must_use]
pub const fn observations(&self) -> &WebDriverBiDiSessionTeardownObservations {
&self.observations
}

/// Return whether typed evidence proves every required operational teardown boundary.
///
/// This is always `false` while typed browser-process-exit and task-profile-removal evidence is
/// absent from this boundary.
#[must_use]
pub const fn is_operationally_complete(&self) -> bool {
false
}

/// Return the fail-closed disposition for the currently supplied observations.
///
/// The current boundary cannot emit an operational-completion disposition because process and
/// profile cleanup still lack typed owner evidence.
#[must_use]
pub const fn disposition(&self) -> WebDriverBiDiSessionTeardownDisposition {
WebDriverBiDiSessionTeardownDisposition::OperationalTeardownPending
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use std::{error::Error, fmt, time::Duration};

use crate::{WebDriverBiDiWebSocketEstablished, WebDriverBiDiWebSocketFrameError};

/// Bounded transport-closure condition observed on one consumed WebDriver BiDi WebSocket.
///
/// This classification proves only what the already session-correlated transport itself exposed.
/// A peer Close frame does not prove browser-process exit or profile cleanup, while peer EOF does
/// not imply that the RFC 6455 closing handshake completed.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WebDriverBiDiWebSocketTransportClosureKind {
/// The peer sent one RFC 6455 Close frame that passed the existing strict frame validator.
PeerCloseFrame,
/// The peer ended the TCP byte stream cleanly before any new WebSocket frame byte was read.
PeerEof,
}

/// Credential-free observation that one established WebDriver BiDi transport ceased carrying data.
///
/// Construction consumes the established WebSocket, so this value cannot be used to regain the
/// underlying connection. It records only a validated peer Close status when one was actually
/// present, or clean TCP EOF before a new frame began. It grants no browser, process, profile,
/// policy, secret, retry, reconnect, or Agent authority and does not perform a reciprocal Close
/// handshake.
#[derive(Debug, Eq, PartialEq)]
pub struct WebDriverBiDiWebSocketTransportClosureObservation {
kind: WebDriverBiDiWebSocketTransportClosureKind,
peer_close_status_code: Option<u16>,
}

impl WebDriverBiDiWebSocketTransportClosureObservation {
/// Consume one established connection and observe one bounded transport-closure condition.
///
/// A validated peer Close frame and zero-byte clean EOF are the only success cases. One
/// unsolicited Pong may be ignored before that closure signal because Pong requires no client
/// response; a second Pong, Ping, data frame, partial-frame EOF, timeout, malformed Close, I/O
/// failure, or integrity failure remains a typed error. This fixed two-read envelope prevents
/// peer control traffic from extending teardown observation indefinitely and does not invent
/// masking entropy or outbound authority to answer Ping frames.
pub fn observe(
established: WebDriverBiDiWebSocketEstablished,
frame_timeout: Duration,
) -> Result<Self, WebDriverBiDiWebSocketTransportClosureError> {
Self::observe_frame(established, frame_timeout, true)
}

fn observe_frame(
established: WebDriverBiDiWebSocketEstablished,
frame_timeout: Duration,
allow_pong: bool,
) -> Result<Self, WebDriverBiDiWebSocketTransportClosureError> {
match established.read_frame(frame_timeout) {
Ok((established, frame)) if frame.opcode() == 0xa && allow_pong => {
Self::observe_frame(established, frame_timeout, false)
}
Ok((established, frame)) => {
if frame.opcode() != 0x8 {
return Err(
WebDriverBiDiWebSocketTransportClosureError::UnexpectedFrame {
opcode: frame.opcode(),
},
);
}
let peer_close_status_code = frame
.payload()
.get(..2)
.map(|bytes| u16::from_be_bytes([bytes[0], bytes[1]]));
drop(established);
Ok(Self {
kind: WebDriverBiDiWebSocketTransportClosureKind::PeerCloseFrame,
peer_close_status_code,
})
}
Err(WebDriverBiDiWebSocketFrameError::FrameEnded { bytes_read: 0 }) => Ok(Self {
kind: WebDriverBiDiWebSocketTransportClosureKind::PeerEof,
peer_close_status_code: None,
}),
Err(source) => Err(WebDriverBiDiWebSocketTransportClosureError::Frame { source }),
}
}

/// Return the exact transport-closure condition that produced this observation.
#[must_use]
pub const fn kind(&self) -> WebDriverBiDiWebSocketTransportClosureKind {
self.kind
}

/// Return the validated peer Close status when a Close frame actually carried one.
#[must_use]
pub const fn peer_close_status_code(&self) -> Option<u16> {
self.peer_close_status_code
}
}

/// Fail-closed errors while converting one established BiDi transport into closure evidence.
#[derive(Debug)]
pub enum WebDriverBiDiWebSocketTransportClosureError {
/// The peer sent a valid WebSocket frame that was not an admissible closure signal.
UnexpectedFrame {
/// Exact validated RFC 6455 opcode observed instead of an admissible closure signal.
opcode: u8,
},
/// The existing bounded WebSocket frame reader failed before closure was proven.
Frame {
/// Original typed frame failure retained as the causal source.
source: WebDriverBiDiWebSocketFrameError,
},
}

impl fmt::Display for WebDriverBiDiWebSocketTransportClosureError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnexpectedFrame { .. } => formatter
.write_str("WebDriver BiDi peer sent non-closure traffic instead of closing"),
Self::Frame { .. } => {
formatter.write_str("WebDriver BiDi transport closure could not be observed safely")
}
}
}
}

impl Error for WebDriverBiDiWebSocketTransportClosureError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::UnexpectedFrame { .. } => None,
Self::Frame { source } => Some(source),
}
}
}
Loading
Loading