starpc 0.52.1 → 0.52.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -8
- package/dist/srpc/client-rpc.d.ts +1 -0
- package/dist/srpc/client-rpc.js +9 -0
- package/dist/srpc/client-rpc.test.d.ts +1 -0
- package/dist/srpc/client-rpc.test.js +50 -0
- package/dist/srpc/common-rpc.d.ts +5 -0
- package/dist/srpc/common-rpc.js +37 -8
- package/dist/srpc/errors.d.ts +2 -0
- package/dist/srpc/errors.js +14 -0
- package/dist/srpc/index.d.ts +1 -1
- package/dist/srpc/index.js +1 -1
- package/echo/Cargo.toml +8 -0
- package/echo/echo_e2e_test.cpp +542 -985
- package/echo/echo_srpc.pb.cpp +3 -5
- package/echo/echo_srpc.pb.go +6 -9
- package/echo/echo_srpc.pb.hpp +23 -3
- package/echo/echo_srpc.pb.rs +61 -26
- package/echo/echo_srpc.py +4 -1
- package/echo/half_close_test.rs +178 -0
- package/echo/integration_server.rs +14 -21
- package/echo/main.rs +28 -48
- package/go.mod +1 -1
- package/go.sum +2 -18
- package/integration/cross-language/cpp-client.cpp +446 -402
- package/integration/cross-language/cpp-pipe-server.cpp +139 -0
- package/integration/cross-language/cpp-pipe_test.go +114 -0
- package/integration/cross-language/cpp-server.cpp +222 -186
- package/mock/mock_srpc.pb.cpp +1 -1
- package/mock/mock_srpc.pb.hpp +1 -1
- package/mock/mock_srpc.pb.rs +10 -9
- package/package.json +1 -1
- package/srpc/CMakeLists.txt +3 -58
- package/srpc/README-cpp.md +69 -0
- package/srpc/accept.go +10 -4
- package/srpc/accept_test.go +25 -0
- package/srpc/build.rs +129 -34
- package/srpc/client-half-close_test.go +152 -0
- package/srpc/client-invoker.go +32 -42
- package/srpc/client-invoker_test.go +53 -0
- package/srpc/client-rpc.cpp +65 -80
- package/srpc/client-rpc.go +32 -33
- package/srpc/client-rpc.hpp +52 -38
- package/srpc/client-rpc.test.ts +67 -0
- package/srpc/client-rpc.ts +10 -0
- package/srpc/client-rpc_test.go +76 -0
- package/srpc/client.cpp +99 -87
- package/srpc/client.hpp +46 -35
- package/srpc/client.rs +78 -38
- package/srpc/client_half_close_test.cc +331 -0
- package/srpc/client_invoker.rs +75 -0
- package/srpc/common-rpc.cpp +186 -146
- package/srpc/common-rpc.go +5 -4
- package/srpc/common-rpc.hpp +119 -89
- package/srpc/common-rpc.ts +43 -8
- package/srpc/common-rpc_test.go +23 -0
- package/srpc/error.rs +19 -2
- package/srpc/errors.hpp +80 -50
- package/srpc/errors.ts +16 -0
- package/srpc/handler.hpp +9 -7
- package/srpc/handler.rs +7 -45
- package/srpc/index.ts +2 -0
- package/srpc/invoker.hpp +82 -55
- package/srpc/invoker.rs +14 -52
- package/srpc/lib.rs +2 -0
- package/srpc/message.hpp +17 -12
- package/srpc/msg-stream.go +1 -0
- package/srpc/msg-stream.hpp +108 -61
- package/srpc/mux.cpp +84 -84
- package/srpc/mux.hpp +48 -38
- package/srpc/mux.rs +4 -4
- package/srpc/net.go +12 -3
- package/srpc/packet.cpp +60 -58
- package/srpc/packet.hpp +35 -20
- package/srpc/packet.rs +8 -27
- package/srpc/rpc.rs +332 -115
- package/srpc/rpc_lifecycle_test.cc +333 -0
- package/srpc/server-cancel_test.go +39 -0
- package/srpc/server-rpc.cpp +83 -73
- package/srpc/server-rpc.go +21 -19
- package/srpc/server-rpc.hpp +56 -37
- package/srpc/server.rs +76 -131
- package/srpc/starpc.hpp +4 -2
- package/srpc/stream.go +4 -0
- package/srpc/stream.hpp +95 -34
- package/srpc/stream.rs +7 -2
- package/srpc/testing.rs +11 -66
- package/srpc/transport.rs +21 -71
- package/srpc/websocket.rs +106 -57
- package/srpc/writer.hpp +46 -32
- package/srpc/yamux.rs +82 -26
- package/srpc/tests/integration_test.rs +0 -495
package/srpc/testing.rs
CHANGED
|
@@ -13,22 +13,8 @@ use crate::error::{Error, Result};
|
|
|
13
13
|
use crate::rpc::PacketWriter;
|
|
14
14
|
use crate::transport::create_packet_channel;
|
|
15
15
|
|
|
16
|
-
/// Creates a pair of connected in-memory streams
|
|
17
|
-
///
|
|
18
|
-
/// Returns (client_stream, server_stream) that can be used for testing
|
|
19
|
-
/// client-server communication.
|
|
20
|
-
///
|
|
21
|
-
/// # Arguments
|
|
22
|
-
/// * `buffer_size` - Size of the internal buffer for each direction
|
|
23
|
-
///
|
|
24
|
-
/// # Example
|
|
25
|
-
///
|
|
26
|
-
/// ```ignore
|
|
27
|
-
/// let (client_stream, server_stream) = create_pipe(64 * 1024);
|
|
28
|
-
///
|
|
29
|
-
/// // Use client_stream with a client
|
|
30
|
-
/// // Use server_stream with a server
|
|
31
|
-
/// ```
|
|
16
|
+
/// Creates a pair of connected in-memory streams for testing client-server
|
|
17
|
+
/// communication, with `buffer_size` bytes of buffer in each direction.
|
|
32
18
|
pub fn create_pipe(buffer_size: usize) -> (DuplexStream, DuplexStream) {
|
|
33
19
|
duplex(buffer_size)
|
|
34
20
|
}
|
|
@@ -38,21 +24,8 @@ pub fn create_pipe_default() -> (DuplexStream, DuplexStream) {
|
|
|
38
24
|
create_pipe(64 * 1024)
|
|
39
25
|
}
|
|
40
26
|
|
|
41
|
-
/// In-memory stream opener for testing
|
|
42
|
-
///
|
|
43
|
-
/// This opener can hold multiple streams and returns them one at a time
|
|
44
|
-
/// on each call to `open_stream`.
|
|
45
|
-
///
|
|
46
|
-
/// # Example
|
|
47
|
-
///
|
|
48
|
-
/// ```ignore
|
|
49
|
-
/// let (client_stream, server_stream) = create_pipe_default();
|
|
50
|
-
///
|
|
51
|
-
/// let opener = InMemoryOpener::new(vec![client_stream]);
|
|
52
|
-
/// let client = SrpcClient::new(opener);
|
|
53
|
-
///
|
|
54
|
-
/// // The client will use client_stream for its RPC
|
|
55
|
-
/// ```
|
|
27
|
+
/// In-memory stream opener for testing that holds multiple streams and
|
|
28
|
+
/// returns them one at a time on each call to `open_stream`.
|
|
56
29
|
pub struct InMemoryOpener {
|
|
57
30
|
streams: Arc<Mutex<Vec<DuplexStream>>>,
|
|
58
31
|
}
|
|
@@ -108,40 +81,15 @@ impl SingleInMemoryOpener {
|
|
|
108
81
|
#[async_trait]
|
|
109
82
|
impl OpenStream for SingleInMemoryOpener {
|
|
110
83
|
async fn open_stream(&self) -> Result<(Arc<dyn PacketWriter>, PacketReceiver)> {
|
|
111
|
-
let stream = self
|
|
112
|
-
.stream
|
|
113
|
-
.lock()
|
|
114
|
-
.await
|
|
115
|
-
.take()
|
|
116
|
-
.ok_or(Error::StreamClosed)?;
|
|
84
|
+
let stream = self.stream.lock().await.take().ok_or(Error::StreamClosed)?;
|
|
117
85
|
|
|
118
86
|
let (read_half, write_half) = tokio::io::split(stream);
|
|
119
87
|
Ok(create_packet_channel(read_half, write_half))
|
|
120
88
|
}
|
|
121
89
|
}
|
|
122
90
|
|
|
123
|
-
/// Creates a connected client-server test setup
|
|
124
|
-
///
|
|
125
|
-
/// Returns (client_opener, server_stream) where:
|
|
126
|
-
/// - `client_opener` can be used to create a `SrpcClient`
|
|
127
|
-
/// - `server_stream` can be passed to `Server::handle_stream`
|
|
128
|
-
///
|
|
129
|
-
/// # Example
|
|
130
|
-
///
|
|
131
|
-
/// ```ignore
|
|
132
|
-
/// let (opener, server_stream) = create_test_pair();
|
|
133
|
-
///
|
|
134
|
-
/// let client = SrpcClient::new(opener);
|
|
135
|
-
/// let server = Server::new(mux);
|
|
136
|
-
///
|
|
137
|
-
/// // Spawn server
|
|
138
|
-
/// tokio::spawn(async move {
|
|
139
|
-
/// server.handle_stream(server_stream).await
|
|
140
|
-
/// });
|
|
141
|
-
///
|
|
142
|
-
/// // Use client
|
|
143
|
-
/// let response = client.exec_call("svc", "method", &request).await?;
|
|
144
|
-
/// ```
|
|
91
|
+
/// Creates a connected client-server test setup: an opener for a
|
|
92
|
+
/// `SrpcClient` and the server stream for `Server::handle_stream`.
|
|
145
93
|
pub fn create_test_pair() -> (SingleInMemoryOpener, DuplexStream) {
|
|
146
94
|
let (client_stream, server_stream) = create_pipe_default();
|
|
147
95
|
(SingleInMemoryOpener::new(client_stream), server_stream)
|
|
@@ -225,13 +173,10 @@ mod tests {
|
|
|
225
173
|
stream.close_send().await.unwrap();
|
|
226
174
|
|
|
227
175
|
// Read response with a timeout to handle potential races.
|
|
228
|
-
let response = tokio::time::timeout(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
.await
|
|
233
|
-
.expect("timeout")
|
|
234
|
-
.expect("recv_bytes failed");
|
|
176
|
+
let response = tokio::time::timeout(std::time::Duration::from_secs(5), stream.recv_bytes())
|
|
177
|
+
.await
|
|
178
|
+
.expect("timeout")
|
|
179
|
+
.expect("recv_bytes failed");
|
|
235
180
|
assert_eq!(&response[..], b"hello");
|
|
236
181
|
|
|
237
182
|
// Wait for server to complete.
|
package/srpc/transport.rs
CHANGED
|
@@ -12,6 +12,7 @@ use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
|
|
|
12
12
|
use tokio::sync::Mutex;
|
|
13
13
|
use tokio_util::codec::{Encoder, FramedRead};
|
|
14
14
|
|
|
15
|
+
use crate::client::PacketReceiver;
|
|
15
16
|
use crate::codec::PacketCodec;
|
|
16
17
|
use crate::error::{Error, Result};
|
|
17
18
|
use crate::proto::Packet;
|
|
@@ -68,65 +69,13 @@ impl<W: AsyncWrite + Send + Unpin + 'static> PacketWriter for TransportPacketWri
|
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
///
|
|
72
|
-
|
|
72
|
+
/// Default bounded request capacity for multiplexed transport admission.
|
|
73
|
+
#[cfg(feature = "yamux")]
|
|
74
|
+
pub(crate) const DEFAULT_CHANNEL_BUFFER: usize = 32;
|
|
73
75
|
|
|
74
|
-
///
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
/// Default channel buffer size for packet channels.
|
|
78
|
-
pub const DEFAULT_CHANNEL_BUFFER: usize = 32;
|
|
79
|
-
|
|
80
|
-
/// Spawns a task that reads packets from a transport and sends them through a channel.
|
|
81
|
-
///
|
|
82
|
-
/// This is a common pattern used by both client and server to handle incoming packets.
|
|
83
|
-
/// The task will run until the transport is closed or an error occurs.
|
|
84
|
-
///
|
|
85
|
-
/// # Arguments
|
|
86
|
-
/// * `reader` - The async reader to read packets from
|
|
87
|
-
/// * `sender` - The channel sender to forward packets to
|
|
88
|
-
///
|
|
89
|
-
/// # Returns
|
|
90
|
-
/// A `JoinHandle` for the spawned task.
|
|
91
|
-
pub fn spawn_packet_reader<R>(
|
|
92
|
-
reader: R,
|
|
93
|
-
sender: PacketSender,
|
|
94
|
-
) -> tokio::task::JoinHandle<()>
|
|
95
|
-
where
|
|
96
|
-
R: AsyncRead + Send + Unpin + 'static,
|
|
97
|
-
{
|
|
98
|
-
tokio::spawn(async move {
|
|
99
|
-
let mut framed = FramedRead::new(reader, PacketCodec::new());
|
|
100
|
-
while let Some(result) = framed.next().await {
|
|
101
|
-
match result {
|
|
102
|
-
Ok(packet) => {
|
|
103
|
-
if sender.send(packet).await.is_err() {
|
|
104
|
-
// Receiver dropped, stop reading
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
Err(_) => {
|
|
109
|
-
// Read error, stop reading
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/// Creates a packet writer and receiver from a split transport.
|
|
118
|
-
///
|
|
119
|
-
/// This is a convenience function that:
|
|
120
|
-
/// 1. Creates a `TransportPacketWriter` from the write half
|
|
121
|
-
/// 2. Spawns a packet reader task for the read half
|
|
122
|
-
/// 3. Returns the writer and receiver channel
|
|
123
|
-
///
|
|
124
|
-
/// # Arguments
|
|
125
|
-
/// * `read_half` - The read half of the transport
|
|
126
|
-
/// * `write_half` - The write half of the transport
|
|
127
|
-
///
|
|
128
|
-
/// # Returns
|
|
129
|
-
/// A tuple of (packet writer, packet receiver).
|
|
76
|
+
/// Creates a packet writer and an owned incoming packet stream from a split transport.
|
|
77
|
+
/// The receiver reads on demand; dropping it immediately releases the read half.
|
|
78
|
+
/// No background task or additional packet queue is created.
|
|
130
79
|
pub fn create_packet_channel<R, W>(
|
|
131
80
|
read_half: R,
|
|
132
81
|
write_half: W,
|
|
@@ -135,10 +84,18 @@ where
|
|
|
135
84
|
R: AsyncRead + Send + Unpin + 'static,
|
|
136
85
|
W: AsyncWrite + Send + Unpin + 'static,
|
|
137
86
|
{
|
|
87
|
+
// Framing belongs to the returned receive capability for its complete lifetime.
|
|
88
|
+
let reader = FramedRead::new(read_half, PacketCodec::new());
|
|
89
|
+
let packets = futures::stream::unfold(reader, |mut reader| async move {
|
|
90
|
+
match reader.next().await {
|
|
91
|
+
Some(Ok(packet)) => Some((packet, reader)),
|
|
92
|
+
_ => None,
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// The peer-facing writer retains its own half of this same transport.
|
|
138
97
|
let writer: Arc<dyn PacketWriter> = Arc::new(TransportPacketWriter::new(write_half));
|
|
139
|
-
|
|
140
|
-
spawn_packet_reader(read_half, tx);
|
|
141
|
-
(writer, rx)
|
|
98
|
+
(writer, Box::pin(packets))
|
|
142
99
|
}
|
|
143
100
|
|
|
144
101
|
/// Encodes optional data for protobuf messages.
|
|
@@ -147,9 +104,6 @@ where
|
|
|
147
104
|
/// - `None` -> empty data, `data_is_zero = false`
|
|
148
105
|
/// - `Some(empty)` -> empty data, `data_is_zero = true`
|
|
149
106
|
/// - `Some(data)` -> data bytes, `data_is_zero = false`
|
|
150
|
-
///
|
|
151
|
-
/// # Returns
|
|
152
|
-
/// A tuple of (data bytes, data_is_zero flag).
|
|
153
107
|
pub fn encode_optional_data(data: Option<Bytes>) -> (Vec<u8>, bool) {
|
|
154
108
|
match data {
|
|
155
109
|
Some(d) if d.is_empty() => (vec![], true),
|
|
@@ -158,13 +112,9 @@ pub fn encode_optional_data(data: Option<Bytes>) -> (Vec<u8>, bool) {
|
|
|
158
112
|
}
|
|
159
113
|
}
|
|
160
114
|
|
|
161
|
-
/// Decodes optional data from protobuf messages
|
|
162
|
-
///
|
|
163
|
-
///
|
|
164
|
-
///
|
|
165
|
-
/// # Returns
|
|
166
|
-
/// `Some(Bytes)` if data was present (including empty data with `data_is_zero`),
|
|
167
|
-
/// `None` if no data was included.
|
|
115
|
+
/// Decodes optional data from protobuf messages, inverting
|
|
116
|
+
/// `encode_optional_data`: `Some(Bytes)` when data was present, including
|
|
117
|
+
/// empty data with `data_is_zero` set.
|
|
168
118
|
pub fn decode_optional_data(data: Vec<u8>, data_is_zero: bool) -> Option<Bytes> {
|
|
169
119
|
if !data.is_empty() || data_is_zero {
|
|
170
120
|
Some(Bytes::from(data))
|
package/srpc/websocket.rs
CHANGED
|
@@ -1,70 +1,119 @@
|
|
|
1
|
-
//! WebSocket transport
|
|
1
|
+
//! Owned WebSocket byte transport for StarPC.
|
|
2
2
|
|
|
3
|
-
use bytes::Bytes;
|
|
4
|
-
use futures::{
|
|
5
|
-
use
|
|
6
|
-
use
|
|
3
|
+
use bytes::{Buf, Bytes};
|
|
4
|
+
use futures::{Sink, Stream};
|
|
5
|
+
use std::io;
|
|
6
|
+
use std::pin::Pin;
|
|
7
|
+
use std::task::{ready, Context, Poll};
|
|
8
|
+
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
|
9
|
+
use tokio_tungstenite::{tungstenite, tungstenite::Message, WebSocketStream};
|
|
7
10
|
|
|
8
|
-
///
|
|
9
|
-
pub const WEBSOCKET_BYTE_STREAM_BUFFER: usize = 64 * 1024;
|
|
10
|
-
|
|
11
|
-
/// WEBSOCKET_MESSAGE_BUFFER is the maximum byte chunk sent in one WebSocket message.
|
|
11
|
+
/// Maximum byte chunk sent in one WebSocket message.
|
|
12
12
|
pub const WEBSOCKET_MESSAGE_BUFFER: usize = 16 * 1024;
|
|
13
13
|
|
|
14
|
-
///
|
|
15
|
-
///
|
|
16
|
-
///
|
|
17
|
-
///
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
let (mut peer_reader, mut peer_writer) = tokio::io::split(peer);
|
|
14
|
+
/// Adapts binary WebSocket messages to byte I/O without background tasks.
|
|
15
|
+
/// Incoming binary messages form one byte stream. Text and control messages
|
|
16
|
+
/// carry no application bytes; a close message ends input. Dropping this
|
|
17
|
+
/// capability drops its socket, including when input is idle.
|
|
18
|
+
pub fn websocket_byte_stream<S>(socket: WebSocketStream<S>) -> WebSocketByteStream<S> {
|
|
19
|
+
WebSocketByteStream {
|
|
20
|
+
socket,
|
|
21
|
+
buffered: Bytes::new(),
|
|
22
|
+
input: Input::Open,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
26
|
+
/// Byte I/O that owns its WebSocket and the unread part of one incoming message.
|
|
27
|
+
pub struct WebSocketByteStream<S> {
|
|
28
|
+
socket: WebSocketStream<S>,
|
|
29
|
+
buffered: Bytes,
|
|
30
|
+
input: Input,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
enum Input {
|
|
34
|
+
Open,
|
|
35
|
+
Closing,
|
|
36
|
+
Ended,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
impl<S: AsyncRead + AsyncWrite + Unpin> AsyncRead for WebSocketByteStream<S> {
|
|
40
|
+
fn poll_read(
|
|
41
|
+
self: Pin<&mut Self>,
|
|
42
|
+
cx: &mut Context<'_>,
|
|
43
|
+
output: &mut ReadBuf<'_>,
|
|
44
|
+
) -> Poll<io::Result<()>> {
|
|
45
|
+
let this = self.get_mut();
|
|
46
|
+
if output.remaining() == 0 {
|
|
47
|
+
return Poll::Ready(Ok(()));
|
|
43
48
|
}
|
|
44
|
-
let _ = peer_writer.shutdown().await;
|
|
45
|
-
});
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
let mut buf = vec![0; WEBSOCKET_MESSAGE_BUFFER];
|
|
50
|
+
// Retain each message until all its bytes have reached the reader.
|
|
49
51
|
loop {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
let _ = socket_writer.close().await;
|
|
63
|
-
break;
|
|
52
|
+
if !this.buffered.is_empty() {
|
|
53
|
+
let count = output.remaining().min(this.buffered.len());
|
|
54
|
+
output.put_slice(&this.buffered[..count]);
|
|
55
|
+
this.buffered.advance(count);
|
|
56
|
+
return Poll::Ready(Ok(()));
|
|
57
|
+
}
|
|
58
|
+
match this.input {
|
|
59
|
+
Input::Closing => {
|
|
60
|
+
// Flush Tungstenite's close acknowledgment before reporting EOF.
|
|
61
|
+
ready!(Pin::new(&mut this.socket).poll_flush(cx)).map_err(socket_error)?;
|
|
62
|
+
this.input = Input::Ended;
|
|
63
|
+
return Poll::Ready(Ok(()));
|
|
64
64
|
}
|
|
65
|
+
Input::Ended => return Poll::Ready(Ok(())),
|
|
66
|
+
Input::Open => {}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Tungstenite owns WebSocket control handling and pending control writes.
|
|
70
|
+
match ready!(Pin::new(&mut this.socket).poll_next(cx)) {
|
|
71
|
+
Some(Ok(Message::Binary(data))) => this.buffered = data,
|
|
72
|
+
Some(Ok(Message::Close(_))) => this.input = Input::Closing,
|
|
73
|
+
None => this.input = Input::Ended,
|
|
74
|
+
Some(Ok(_)) => {}
|
|
75
|
+
Some(Err(error)) => return Poll::Ready(Err(socket_error(error))),
|
|
65
76
|
}
|
|
66
77
|
}
|
|
67
|
-
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
impl<S: AsyncRead + AsyncWrite + Unpin> AsyncWrite for WebSocketByteStream<S> {
|
|
82
|
+
fn poll_write(
|
|
83
|
+
self: Pin<&mut Self>,
|
|
84
|
+
cx: &mut Context<'_>,
|
|
85
|
+
input: &[u8],
|
|
86
|
+
) -> Poll<io::Result<usize>> {
|
|
87
|
+
if input.is_empty() {
|
|
88
|
+
return Poll::Ready(Ok(0));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Respect socket backpressure before accepting another bounded message.
|
|
92
|
+
let socket = &mut self.get_mut().socket;
|
|
93
|
+
ready!(Pin::new(&mut *socket).poll_ready(cx)).map_err(socket_error)?;
|
|
94
|
+
let count = input.len().min(WEBSOCKET_MESSAGE_BUFFER);
|
|
95
|
+
Pin::new(socket)
|
|
96
|
+
.start_send(Message::Binary(Bytes::copy_from_slice(&input[..count])))
|
|
97
|
+
.map_err(socket_error)?;
|
|
98
|
+
Poll::Ready(Ok(count))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
102
|
+
Pin::new(&mut self.get_mut().socket)
|
|
103
|
+
.poll_flush(cx)
|
|
104
|
+
.map_err(socket_error)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
108
|
+
Pin::new(&mut self.get_mut().socket)
|
|
109
|
+
.poll_close(cx)
|
|
110
|
+
.map_err(socket_error)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
68
113
|
|
|
69
|
-
|
|
114
|
+
fn socket_error(error: tungstenite::Error) -> io::Error {
|
|
115
|
+
match error {
|
|
116
|
+
tungstenite::Error::Io(error) => error,
|
|
117
|
+
other => io::Error::other(other),
|
|
118
|
+
}
|
|
70
119
|
}
|
package/srpc/writer.hpp
CHANGED
|
@@ -1,60 +1,74 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include "errors.hpp"
|
|
4
|
+
|
|
3
5
|
#include <functional>
|
|
4
6
|
#include <memory>
|
|
5
7
|
|
|
6
|
-
#include "errors.hpp"
|
|
7
|
-
|
|
8
8
|
namespace srpc {
|
|
9
9
|
class Packet;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
namespace starpc {
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/*
|
|
15
|
+
* PacketWriter writes one packet to the transport. Close may run from a
|
|
16
|
+
* receive callback or concurrently with a write, and must interrupt blocked
|
|
17
|
+
* I/O without joining callbacks. Destruction joins any transport receive
|
|
18
|
+
* threads.
|
|
19
|
+
*/
|
|
16
20
|
class PacketWriter {
|
|
17
21
|
public:
|
|
18
|
-
|
|
22
|
+
virtual ~PacketWriter() = default;
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
/* WritePacket serializes pkt onto the transport. */
|
|
25
|
+
virtual Error WritePacket(const srpc::Packet &pkt) = 0;
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
/* Close interrupts the transport. Repeated calls are safe. */
|
|
28
|
+
virtual Error Close() = 0;
|
|
25
29
|
};
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
/*
|
|
32
|
+
* PacketWriterWithClose forwards writes to an owned writer and runs an extra
|
|
33
|
+
* callback on the first Close. Matches packetWriterWithClose in writer.go.
|
|
34
|
+
*/
|
|
29
35
|
class PacketWriterWithClose : public PacketWriter {
|
|
30
36
|
public:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
/* close_fn runs once, after inner->Close. */
|
|
38
|
+
PacketWriterWithClose(std::unique_ptr<PacketWriter> inner,
|
|
39
|
+
std::function<Error()> close_fn)
|
|
40
|
+
: inner(std::move(inner)),
|
|
41
|
+
close_fn(std::move(close_fn))
|
|
42
|
+
{
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Error WritePacket(const srpc::Packet &pkt) override
|
|
46
|
+
{
|
|
47
|
+
return inner->WritePacket(pkt);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
Error Close() override
|
|
51
|
+
{
|
|
52
|
+
/* Report the inner close even when the callback also fails. */
|
|
53
|
+
Error err = inner->Close();
|
|
54
|
+
Error err2 = close_fn();
|
|
55
|
+
if (err != Error::OK)
|
|
56
|
+
return err;
|
|
57
|
+
return err2;
|
|
58
|
+
}
|
|
46
59
|
|
|
47
60
|
private:
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
std::unique_ptr<PacketWriter> inner;
|
|
62
|
+
std::function<Error()> close_fn;
|
|
50
63
|
};
|
|
51
64
|
|
|
52
|
-
|
|
65
|
+
/* NewPacketWriterWithClose wraps prw so close_fn runs on the first Close. */
|
|
53
66
|
inline std::unique_ptr<PacketWriter>
|
|
54
67
|
NewPacketWriterWithClose(std::unique_ptr<PacketWriter> prw,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
std::function<Error()> close_fn)
|
|
69
|
+
{
|
|
70
|
+
return std::make_unique<PacketWriterWithClose>(std::move(prw),
|
|
71
|
+
std::move(close_fn));
|
|
58
72
|
}
|
|
59
73
|
|
|
60
74
|
} // namespace starpc
|