use std::collections::vec_deque::VecDeque;
use std::fmt;
use std::fmt::Debug;
use std::net;
#[cfg(feature = "metrics")]
use std::sync::Arc;
use futures::prelude::*;
use tokio::net::TcpStream;
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
use tokio_tungstenite::stream::PeerAddr;
use tungstenite::error::Error as WebSocketError;
use tungstenite::protocol::CloseFrame;
use tungstenite::protocol::Message as WebSocketMessage;
use beserial::{Deserialize, Serialize};
use network_messages::Message as NimiqMessage;
use network_primitives::address::net_address::NetAddress;
#[cfg(feature = "metrics")]
use crate::network_metrics::NetworkMetrics;
use crate::websocket::error::Error;
use crate::websocket::Message;
use crate::websocket::public_state::PublicStreamInfo;
type WebSocketLayer = WebSocketStream<MaybeTlsStream<TcpStream>>;
#[derive(Clone, Debug)]
pub enum WebSocketState {
Active,
ClosedByUs,
ClosedByPeer(Option<CloseFrame<'static>>),
}
impl WebSocketState {
#[inline]
pub fn is_active(&self) -> bool {
match self {
WebSocketState::Active => true,
_ => false,
}
}
#[inline]
pub fn is_closed(&self) -> bool {
!self.is_active()
}
}
const MAX_CHUNK_SIZE: usize = 1024 * 16; const MAX_MESSAGE_SIZE: usize = 1024 * 1024 * 10;
pub struct NimiqMessageStream {
inner: WebSocketLayer,
receiving_tag: u8,
sending_tag: u8,
ws_queue: VecDeque<WebSocketMessage>,
msg_buf: Option<Vec<u8>>,
state: WebSocketState,
pub(crate) public_state: PublicStreamInfo,
}
impl NimiqMessageStream {
pub(super) fn new(ws_socket: WebSocketLayer, outbound: bool) -> Result<Self, Error> {
let peer_addr = ws_socket.peer_addr().map_err(Error::NetAddressMissing)?;
Ok(NimiqMessageStream {
inner: ws_socket,
receiving_tag: 254,
sending_tag: 0,
ws_queue: VecDeque::new(),
msg_buf: None,
state: WebSocketState::Active,
public_state: PublicStreamInfo::new(match peer_addr.ip() {
net::IpAddr::V4(ip4) => NetAddress::IPv4(ip4),
net::IpAddr::V6(ip6) => NetAddress::IPv6(ip6),
}, outbound),
})
}
pub fn state(&self) -> &PublicStreamInfo {
&self.public_state
}
pub fn is_closed(&self) -> bool {
self.state.is_closed()
}
fn next_tag(&mut self) -> u8 {
let tag = self.sending_tag;
self.sending_tag = (self.sending_tag + 1) % 255;
tag
}
#[cfg(feature = "metrics")]
pub fn network_metrics(&self) -> &Arc<NetworkMetrics> {
&self.public_state.network_metrics
}
}
impl Sink for NimiqMessageStream {
type SinkItem = Message;
type SinkError = Error;
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
let (serialized_msg, tag) = match item {
Message::Message(msg) => {
let serialized_msg = msg.serialize_to_vec();
(serialized_msg, self.next_tag())
},
Message::Resume(serialized_msg, sending_tag) => {
let tag = match sending_tag {
Some(tag) => tag,
None => self.next_tag(),
};
(serialized_msg, tag)
},
Message::Close(frame) => {
self.state = WebSocketState::ClosedByUs;
return match self.inner.start_send(WebSocketMessage::Close(frame)) {
Ok(state) => match state {
AsyncSink::Ready => Ok(AsyncSink::Ready),
AsyncSink::NotReady(WebSocketMessage::Close(frame)) => Ok(AsyncSink::NotReady(Message::Close(frame))),
AsyncSink::NotReady(_) => {
error!("Expected to get NotReady of a Close message, but got something else.");
Err(Error::InvalidClosingState)
},
},
Err(err) => Err(Error::WebSocketError(err)),
}
},
};
let mut remaining = serialized_msg.len();
let mut chunk;
while remaining > 0 {
let mut buffer;
let start = serialized_msg.len() - remaining;
if remaining + 1 >= MAX_CHUNK_SIZE {
buffer = Vec::with_capacity(MAX_CHUNK_SIZE + 1);
buffer.push(tag);
chunk = &serialized_msg[start..start + MAX_CHUNK_SIZE - 1];
} else {
buffer = Vec::with_capacity(remaining + 1);
buffer.push(tag);
chunk = &serialized_msg[start..];
}
buffer.extend(chunk);
#[cfg(feature = "metrics")]
let buffer_len = buffer.len();
match self.inner.start_send(WebSocketMessage::binary(buffer)) {
Ok(state) => match state {
AsyncSink::Ready => {
#[cfg(feature = "metrics")]
self.public_state.network_metrics.note_bytes_sent(buffer_len);
},
AsyncSink::NotReady(_) => return Ok(AsyncSink::NotReady(Message::Resume(serialized_msg[start..].to_vec(), Some(tag)))),
},
Err(error) => return Err(Error::WebSocketError(error)),
};
remaining -= chunk.len();
}
Ok(AsyncSink::Ready)
}
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
match self.inner.poll_complete() {
Ok(r_async) => Ok(r_async),
Err(error) => Err(Error::WebSocketError(error)),
}
}
fn close(&mut self) -> Poll<(), Self::SinkError> {
match self.inner.close() {
Ok(r_async) => Ok(r_async),
Err(error) => Err(Error::WebSocketError(error)),
}
}
}
impl NimiqMessageStream {
fn try_read_message(&mut self) -> Result<Option<Message>, Error> {
if self.ws_queue.is_empty() {
return Ok(None);
}
while let Some(ws_msg) = self.ws_queue.pop_front() {
let raw_msg = ws_msg.into_data();
if raw_msg.is_empty() {
return Err(Error::InvalidMessageFormat);
}
let tag = raw_msg[0];
let chunk = &raw_msg[1..];
if self.msg_buf.is_none() {
if let Ok(msg_size) = NimiqMessage::peek_length(chunk) {
if msg_size > MAX_MESSAGE_SIZE {
error!("Max message size exceeded ({} > {})", msg_size, MAX_MESSAGE_SIZE);
return Err(Error::MessageSizeExceeded);
}
self.msg_buf = Some(Vec::with_capacity(msg_size));
} else {
return Err(Error::InvalidMessageFormat);
}
self.receiving_tag = (self.receiving_tag + 1) % 255;
}
if self.receiving_tag != tag {
error!("Tag mismatch: expected {}, got {}", self.receiving_tag, tag);
return Err(Error::TagMismatch);
}
let msg_buf = self.msg_buf.as_mut().unwrap();
let mut remaining = msg_buf.capacity() - msg_buf.len();
let chunk_size = raw_msg.len() - 1;
if chunk_size > remaining {
error!("Final chunk size exceeded ({} > {})", chunk_size, remaining);
return Err(Error::FinalChunkSizeExceeded);
}
msg_buf.extend_from_slice(chunk);
remaining -= chunk_size;
if remaining == 0 {
let msg = Deserialize::deserialize(&mut &msg_buf[..]);
self.msg_buf = None;
match msg {
Err(e) => {
return Err(Error::ParseError(e));
}
Ok(msg) => {
return Ok(Some(Message::Message(msg)));
}
}
}
}
Ok(None)
}
}
impl Stream for NimiqMessageStream {
type Item = Message;
type Error = Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if self.state.is_closed() {
return Ok(Async::Ready(None));
}
loop {
match self.inner.poll() {
Ok(Async::Ready(Some(WebSocketMessage::Close(frame)))) => {
if !self.state.is_closed() {
self.state = WebSocketState::ClosedByPeer(frame.clone());
}
return Ok(Async::Ready(Some(Message::Close(frame))))
},
Ok(Async::Ready(Some(m))) => {
#[cfg(feature = "metrics")]
self.public_state.network_metrics.note_bytes_received(m.len());
if m.len() > MAX_CHUNK_SIZE {
error!("Max chunk size exceeded ({} > {})", m.len(), MAX_CHUNK_SIZE);
return Err(Error::ChunkSizeExceeded);
}
self.ws_queue.push_back(m);
if let Some(msg) = self.try_read_message()? {
return Ok(Async::Ready(Some(msg)));
}
},
Ok(Async::Ready(None)) => {
return match self.try_read_message()? {
Some(msg) => Ok(Async::Ready(Some(msg))),
None => Ok(Async::Ready(None)),
};
},
Ok(Async::NotReady) => {
break
},
Err(e) => {
if let WebSocketError::ConnectionClosed = e {
if !self.state.is_closed() {
self.state = WebSocketState::ClosedByPeer(None);
}
}
return Err(e.into())
}
}
}
Ok(Async::NotReady)
}
}
impl Debug for NimiqMessageStream {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "NimiqMessageStream {{}}")
}
}