cloudpub_common/transport/
websocket.rs1use core::result::Result;
2use std::io::{Error, ErrorKind};
3#[cfg(unix)]
4use std::os::fd::AsRawFd;
5use std::pin::Pin;
6use std::task::{ready, Context, Poll};
7
8use super::{
9 AddrMaybeCached, Listener, NamedSocketAddr, ProtobufStream, SocketAddr, SocketOpts, Stream,
10 TcpTransport, Transport,
11};
12use crate::config::TransportConfig;
13use crate::constants::MESSAGE_TIMEOUT_SECS;
14use anyhow::{anyhow, Context as _};
15use async_trait::async_trait;
16use bytes::{Bytes, BytesMut};
17use futures_core::stream::Stream as AsyncStream;
18use std::time::Duration;
19use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
20use tokio::time::timeout;
21
22use crate::utils::{describe_message, trace_message};
23use dashmap::DashMap;
24#[cfg(unix)]
25use std::os::fd::RawFd;
26use std::sync::Arc;
27use tokio_tungstenite::tungstenite::client::IntoClientRequest;
28use tokio_tungstenite::tungstenite::handshake::server::{
29 Callback, ErrorResponse, Request, Response,
30};
31use tokio_tungstenite::tungstenite::protocol::{Message, WebSocketConfig};
32use tokio_tungstenite::{accept_hdr_async_with_config, client_async_with_config, WebSocketStream};
33use tokio_util::io::StreamReader;
34use tracing::{debug, error, trace};
35
36use futures_util::sink::{Sink, SinkExt};
37use futures_util::stream::StreamExt;
38
39#[cfg(feature = "rustls")]
40use super::tls::{get_stream, TlsStream, TlsTransport};
41
42use crate::protocol::message::Message as ProtocolMessage;
43use crate::protocol::ProstMessage;
44
45#[derive(Debug)]
46enum TransportStream {
47 Insecure(Stream),
48 #[cfg(feature = "rustls")]
49 Secure(Box<TlsStream<Stream>>),
50}
51
52impl TransportStream {
53 fn get_tcpstream(&self) -> &Stream {
54 match self {
55 TransportStream::Insecure(s) => s,
56 #[cfg(feature = "rustls")]
57 TransportStream::Secure(s) => get_stream(s.as_ref()),
58 }
59 }
60}
61
62impl AsyncRead for TransportStream {
63 fn poll_read(
64 self: Pin<&mut Self>,
65 cx: &mut Context<'_>,
66 buf: &mut ReadBuf<'_>,
67 ) -> Poll<std::io::Result<()>> {
68 match self.get_mut() {
69 TransportStream::Insecure(s) => Pin::new(s).poll_read(cx, buf),
70 #[cfg(feature = "rustls")]
71 TransportStream::Secure(s) => Pin::new(s.as_mut()).poll_read(cx, buf),
72 }
73 }
74}
75
76impl AsyncWrite for TransportStream {
77 fn poll_write(
78 self: Pin<&mut Self>,
79 cx: &mut Context<'_>,
80 buf: &[u8],
81 ) -> Poll<Result<usize, std::io::Error>> {
82 match self.get_mut() {
83 TransportStream::Insecure(s) => Pin::new(s).poll_write(cx, buf),
84 #[cfg(feature = "rustls")]
85 TransportStream::Secure(s) => Pin::new(s.as_mut()).poll_write(cx, buf),
86 }
87 }
88
89 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
90 match self.get_mut() {
91 TransportStream::Insecure(s) => Pin::new(s).poll_flush(cx),
92 #[cfg(feature = "rustls")]
93 TransportStream::Secure(s) => Pin::new(s.as_mut()).poll_flush(cx),
94 }
95 }
96
97 fn poll_shutdown(
98 self: Pin<&mut Self>,
99 cx: &mut Context<'_>,
100 ) -> Poll<Result<(), std::io::Error>> {
101 match self.get_mut() {
102 TransportStream::Insecure(s) => Pin::new(s).poll_shutdown(cx),
103 #[cfg(feature = "rustls")]
104 TransportStream::Secure(s) => Pin::new(s.as_mut()).poll_shutdown(cx),
105 }
106 }
107}
108
109#[derive(Debug)]
110struct StreamWrapper {
111 inner: WebSocketStream<TransportStream>,
112}
113
114#[async_trait]
115impl ProtobufStream for StreamWrapper {
116 async fn recv_message(&mut self) -> anyhow::Result<Option<ProtocolMessage>> {
117 let timeout_duration = Duration::from_secs(MESSAGE_TIMEOUT_SECS);
118 let result = timeout(timeout_duration, self.inner.next()).await;
119
120 match result {
121 Err(_) => Err(anyhow!(
122 "Timeout reading message after {} seconds",
123 MESSAGE_TIMEOUT_SECS
124 )),
125 Ok(msg_result) => match msg_result {
126 Some(Ok(Message::Binary(b))) => {
127 let msg = crate::protocol::Message::decode(b.as_ref())
128 .context("Failed to decode protobuf message")?;
129 let msg = msg
130 .message
131 .context("Message field is missing in the protobuf message")?;
132 trace_message("Recv", &msg);
133 Ok(Some(msg))
134 }
135 Some(Ok(Message::Close(_))) => {
136 debug!("WebSocket connection closed");
137 Ok(None)
138 }
139 Some(Ok(Message::Ping(data))) => {
140 debug!("Received ping, sending pong");
141 self.inner
142 .send(Message::Pong(data))
143 .await
144 .context("Failed to send pong")?;
145 Ok(None)
146 }
147 Some(Ok(Message::Pong(_))) => {
148 debug!("Received pong");
149 Ok(None)
150 }
151 Some(Ok(Message::Text(_))) => {
152 error!("Received unexpected text message");
153 Err(anyhow!("Unexpected text message received"))
154 }
155 Some(Ok(m)) => {
156 error!("Received unexpected message: {:?}", m);
157 Err(anyhow!("Unexpected message received"))
158 }
159 None => Ok(None),
160 Some(Err(e)) => Err(anyhow!("WebSocket error: {}", e)),
161 },
162 }
163 }
164
165 async fn send_message(
166 &mut self,
167 msg: &crate::protocol::message::Message,
168 ) -> anyhow::Result<()> {
169 trace_message("Send", msg);
170 let mut buf = BytesMut::new();
171 let wrapper = crate::protocol::Message {
174 message: Some(msg.clone()),
175 };
176 wrapper
177 .encode(&mut buf)
178 .context("Failed to encode protobuf message")?;
179
180 let timeout_duration = Duration::from_secs(MESSAGE_TIMEOUT_SECS);
181 timeout(
182 timeout_duration,
183 self.inner.send(Message::Binary(buf.into())),
184 )
185 .await
186 .map_err(|_| {
187 anyhow::anyhow!(
188 "Timeout writing {} after {} seconds",
189 describe_message(msg),
190 MESSAGE_TIMEOUT_SECS
191 )
192 })?
193 .context("Failed to send WebSocket message")?;
194 Ok(())
195 }
196
197 async fn close(&mut self) -> anyhow::Result<()> {
198 debug!("Closing WebSocket connection");
199 self.inner
200 .close(None)
201 .await
202 .context("Failed to close WebSocket stream")
203 }
204}
205
206impl AsyncStream for StreamWrapper {
207 type Item = Result<Bytes, Error>;
208
209 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
210 match Pin::new(&mut self.get_mut().inner).poll_next(cx) {
211 Poll::Pending => Poll::Pending,
212 Poll::Ready(None) => Poll::Ready(None),
213 Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(Error::other(err)))),
214 Poll::Ready(Some(Ok(res))) => {
215 if let Message::Binary(b) = res {
216 Poll::Ready(Some(Ok(b)))
217 } else {
218 Poll::Ready(Some(Err(Error::new(
219 ErrorKind::InvalidData,
220 "unexpected frame",
221 ))))
222 }
223 }
224 }
225 }
226
227 fn size_hint(&self) -> (usize, Option<usize>) {
228 self.inner.size_hint()
229 }
230}
231
232#[derive(Debug)]
233pub struct WebsocketStream {
234 inner: StreamReader<StreamWrapper, Bytes>,
235}
236
237impl AsyncRead for WebsocketStream {
238 fn poll_read(
239 self: Pin<&mut Self>,
240 cx: &mut Context<'_>,
241 buf: &mut ReadBuf<'_>,
242 ) -> Poll<std::io::Result<()>> {
243 Pin::new(&mut self.get_mut().inner).poll_read(cx, buf)
244 }
245}
246
247impl AsyncBufRead for WebsocketStream {
248 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<&[u8]>> {
249 Pin::new(&mut self.get_mut().inner).poll_fill_buf(cx)
250 }
251
252 fn consume(self: Pin<&mut Self>, amt: usize) {
253 Pin::new(&mut self.get_mut().inner).consume(amt)
254 }
255}
256
257impl AsyncWrite for WebsocketStream {
258 fn poll_write(
259 self: Pin<&mut Self>,
260 cx: &mut Context<'_>,
261 buf: &[u8],
262 ) -> Poll<Result<usize, std::io::Error>> {
263 let sw = self.get_mut().inner.get_mut();
264 ready!(Pin::new(&mut sw.inner).poll_ready(cx).map_err(Error::other))?;
265
266 let bbuf = BytesMut::from(buf);
267
268 match Pin::new(&mut sw.inner).start_send(Message::Binary(bbuf.into())) {
269 Ok(()) => Poll::Ready(Ok(buf.len())),
270 Err(e) => Poll::Ready(Err(Error::other(e))),
271 }
272 }
273
274 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
275 Pin::new(&mut self.get_mut().inner.get_mut().inner)
276 .poll_flush(cx)
277 .map_err(Error::other)
278 }
279
280 fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
281 Pin::new(&mut self.get_mut().inner.get_mut().inner)
282 .poll_close(cx)
283 .map_err(Error::other)
284 }
285}
286
287#[async_trait]
288impl ProtobufStream for WebsocketStream {
289 async fn recv_message(&mut self) -> anyhow::Result<Option<ProtocolMessage>> {
290 self.inner.get_mut().recv_message().await
291 }
292
293 async fn send_message(&mut self, msg: &ProtocolMessage) -> anyhow::Result<()> {
294 self.inner.get_mut().send_message(msg).await
295 }
296
297 async fn close(&mut self) -> anyhow::Result<()> {
298 debug!("Closing WebSocket stream");
299 self.inner.get_mut().close().await
300 }
301}
302
303#[derive(Debug)]
304enum SubTransport {
305 #[cfg(feature = "rustls")]
306 Secure(TlsTransport),
307 Insecure(TcpTransport),
308}
309
310#[derive(Debug)]
311pub struct WebsocketTransport {
312 sub: SubTransport,
313 conf: WebSocketConfig,
314 headers: Arc<DashMap<String, String>>,
315 connect_timeout_secs: u64,
316}
317
318struct HeaderCollector {
326 headers: Arc<DashMap<String, String>>,
327}
328
329impl Callback for HeaderCollector {
330 fn on_request(self, req: &Request, res: Response) -> Result<Response, ErrorResponse> {
331 for ref header in req.headers() {
332 trace!("WS headers: {:?}", header);
333 self.headers.insert(
334 header.0.to_string(),
335 header.1.to_str().unwrap_or_default().to_string(),
336 );
337 }
338 Ok(res)
339 }
340}
341
342#[async_trait]
343impl Transport for WebsocketTransport {
344 type Acceptor = Listener;
345 type RawStream = Stream;
346 type Stream = WebsocketStream;
347
348 fn new(config: &TransportConfig) -> anyhow::Result<Self> {
349 let wsconfig = config
350 .websocket
351 .as_ref()
352 .ok_or_else(|| anyhow!("Missing websocket config"))?;
353
354 let conf = WebSocketConfig::default()
355 .write_buffer_size(0)
356 .read_buffer_size(crate::constants::WEBSOCKET_READ_BUFFER_SIZE);
357
358 let sub = match wsconfig.tls {
359 #[cfg(feature = "rustls")]
360 true => SubTransport::Secure(TlsTransport::new(config)?),
361 #[cfg(not(feature = "rustls"))]
362 true => unreachable!("TLS support not enabled"),
363 false => SubTransport::Insecure(TcpTransport::new(config)?),
364 };
365 let headers = Arc::new(DashMap::new());
366 Ok(WebsocketTransport {
367 sub,
368 conf,
369 headers,
370 connect_timeout_secs: config.tcp.connect_timeout_secs,
371 })
372 }
373
374 fn hint(conn: &Self::Stream, opt: SocketOpts) {
375 opt.apply(conn.inner.get_ref().inner.get_ref().get_tcpstream())
376 }
377
378 #[cfg(unix)]
379 fn as_raw_fd(conn: &Self::Stream) -> RawFd {
380 match conn.inner.get_ref().inner.get_ref().get_tcpstream() {
381 Stream::Tcp(tcp_stream) => tcp_stream.as_raw_fd(),
382 Stream::Unix(unix_stream) => unix_stream.as_raw_fd(),
383 }
384 }
385
386 async fn bind(&self, addr: NamedSocketAddr) -> anyhow::Result<Self::Acceptor> {
387 Listener::bind(&addr).await.map_err(Into::into)
388 }
389
390 async fn accept(&self, a: &Self::Acceptor) -> anyhow::Result<(Self::RawStream, SocketAddr)> {
391 let (s, addr) = match &self.sub {
392 SubTransport::Insecure(t) => t.accept(a).await?,
393 #[cfg(feature = "rustls")]
394 SubTransport::Secure(t) => t.accept(a).await?,
395 };
396 Ok((s, addr))
397 }
398
399 async fn handshake(&self, conn: Self::RawStream) -> anyhow::Result<Self::Stream> {
400 let tsream = match &self.sub {
401 SubTransport::Insecure(t) => {
402 TransportStream::Insecure(t.handshake(conn).await?.into_stream())
403 }
404 #[cfg(feature = "rustls")]
405 SubTransport::Secure(t) => TransportStream::Secure(Box::new(t.handshake(conn).await?)),
406 };
407
408 let wsstream = accept_hdr_async_with_config(
409 tsream,
410 HeaderCollector {
411 headers: self.headers.clone(),
412 },
413 Some(self.conf),
414 )
415 .await?;
416
417 let tun = WebsocketStream {
418 inner: StreamReader::new(StreamWrapper { inner: wsstream }),
419 };
420 Ok(tun)
421 }
422
423 async fn connect(&self, addr: &AddrMaybeCached) -> anyhow::Result<Self::Stream> {
424 let u = format!("wss://{}/endpoint/v3", addr.addr.as_str());
425 super::with_connect_deadline(self.connect_timeout_secs, "WebSocket connect", async {
429 let tstream = match &self.sub {
430 SubTransport::Insecure(t) => {
431 TransportStream::Insecure(t.connect(addr).await?.into_stream())
432 }
433 #[cfg(feature = "rustls")]
434 SubTransport::Secure(t) => {
435 TransportStream::Secure(Box::new(t.connect(addr).await?))
436 }
437 };
438 debug!("Connecting to {}", u);
439 let (wsstream, _) = client_async_with_config(
440 u.clone()
441 .into_client_request()
442 .context("Failed to create client request")?,
443 tstream,
444 Some(self.conf),
445 )
446 .await
447 .with_context(|| format!("Failed to connect to {}", u))?;
448
449 debug!("Connected");
450
451 let tun = WebsocketStream {
452 inner: StreamReader::new(StreamWrapper { inner: wsstream }),
453 };
454 Ok(tun)
455 })
456 .await
457 }
458
459 fn get_header(&self, name: &str) -> Option<String> {
460 self.headers.get(&name.to_lowercase()).map(|v| v.clone())
461 }
462}