Skip to content

Commit 123a17c

Browse files
committed
Generic stream transport
This extracts some of the core logic from `TcpConnector` and `TcpTransport` to a more general `StreamConnector` and `StreamTransport`. This is essentially the same as before, except we operate over `AsyncRead` and `AsyncWrite`. To facilite this, users need to provide a generic `C: Fn(...) -> Future<Output = Result<R, W>>`, effectively providing their own custom connection establishment. This makes it possible to have custom transport implementations that don't rely on a raw TCP stream.
1 parent 9a2b529 commit 123a17c

8 files changed

Lines changed: 551 additions & 392 deletions

File tree

async-opcua-client/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ pub use session::{
130130
SessionActivity, SessionBuilder, SessionConnectMode, SessionEventLoop, SessionPollResult,
131131
Subscription, SubscriptionActivity, SubscriptionCallbacks, UARequest,
132132
};
133-
pub use transport::{AsyncSecureChannel, TcpConnector, TcpTransport};
133+
pub use transport::AsyncSecureChannel;
134134

135135
/// This module contains utilities for reverse connect. Allowing you to
136136
/// connect to a server by having the server initiate the connection to the client.

async-opcua-client/src/transport/connect.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use std::{future::Future, sync::Arc};
22

33
use opcua_types::{EndpointDescription, Error, StatusCode};
44

5-
use crate::transport::state::SecureChannelState;
5+
use crate::transport::{state::SecureChannelState, RequestRecv};
66

7-
use super::{tcp::TransportConfiguration, OutgoingMessage, TcpConnector, TransportPollResult};
7+
use super::{tcp::TransportConfiguration, TcpConnector, TransportPollResult};
88

