use std::io::{self, BufRead, BufReader, Write};
use std::net::{Shutdown, TcpStream};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{self, Sender, Receiver};
use std::thread;
use encoding::{DecoderTrap, EncodingRef, EncoderTrap};
use std::time::Duration;
use message::{Message, ParseError};
#[derive(Debug)]
pub enum Event {
Closed(&'static str),
Disconnected,
Message(Message),
ParseError(ParseError),
Reconnected,
Reconnecting,
ReconnectionError(io::Error),
}
pub type Reader = Receiver<Event>;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Error {
AlreadyClosed,
AlreadyDisconnected,
Closed,
Disconnected,
}
enum StreamStatus {
Closed,
Connected(TcpStream),
Disconnected,
}
#[derive(Clone)]
pub struct Writer {
stream: Arc<Mutex<StreamStatus>>,
encoding: EncodingRef,
}
impl Writer {
fn new(stream: TcpStream, encoding: EncodingRef) -> Writer {
Writer {
stream: Arc::new(Mutex::new(StreamStatus::Connected(stream))),
encoding: encoding,
}
}
fn set_connected(&self, stream: TcpStream) {
*self.stream.lock().unwrap() = StreamStatus::Connected(stream);
}
fn set_disconnected(&self) {
*self.stream.lock().unwrap() = StreamStatus::Disconnected;
}
pub fn disconnect(&self) -> Result<(), Error> {
let mut status = self.stream.lock().unwrap();
match *status {
StreamStatus::Closed => {
return Err(Error::Closed);
}
StreamStatus::Connected(ref mut stream) => {
let _ = stream.shutdown(Shutdown::Both);
}
StreamStatus::Disconnected => {
return Err(Error::AlreadyDisconnected);
}
}
*status = StreamStatus::Disconnected;
Ok(())
}
pub fn is_closed(&self) -> bool {
match *self.stream.lock().unwrap() {
StreamStatus::Closed => true,
_ => false,
}
}
pub fn close(&self) -> Result<(), Error> {
let mut status = self.stream.lock().unwrap();
match *status {
StreamStatus::Closed => {
return Err(Error::AlreadyClosed);
}
StreamStatus::Connected(ref mut stream) => {
let _ = stream.shutdown(Shutdown::Both);
}
_ => {}
}
*status = StreamStatus::Closed;
Ok(())
}
pub fn raw<S: AsRef<str>>(&self, data: S) -> Result<(), Error> {
let mut status = self.stream.lock().unwrap();
let mut failed = false;
match *status {
StreamStatus::Closed => {
return Err(Error::Closed);
}
StreamStatus::Connected(ref mut stream) => {
let bytes = self.encoding.encode(data.as_ref(), EncoderTrap::Ignore).unwrap();
if stream.write(&bytes).is_err() {
let _ = stream.shutdown(Shutdown::Both);
failed = true;
}
}
StreamStatus::Disconnected => {
return Err(Error::Disconnected);
}
}
if failed {
*status = StreamStatus::Disconnected;
Err(Error::Disconnected)
} else {
Ok(())
}
}
}
impl Into<Event> for Result<Message, ParseError> {
fn into(self) -> Event {
match self {
Ok(msg) => Event::Message(msg),
Err(err) => Event::ParseError(err),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ReconnectionSettings {
DoNotReconnect,
Reconnect {
max_attempts: u32,
delay_between_attempts: Duration,
delay_after_disconnect: Duration,
}
}
impl Default for ReconnectionSettings {
fn default() -> ReconnectionSettings {
ReconnectionSettings::Reconnect {
max_attempts: 10,
delay_between_attempts: Duration::from_secs(5),
delay_after_disconnect: Duration::from_secs(60),
}
}
}
fn reconnect(address: &str, handle: &Writer) -> io::Result<(BufReader<TcpStream>)> {
let stream = try!(TcpStream::connect(address));
let reader = BufReader::new(try!(stream.try_clone()));
handle.set_connected(stream);
Ok((reader))
}
fn reader_thread(address: String, mut reader: BufReader<TcpStream>,
event_sender: Sender<Event>, handle: Writer,
reco_settings: ReconnectionSettings,
encoding: EncodingRef) {
'read: loop {
let mut buff = Vec::new();
let res = reader.read_until(b'\n', &mut buff);
if res.is_err() || res.unwrap() == 0 {
if handle.is_closed() {
let _ = event_sender.send(Event::Closed("manually closed"));
break;
} else {
handle.set_disconnected();
if event_sender.send(Event::Disconnected).is_err() {
break;
}
let (max_attempts, delay_between_attempts, delay_after_disconnect) = match reco_settings {
ReconnectionSettings::DoNotReconnect => {
let _ = handle.close();
let _ = event_sender.send(Event::Closed("do not reconnect"));
break;
}
ReconnectionSettings::Reconnect{ max_attempts,
delay_between_attempts,
delay_after_disconnect } => {
(max_attempts, delay_between_attempts, delay_after_disconnect)
}
};
thread::sleep(delay_after_disconnect);
let mut attempts = 0u32;
'reconnect: loop {
if max_attempts > 0 {
attempts += 1;
if attempts > max_attempts {
let _ = handle.close();
let _ = event_sender.send(Event::Closed("max attempts reached"));
break 'read;
}
}
if event_sender.send(Event::Reconnecting).is_err() {
break 'read;
}
match reconnect(&address, &handle) {
Ok(new_reader) => {
reader = new_reader;
if event_sender.send(Event::Reconnected).is_err() {
break 'read;
}
break 'reconnect;
}
Err(err) => {
if event_sender.send(Event::ReconnectionError(err)).is_err() {
break 'read;
}
}
}
thread::sleep(delay_between_attempts);
}
}
} else {
let line = encoding.decode(&buff, DecoderTrap::Ignore).unwrap();
if event_sender.send(Message::parse(&line).into()).is_err() {
break;
}
}
}
if !handle.is_closed() {
let _ = handle.close();
}
}
pub fn connect<A: AsRef<str>>(address: A, reco_settings: ReconnectionSettings, encoding: EncodingRef) -> io::Result<(Writer, Reader)> {
let stream = try!(TcpStream::connect(address.as_ref()));
let reader = BufReader::new(try!(stream.try_clone()));
let (event_sender, event_reader) = mpsc::channel::<Event>();
let writer = Writer::new(stream, encoding);
let reader_handle = writer.clone();
let address_clone = address.as_ref().into();
thread::spawn(move || {
reader_thread(address_clone, reader, event_sender, reader_handle, reco_settings, encoding);
});
Ok((writer, event_reader))
}