use crate::{
stream::streaming_client::StreamingClientReceiver, StreamError, StreamResult, USER_AGENT,
};
use futures::{
stream::{SplitSink, SplitStream},
SinkExt, StreamExt,
};
use std::{
str::FromStr,
sync::atomic::{AtomicU64, Ordering},
};
use diem_json_rpc_types::{
stream::{
request::{StreamJsonRpcRequest, StreamMethodRequest},
response::StreamJsonRpcResponse,
},
Id,
};
use reqwest::Method;
use tokio::{sync::mpsc, task::JoinHandle};
use tokio_tungstenite::{
connect_async_with_config,
tungstenite::{handshake::client::Request, protocol::WebSocketConfig, Message},
MaybeTlsStream, WebSocketStream,
};
pub struct WebsocketTransport {
stream: Option<SplitStream<WebSocketStream<MaybeTlsStream<tokio::net::TcpStream>>>>,
sink: SplitSink<WebSocketStream<MaybeTlsStream<tokio::net::TcpStream>>, Message>,
next_id: AtomicU64,
channel_task: Option<JoinHandle<()>>,
}
impl WebsocketTransport {
pub async fn new<T: Into<String>>(
url: T,
websocket_config: Option<WebSocketConfig>,
) -> StreamResult<Self> {
let request = Request::builder()
.header(reqwest::header::USER_AGENT, USER_AGENT)
.header(reqwest::header::CONTENT_LENGTH, 1_000)
.uri(url.into())
.method(Method::GET)
.body(())
.map_err(StreamError::from_http_error)?;
let (stream, _) = connect_async_with_config(request, websocket_config)
.await
.map_err(StreamError::from_tungstenite_error)?;
let (sink, stream) = stream.split();
Ok(Self {
stream: Some(stream),
sink,
next_id: AtomicU64::new(0),
channel_task: None,
})
}
pub async fn send(&mut self, request_json: String) -> StreamResult<()> {
self.sink
.send(Message::text(request_json))
.await
.map_err(StreamError::encode)?;
Ok(())
}
pub async fn send_method_request(
&mut self,
request: StreamMethodRequest,
id: Option<Id>,
) -> StreamResult<Id> {
let id = id.unwrap_or_else(|| self.get_next_id());
let request = StreamJsonRpcRequest::new(request, id.clone());
self.send_request(&request).await
}
pub async fn send_request(&mut self, request: &StreamJsonRpcRequest) -> StreamResult<Id> {
let json = serde_json::to_string(&request)?;
self.send(json).await?;
Ok(request.id.clone())
}
pub fn get_next_id(&self) -> Id {
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
Id::Number(id)
}
pub fn get_stream(mut self) -> (StreamingClientReceiver, Self) {
let (sender, receiver) = mpsc::channel(100);
let mut stream = self
.stream
.expect("Stream is `None`: it has already been consumed");
self.stream = None;
self.channel_task = Some(tokio::task::spawn(async move {
loop {
match stream.next().await {
None => {
sender
.send(Err(StreamError::connection_closed(None::<StreamError>)))
.await
.ok();
}
Some(msg) => match msg {
Ok(msg) => {
if msg.is_text() {
let msg = match msg
.to_text()
.map_err(StreamError::from_tungstenite_error)
{
Ok(msg) => msg,
Err(e) => {
let _ = sender.send(Err(e)).await;
continue;
}
};
match StreamJsonRpcResponse::from_str(msg) {
Ok(msg) => sender.send(Ok(msg)).await.ok(),
Err(e) => sender.send(Err(StreamError::from(e))).await.ok(),
};
}
}
Err(e) => {
let _ = sender
.send(Err(StreamError::from_tungstenite_error(e)))
.await;
}
},
};
}
}));
(receiver, self)
}
}