99
/// Trait implemented by simple wrapper types that create a connection to an OPC-UA server.
1010
///
@@ -13,6 +13,9 @@ use super::{tcp::TransportConfiguration, OutgoingMessage, TcpConnector, Transpor
1313
/// - This deals with connection establishment up to after exchange of HELLO/ACKNOWLEDGE
1414
/// or equivalent.
1515
/// - This should not do any retries, that's handled on a higher level.
16+
///
17+
/// Most implementations will want to use `StreamConnector` instead of doing the
18+
/// hello/acknowledge exchange manually. See `TcpConnector` for an example of this.
1619
pub trait Connector: Send + Sync {
1720
/// The transport type created by this connector.
1821
type Transport: Transport + Send + Sync + 'static;
@@ -23,7 +26,7 @@ pub trait Connector: Send + Sync {
2326
fn connect(
2427
&self,
2528
channel: Arc<SecureChannelState>,
26-
outgoing_recv: tokio::sync::mpsc::Receiver<OutgoingMessage>,
29+
outgoing_recv: RequestRecv,
2730
config: TransportConfiguration,
2831
) -> impl Future<Output = Result<Self::Transport, StatusCode>> + Send + Sync;
2932

async-opcua-client/src/transport/core.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use opcua_core::comms::{
1616
use opcua_types::{Error, StatusCode};
1717

1818
use crate::transport::state::SecureChannelState;
19+
use crate::transport::RequestRecv;
1920

2021
#[derive(Debug)]
2122
struct MessageChunkWithChunkInfo {
@@ -29,13 +30,14 @@ pub(crate) struct MessageState {
2930
deadline: Instant,
3031
}
3132

32-
pub(super) struct TransportState {
33+
/// Internal state of a transport implementation.
34+
pub struct TransportState {
3335
/// Channel for outgoing requests. Will only be polled if the number of inflight requests is below the limit.
3436
outgoing_recv: tokio::sync::mpsc::Receiver<OutgoingMessage>,
3537
/// State of pending requests
3638
message_states: HashMap<u32, MessageState>,
3739
/// Secure channel
38-
pub(super) channel_state: Arc<SecureChannelState>,
40+
pub channel_state: Arc<SecureChannelState>,
3941
/// Max pending incoming messages
4042
max_chunk_count: usize,
4143
/// Last decoded sequence number
@@ -45,6 +47,13 @@ pub(super) struct TransportState {
4547
receive_buffer_size: usize,
4648
}
4749

50+
#[derive(Debug, Clone, Copy)]
51+
pub(super) enum TransportCloseState {
52+
Open,
53+
Closing(StatusCode),
54+
Closed(StatusCode),
55+
}
56+
4857
#[derive(Debug)]
4958
/// Result of polling a transport implementation.
5059
/// This represents a single iteration of the transport event loop.
@@ -62,16 +71,21 @@ pub enum TransportPollResult {
6271
Closed(StatusCode),
6372
}
6473

74+
/// An outgoing message to be sent by the transport.
6575
pub struct OutgoingMessage {
76+
/// The actual request message to send.
6677
pub request: RequestMessage,
78+
/// A callback that should be called when a response is received.
6779
pub callback: Option<tokio::sync::oneshot::Sender<Result<ResponseMessage, StatusCode>>>,
80+
/// Deadline for the request.
6881
pub deadline: Instant,
6982
}
7083

7184
impl TransportState {
72-
pub(super) fn new(
85+
/// Create a new transport state.
86+
pub fn new(
7387
channel_state: Arc<SecureChannelState>,
74-
outgoing_recv: tokio::sync::mpsc::Receiver<OutgoingMessage>,
88+
outgoing_recv: RequestRecv,
7589
max_chunk_count: usize,
7690
receive_buffer_size: usize,
7791
) -> Self {
@@ -91,7 +105,7 @@ impl TransportState {
91105
}
92106

93107
/// Wait for an outgoing message. Will also check for timed out messages.
94-
pub(super) async fn wait_for_outgoing_message(
108+
pub async fn wait_for_outgoing_message(
95109
&mut self,
96110
send_buffer: &mut SendBuffer,
97111
) -> Option<(RequestMessage, u32)> {
@@ -124,7 +138,7 @@ impl TransportState {
124138
}
125139

126140
/// Store incoming messages in the message state.
127-
pub(super) fn handle_incoming_message(&mut self, message: Message) -> Result<(), StatusCode> {
141+
pub fn handle_incoming_message(&mut self, message: Message) -> Result<(), StatusCode> {
128142
let status = match message {
129143
Message::Acknowledge(ack) => {
130144
debug!("Reader got an unexpected ack {:?}", ack);
@@ -151,7 +165,9 @@ impl TransportState {
151165
}
152166
}
153167

154-
pub(super) fn message_send_failed(&mut self, request_id: u32, err: StatusCode) {
168+
/// Call this if sending a message fails. This will notify the waiting request
169+
/// that the message could not be sent.
170+
pub fn message_send_failed(&mut self, request_id: u32, err: StatusCode) {
155171
if let Some(message_state) = self.message_states.remove(&request_id) {
156172
let _ = message_state.callback.send(Err(err));
157173
}
@@ -314,7 +330,7 @@ impl TransportState {
314330
/// Close the transport, aborting any pending requests.
315331
/// If `status` is good, the pending requests will be terminated with
316332
/// `BadConnectionClosed`.
317-
pub(super) async fn close(&mut self, status: StatusCode) -> StatusCode {
333+
pub async fn close(&mut self, status: StatusCode) -> StatusCode {
318334
// If the status is good, we still want to send a bad status code
319335
// to the pending requests. They didn't succeed, after all.
320336
let request_status = if status.is_good() {

async-opcua-client/src/transport/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ mod channel;
44
mod connect;
55
mod core;
66
mod state;
7+
mod stream;
78
pub(super) mod tcp;
89

910
pub use channel::{AsyncSecureChannel, SecureChannelEventLoop};
1011
pub use connect::{Connector, ConnectorBuilder, Transport};
11-
pub(crate) use core::OutgoingMessage;
12-
pub use core::TransportPollResult;
13-
pub use tcp::{ReverseHelloVerifier, ReverseTcpConnector, TcpConnector, TcpTransport};
12+
pub use core::{OutgoingMessage, TransportPollResult, TransportState};
13+
pub use state::{RequestRecv, RequestSend, SecureChannelState};
14+
pub use stream::{wait_for_reverse_hello, StreamConnection, StreamConnector, StreamTransport};
15+
pub use tcp::{
16+
ReverseHelloVerifier, ReverseTcpConnector, TcpConnector, TcpTransport, TransportConfiguration,
17+
};

async-opcua-client/src/transport/state.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ use opcua_types::{
1818
OpenSecureChannelResponse, RequestHeader, SecurityTokenRequestType, StatusCode,
1919
};
2020

21-
pub(crate) type RequestSend = tokio::sync::mpsc::Sender<OutgoingMessage>;
21+
/// Tokio channel for sending requests to the transport.
22+
pub type RequestSend = tokio::sync::mpsc::Sender<OutgoingMessage>;
23+
/// Tokio channel for receiving requests in the transport.
24+
pub type RequestRecv = tokio::sync::mpsc::Receiver<OutgoingMessage>;
2225

2326
/// The state of the secure channel used by the transport.
2427
pub struct SecureChannelState {
@@ -211,6 +214,7 @@ impl SecureChannelState {
211214
self.authentication_token.store(Arc::new(token));
212215
}
213216

217+
/// Get a reference to the secure channel.
214218
pub fn secure_channel(&self) -> &RwLock<SecureChannel> {
215219
&self.secure_channel
216220
}

0 commit comments

Comments
 (0)